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