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 }