The OpenD Programming Language

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 }