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 }