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