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