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