1 /// Reimplementation of `core.atomic` with better data support.
2 module tern.atomic;
3 
4 import tern.traits;
5 import core.atomic;
6 import core.sync.mutex;
7 import core.sync.condition;
8 
9 private:
10 static:
11 shared Mutex mutex;
12 
13 public:
14 shared static this()
15 {
16     mutex = new shared Mutex();
17 }
18 
19 /// Inserts a load/store memory fence, ensuring that all loads and stores before this call happen before any loads and stores after.
20 pragma(inline)
21 void fence()
22 {
23     atomicFence();
24 }
25 
26 /// Atomically loads the value of `rhs`.
27 pragma(inline)
28 auto atomicLoad(R)(ref shared R rhs)
29 {
30     static if (isScalar!R)
31         return core.atomic.atomicLoad!(MemoryOrder.seq)(rhs);
32     else
33     {
34         mutex.lock();
35         scope (exit) mutex.unlock();
36         return rhs;
37     }
38 }
39 
40 /// Atomically loads an array element in the value of `rhs`.
41 pragma(inline)
42 auto atomicLoadElem(size_t ELEM, R)(ref shared R rhs)
43 {
44     mutex.lock();
45     scope (exit) mutex.unlock();
46     return rhs[ELEM];
47 }
48 
49 /// Atomically loads a field element in the value of `rhs`.
50 pragma(inline)
51 auto atomicLoadElem(string ELEM, R)(ref shared R rhs)
52 {
53     mutex.lock();
54     scope (exit) mutex.unlock();
55     return mixin("rhs."~ELEM);
56 }
57 
58 
59 /// Atomically stores `lhs` in `rhs`.
60 pragma(inline)
61 void atomicStore(R, L)(ref shared R rhs, L lhs)
62 {
63     static if (isScalar!R)
64         core.atomic.atomicStore!(MemoryOrder.seq)(rhs, lhs);
65     else
66     {
67         mutex.lock();
68         scope (exit) mutex.unlock();
69         rhs = lhs;
70     }
71 }
72 
73 /// Atomically stores the array element `lhs` in an array element of `rhs`.
74 pragma(inline)
75 auto atomicLoadElem(size_t ELEM, R)(ref shared R rhs, L lhs)
76 {
77     mutex.lock();
78     scope (exit) mutex.unlock();
79     return rhs[ELEM] = lhs;
80 }
81 
82 /// Atomically stores the field element `lhs` in a field element of `rhs`.
83 pragma(inline)
84 auto atomicLoadElem(string ELEM, R)(ref shared R rhs, L lhs)
85 {
86     mutex.lock();
87     scope (exit) mutex.unlock();
88     return mixin("rhs."~ELEM~" = lhs");
89 }
90 
91 /// Atomically exchanges `rhs` and `lhs`.
92 pragma(inline)
93 void atomicExchange(R, L)(ref shared R rhs, L lhs)
94 {
95     static if (isScalar!R)
96         core.atomic.atomicExchange!(MemoryOrder.seq)(&rhs, lhs);
97     else
98     {
99         mutex.lock();
100         scope (exit) mutex.unlock();
101         R t = lhs;
102         lhs = rhs; 
103         rhs = t;
104     }
105 }
106 
107 /// Performs an atomic operation `op` on `rhs` and `lhs`.
108 pragma(inline)
109 auto atomicOp(string op, R, L)(ref shared R rhs, L lhs)
110 {
111     static if (isScalar!R)
112         return core.atomic.atomicOp!op(rhs, lhs);
113     else
114     {
115         mutex.lock();
116         scope (exit) mutex.unlock();
117         return mixin("rhs "~op~" lhs");
118     }
119 }
120 
121 /// Spinlock implementation backed by `Condition`.
122 public class SpinLock
123 {
124 private:
125 final:
126 shared:
127     Mutex mutex;
128     Condition condition;
129 
130 public:
131     this()
132     {
133         mutex = new shared Mutex();
134         condition = new shared Condition(mutex);
135     }
136 
137     void lock() shared
138     {
139         mutex.lock();
140         while (!tryLock())
141             condition.wait();
142     }
143 
144     bool tryLock() shared
145     {
146         return mutex.tryLock();
147     }
148 
149     void unlock() shared
150     {
151         mutex.unlock();
152         condition.notifyAll();
153     }
154 }