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