The OpenD Programming Language

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 core.sync.mutex;
17 
18 
19 public import core.sync.exception;
20 
21 version (Windows)
22 {
23     import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
24         EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection,
25         TryEnterCriticalSection+/;
26 }
27 else version (Posix)
28 {
29     import core.sys.posix.pthread;
30 }
31 else version (FreeStanding) { /* FIXME: this is a purely non-functional stub! */ }
32 else
33 {
34     static assert(false, "Platform not supported");
35 }
36 
37 ////////////////////////////////////////////////////////////////////////////////
38 // Mutex
39 //
40 // void lock();
41 // void unlock();
42 // bool tryLock();
43 ////////////////////////////////////////////////////////////////////////////////
44 
45 
46 /**
47  * This class represents a general purpose, recursive mutex.
48  *
49  * Implemented using `pthread_mutex` on Posix and `CRITICAL_SECTION`
50  * on Windows.
51  */
52 class Mutex :
53     Object.Monitor
54 {
55     ////////////////////////////////////////////////////////////////////////////
56     // Initialization
57     ////////////////////////////////////////////////////////////////////////////
58 
59 
60     /**
61      * Initializes a mutex object.
62      *
63      */
64     this() @trusted nothrow @nogc
65     {
66         this(true);
67     }
68 
69     /// ditto
70     this() shared @trusted nothrow @nogc
71     {
72         this(true);
73     }
74 
75     // Undocumented, useful only in Mutex.this().
76     private this(this Q)(bool _unused_) @trusted nothrow @nogc
77         if (is(Q == Mutex) || is(Q == shared Mutex))
78     {
79         version (Windows)
80         {
81             InitializeCriticalSection(cast(CRITICAL_SECTION*) &m_hndl);
82         }
83         else version (Posix)
84         {
85             import core.internal.abort : abort;
86             pthread_mutexattr_t attr = void;
87 
88             !pthread_mutexattr_init(&attr) ||
89                 abort("Error: pthread_mutexattr_init failed.");
90 
91             scope (exit) !pthread_mutexattr_destroy(&attr) ||
92                 abort("Error: pthread_mutexattr_destroy failed.");
93 
94             !pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ||
95                 abort("Error: pthread_mutexattr_settype failed.");
96 
97             !pthread_mutex_init(cast(pthread_mutex_t*) &m_hndl, &attr) ||
98                 abort("Error: pthread_mutex_init failed.");
99         }
100 
101         m_proxy.link = this;
102         this.__monitor = cast(void*) &m_proxy;
103     }
104 
105 
106     /**
107      * Initializes a mutex object and sets it as the monitor for `obj`.
108      *
109      * In:
110      *  `obj` must not already have a monitor.
111      */
112     this(Object obj) @trusted nothrow @nogc
113     {
114         this(obj, true);
115     }
116 
117     /// ditto
118     this(Object obj) shared @trusted nothrow @nogc
119     {
120         this(obj, true);
121     }
122 
123     // Undocumented, useful only in Mutex.this(Object).
124     private this(this Q)(Object obj, bool _unused_) @trusted nothrow @nogc
125         if (is(Q == Mutex) || is(Q == shared Mutex))
126     in
127     {
128         assert(obj !is null,
129             "The provided object must not be null.");
130         assert(obj.__monitor is null,
131             "The provided object has a monitor already set!");
132     }
133     do
134     {
135         this();
136         obj.__monitor = cast(void*) &m_proxy;
137     }
138 
139 
140     ~this() @trusted nothrow @nogc
141     {
142         version (Windows)
143         {
144             DeleteCriticalSection(&m_hndl);
145         }
146         else version (Posix)
147         {
148             import core.internal.abort : abort;
149             !pthread_mutex_destroy(&m_hndl) ||
150                 abort("Error: pthread_mutex_destroy failed.");
151         }
152         this.__monitor = null;
153     }
154 
155 
156     ////////////////////////////////////////////////////////////////////////////
157     // General Actions
158     ////////////////////////////////////////////////////////////////////////////
159 
160 
161     /**
162      * If this lock is not already held by the caller, the lock is acquired,
163      * then the internal counter is incremented by one.
164      *
165      * Note:
166      *    `Mutex.lock` does not throw, but a class derived from Mutex can throw.
167      *    Use `lock_nothrow` in `nothrow @nogc` code.
168      */
169     @trusted void lock()
170     {
171         lock_nothrow();
172     }
173 
174     /// ditto
175     @trusted void lock() shared
176     {
177         lock_nothrow();
178     }
179 
180     /// ditto
181     final void lock_nothrow(this Q)() nothrow @trusted @nogc
182         if (is(Q == Mutex) || is(Q == shared Mutex))
183     {
184         version (Windows)
185         {
186             EnterCriticalSection(&m_hndl);
187         }
188         else version (Posix)
189         {
190             if (pthread_mutex_lock(&m_hndl) == 0)
191                 return;
192 
193             SyncError syncErr = cast(SyncError) __traits(initSymbol, SyncError).ptr;
194             syncErr.msg = "Unable to lock mutex.";
195             throw syncErr;
196         }
197     }
198 
199     /**
200      * Decrements the internal lock count by one.  If this brings the count to
201      * zero, the lock is released.
202      *
203      * Note:
204      *    `Mutex.unlock` does not throw, but a class derived from Mutex can throw.
205      *    Use `unlock_nothrow` in `nothrow @nogc` code.
206      */
207     @trusted void unlock()
208     {
209         unlock_nothrow();
210     }
211 
212     /// ditto
213     @trusted void unlock() shared
214     {
215         unlock_nothrow();
216     }
217 
218     /// ditto
219     final void unlock_nothrow(this Q)() nothrow @trusted @nogc
220         if (is(Q == Mutex) || is(Q == shared Mutex))
221     {
222         version (Windows)
223         {
224             LeaveCriticalSection(&m_hndl);
225         }
226         else version (Posix)
227         {
228             if (pthread_mutex_unlock(&m_hndl) == 0)
229                 return;
230 
231             SyncError syncErr = cast(SyncError) __traits(initSymbol, SyncError).ptr;
232             syncErr.msg = "Unable to unlock mutex.";
233             throw syncErr;
234         }
235     }
236 
237     /**
238      * If the lock is held by another caller, the method returns.  Otherwise,
239      * the lock is acquired if it is not already held, and then the internal
240      * counter is incremented by one.
241      *
242      * Returns:
243      *  true if the lock was acquired and false if not.
244      *
245      * Note:
246      *    `Mutex.tryLock` does not throw, but a class derived from Mutex can throw.
247      *    Use `tryLock_nothrow` in `nothrow @nogc` code.
248      */
249     bool tryLock() @trusted
250     {
251         return tryLock_nothrow();
252     }
253 
254     /// ditto
255     bool tryLock() shared @trusted
256     {
257         return tryLock_nothrow();
258     }
259 
260     /// ditto
261     final bool tryLock_nothrow(this Q)() nothrow @trusted @nogc
262         if (is(Q == Mutex) || is(Q == shared Mutex))
263     {
264         version (Windows)
265         {
266             return TryEnterCriticalSection(&m_hndl) != 0;
267         }
268         else version (Posix)
269         {
270             return pthread_mutex_trylock(&m_hndl) == 0;
271         }
272 	else version (FreeStanding) assert(0);
273     }
274 
275 
276 private:
277     version (Windows)
278     {
279         CRITICAL_SECTION    m_hndl;
280     }
281     else version (Posix)
282     {
283         pthread_mutex_t     m_hndl;
284     }
285 
286     struct MonitorProxy
287     {
288         Object.Monitor link;
289     }
290 
291     MonitorProxy            m_proxy;
292 
293 
294 package:
295     version (Posix)
296     {
297         pthread_mutex_t* handleAddr() @nogc
298         {
299             return &m_hndl;
300         }
301     }
302 }
303 
304 ///
305 /* @safe nothrow -> see druntime PR 1726 */
306 // Test regular usage.
307 unittest
308 {
309     import core.thread : Thread;
310 
311     class Resource
312     {
313         Mutex mtx;
314         int cargo;
315 
316         this() shared @safe nothrow
317         {
318             mtx = new shared Mutex();
319             cargo = 42;
320         }
321 
322         void useResource() shared @safe nothrow @nogc
323         {
324             mtx.lock_nothrow();
325             (cast() cargo) += 1;
326             mtx.unlock_nothrow();
327         }
328     }
329 
330     shared Resource res = new shared Resource();
331 
332     auto otherThread = new Thread(
333     {
334         foreach (i; 0 .. 10000)
335             res.useResource();
336     }).start();
337 
338     foreach (i; 0 .. 10000)
339         res.useResource();
340 
341     otherThread.join();
342 
343     assert (res.cargo == 20042);
344 }
345 
346 // Test @nogc usage.
347 @system @nogc nothrow unittest
348 {
349     import core.stdc.stdlib : malloc, free;
350     import core.lifetime : emplace;
351 
352     auto mtx = cast(shared Mutex) malloc(__traits(classInstanceSize, Mutex));
353     emplace(mtx);
354 
355     mtx.lock_nothrow();
356 
357     { // test recursive locking
358         mtx.tryLock_nothrow();
359         mtx.unlock_nothrow();
360     }
361 
362     mtx.unlock_nothrow();
363 
364     // In general destorying classes like this is not
365     // safe, but since we know that the only base class
366     // of Mutex is Object and it doesn't have a dtor
367     // we can simply call the non-virtual __dtor() here.
368 
369     // Ok to cast away shared because destruction
370     // should happen only from a single thread.
371     (cast(Mutex) mtx).__dtor();
372 
373     // Verify that the underlying implementation has been destroyed by checking
374     // that locking is not possible. This assumes that the underlying
375     // implementation is well behaved and makes the object non-lockable upon
376     // destruction. The Bionic, DragonFly, Musl, and Solaris C runtimes don't
377     // appear to do so, so skip this test.
378     version (CRuntime_Bionic) {} else
379     version (CRuntime_Musl) {} else
380     version (DragonFlyBSD) {} else
381     version (Solaris) {} else
382     assert(!mtx.tryLock_nothrow());
383 
384     free(cast(void*) mtx);
385 }
386 
387 // Test single-thread (non-shared) use.
388 unittest
389 {
390     Mutex m = new Mutex();
391 
392     m.lock();
393 
394     m.tryLock();
395     m.unlock();
396 
397     m.unlock();
398 }
399 
400 unittest
401 {
402     import core.thread;
403 
404     auto mutex      = new Mutex;
405     int  numThreads = 10;
406     int  numTries   = 1000;
407     int  lockCount  = 0;
408 
409     void testFn()
410     {
411         for (int i = 0; i < numTries; ++i)
412         {
413             synchronized (mutex)
414             {
415                 ++lockCount;
416             }
417         }
418     }
419 
420     auto group = new ThreadGroup;
421 
422     for (int i = 0; i < numThreads; ++i)
423         group.create(&testFn);
424 
425     group.joinAll();
426     assert(lockCount == numThreads * numTries);
427 }