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