1 /**
2  * The mutex module provides a primitive for maintaining mutually exclusive
3  * access.
4  *
5  * Copyright: Copyright Sean Kelly 2005 - 2009.
6  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Authors:   Sean Kelly
8  * Source:    $(DRUNTIMESRC core/sync/_mutex.d)
9  */
10 
11 /*          Copyright Sean Kelly 2005 - 2009.
12  * Distributed under the Boost Software License, Version 1.0.
13  *    (See accompanying file LICENSE or copy at
14  *          http://www.boost.org/LICENSE_1_0.txt)
15  */
16 module java.nonstandard.sync.mutex;
17 
18 
19 version(Tango){
20 
21     public import tango.core.sync.Mutex;
22 
23 } else { // Phobos
24 
25 public import java.nonstandard.sync.exception;
26 
27 version( Windows )
28 {
29     private import core.sys.windows.windows;
30 }
31 else version( Posix )
32 {
33     private import core.sys.posix.pthread;
34 }
35 else
36 {
37     static assert(false, "Platform not supported");
38 }
39 
40 
41 ////////////////////////////////////////////////////////////////////////////////
42 // Mutex
43 //
44 // void lock();
45 // void unlock();
46 // bool tryLock();
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 
50 /**
51  * This class represents a general purpose, recursive mutex.
52  */
53 class Mutex :
54     Object.Monitor
55 {
56     ////////////////////////////////////////////////////////////////////////////
57     // Initialization
58     ////////////////////////////////////////////////////////////////////////////
59 
60 
61     /**
62      * Initializes a mutex object.
63      *
64      * Throws:
65      *  SyncError on error.
66      */
67     this() @trusted
68     {
69         version( Windows )
70         {
71             InitializeCriticalSection( &m_hndl );
72         }
73         else version( Posix )
74         {
75             pthread_mutexattr_t attr = void;
76 
77             if( pthread_mutexattr_init( &attr ) )
78                 throw new SyncException( "Unable to initialize mutex" );
79             scope(exit) pthread_mutexattr_destroy( &attr );
80 
81             if( pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ) )
82                 throw new SyncException( "Unable to initialize mutex" );
83 
84             if( pthread_mutex_init( &m_hndl, &attr ) )
85                 throw new SyncException( "Unable to initialize mutex" );
86         }
87         m_proxy.link = this;
88         this.__monitor = &m_proxy;
89     }
90 
91 
92     /**
93      * Initializes a mutex object and sets it as the monitor for o.
94      *
95      * In:
96      *  o must not already have a monitor.
97      */
98     this( Object o ) @trusted
99     in
100     {
101         assert( o.__monitor is null );
102     }
103     body
104     {
105         this();
106         o.__monitor = &m_proxy;
107     }
108 
109 
110     ~this()
111     {
112         version( Windows )
113         {
114             DeleteCriticalSection( &m_hndl );
115         }
116         else version( Posix )
117         {
118             int rc = pthread_mutex_destroy( &m_hndl );
119             assert( !rc, "Unable to destroy mutex" );
120         }
121         this.__monitor = null;
122     }
123 
124 
125     ////////////////////////////////////////////////////////////////////////////
126     // General Actions
127     ////////////////////////////////////////////////////////////////////////////
128 
129 
130     /**
131      * If this lock is not already held by the caller, the lock is acquired,
132      * then the internal counter is incremented by one.
133      *
134      * Throws:
135      *  SyncError on error.
136      */
137     @trusted void lock()
138     {
139         version( Windows )
140         {
141             EnterCriticalSection( &m_hndl );
142         }
143         else version( Posix )
144         {
145             int rc = pthread_mutex_lock( &m_hndl );
146             if( rc )
147                 throw new SyncException( "Unable to lock mutex" );
148         }
149     }
150 
151     /**
152      * Decrements the internal lock count by one.  If this brings the count to
153      * zero, the lock is released.
154      *
155      * Throws:
156      *  SyncError on error.
157      */
158     @trusted void unlock()
159     {
160         version( Windows )
161         {
162             LeaveCriticalSection( &m_hndl );
163         }
164         else version( Posix )
165         {
166             int rc = pthread_mutex_unlock( &m_hndl );
167             if( rc )
168                 throw new SyncException( "Unable to unlock mutex" );
169         }
170     }
171 
172     /**
173      * If the lock is held by another caller, the method returns.  Otherwise,
174      * the lock is acquired if it is not already held, and then the internal
175      * counter is incremented by one.
176      *
177      * Throws:
178      *  SyncError on error.
179      *
180      * Returns:
181      *  true if the lock was acquired and false if not.
182      */
183     bool tryLock()
184     {
185         version( Windows )
186         {
187             return TryEnterCriticalSection( &m_hndl ) != 0;
188         }
189         else version( Posix )
190         {
191             return pthread_mutex_trylock( &m_hndl ) == 0;
192         }
193     }
194 
195 
196 private:
197     version( Windows )
198     {
199         CRITICAL_SECTION    m_hndl;
200     }
201     else version( Posix )
202     {
203         pthread_mutex_t     m_hndl;
204     }
205 
206     struct MonitorProxy
207     {
208         Object.Monitor link;
209     }
210 
211     MonitorProxy            m_proxy;
212 
213 
214 package:
215     version( Posix )
216     {
217         pthread_mutex_t* handleAddr()
218         {
219             return &m_hndl;
220         }
221     }
222 }
223 
224 
225 ////////////////////////////////////////////////////////////////////////////////
226 // Unit Tests
227 ////////////////////////////////////////////////////////////////////////////////
228 
229 
230 version( unittest )
231 {
232     private import core.thread;
233 
234 
235     unittest
236     {
237         auto mutex      = new Mutex;
238         int  numThreads = 10;
239         int  numTries   = 1000;
240         int  lockCount  = 0;
241 
242         void testFn()
243         {
244             for( int i = 0; i < numTries; ++i )
245             {
246                 synchronized( mutex )
247                 {
248                     ++lockCount;
249                 }
250             }
251         }
252 
253         auto group = new ThreadGroup;
254 
255         for( int i = 0; i < numThreads; ++i )
256             group.create( &testFn );
257 
258         group.joinAll();
259         assert( lockCount == numThreads * numTries );
260     }
261 }
262 
263 }
264