1 /** 2 @nogc nothrow mutex with thread-safe lazy init. 3 4 Copyright: Copyright Guillaume Piolat 2022 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 7 */ 8 module gamut.internals.mutex; 9 10 version(encodeDDS) version = needsMutex; 11 version(needsMutex): 12 13 import core.atomic; 14 import core.stdc.stdlib : malloc, free; 15 import core.lifetime : emplace; 16 static import core.sync.mutex; 17 18 nothrow @nogc @safe: 19 20 /// A Mutex suitable for disabled runtime, immune from simultaneous creation. 21 /// A `nothrow @nogc @safe` Mutex, on top of druntime mutex. 22 /// Additionally, this mutex is checked against creation race. 23 struct Mutex 24 { 25 nothrow @nogc @safe: 26 27 /// Initialize the mutex, if not existing already. 28 /// This function can be called concurrently. 29 void initialize() @trusted 30 { 31 // Is there a mutex already? 32 if (atomicLoad(_mutex) !is null) 33 { 34 return; 35 } 36 37 // Create one mutex. 38 DRuntimeMutex mtx = cast(DRuntimeMutex) malloc(__traits(classInstanceSize, DRuntimeMutex)); 39 emplace(mtx); 40 41 void* p = cast(void*)mtx; 42 void** here = &_mutex; 43 void* ifThis = null; 44 45 // Try to set _mutex. 46 if (!cas(here, &ifThis, p)) 47 { 48 // Another thread created _mutex first. Destroy our useless instance. 49 destroyMutexInstance(mtx); 50 } 51 } 52 53 ~this() @trusted 54 { 55 destroyMutexInstance(cast(DRuntimeMutex)_mutex); 56 } 57 58 /// Lock mutex, with lazy interlocked initialization if needed. 59 void lockLazy() @trusted 60 { 61 initialize(); 62 (cast(DRuntimeMutex)_mutex).lock_nothrow(); 63 } 64 65 /// Lock mutex. Rentrant. 66 void lock() @trusted 67 { 68 (cast(DRuntimeMutex)_mutex).lock_nothrow(); 69 } 70 71 /// Unlock mutex. 72 void unlock() @trusted 73 { 74 (cast(DRuntimeMutex)_mutex).unlock_nothrow(); 75 } 76 77 private: 78 alias DRuntimeMutex = core.sync.mutex.Mutex; 79 void* _mutex = null; // should be shared, but cas() complained... 80 81 void destroyMutexInstance(DRuntimeMutex m) @system 82 { 83 if (m is null) 84 return; 85 86 // In general destroying classes like this is not 87 // safe, but since we know that the only base class 88 // of Mutex is Object and it doesn't have a dtor 89 // we can simply call the non-virtual __dtor() here. 90 m.__dtor(); 91 free(cast(void*)m); 92 } 93 } 94 95 // Test reentrance. 96 @trusted unittest 97 { 98 Mutex m; 99 m.initialize(); 100 m.lock(); 101 m.lock(); 102 m.unlock(); 103 m.unlock(); 104 } 105 106 // Test lazy-init. 107 @trusted unittest 108 { 109 Mutex m; 110 m.lockLazy(); 111 m.unlock(); 112 }