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 }