Skip to content

Commit 46db8cc

Browse files
committed
Add std.atomic support
1 parent d6af824 commit 46db8cc

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

std/atomic.d

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/**
2+
* The atomic module provides atomic struct support for lock-free
3+
* concurrent programming.
4+
*
5+
* Copyright: Copyright Roy David Margalit 2022 - 2025.
6+
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7+
* Authors: Roy David Margalit
8+
* Source: $(DRUNTIMESRC core/_atomic.d)
9+
*/
10+
module std.atomic;
11+
12+
/** Atomic data like std::atomic
13+
14+
Params:
15+
T = Integral type or pointer for atomic operations
16+
17+
Example:
18+
__gshared Atomic!int a;
19+
assert(a == 0);
20+
assert(a++ == 0)
21+
assert(a == 1);
22+
23+
*/
24+
struct Atomic(T)
25+
if (__traits(isIntegral, T) || isPointer!T)
26+
{
27+
import core.atomic : atomicLoad, atomicStore, atomicExchange, atomicFetchAdd,
28+
atomicFetchSub, atomicCas = cas, atomicCasWeak = casWeak, atomicOp;
29+
30+
private T val;
31+
32+
/// Constructor
33+
this(T init) shared
34+
{
35+
val.atomicStore(init);
36+
}
37+
38+
private shared(T)* ptr() shared
39+
{
40+
return &val;
41+
}
42+
43+
/** Load the value from the atomic location with SC access
44+
Params:
45+
mo = Memory order
46+
47+
Returns: The stored value
48+
*/
49+
T load(MemoryOrder mo = MemoryOrder.seq)() shared
50+
{
51+
return val.atomicLoad!(mo.toCore);
52+
}
53+
54+
/// ditto
55+
alias load this;
56+
57+
58+
/** Store the value to the atomic location
59+
Params:
60+
mo = Memory order
61+
newVal = Value to store to atomic
62+
*/
63+
void store(MemoryOrder mo = MemoryOrder.seq)(T newVal) shared
64+
{
65+
return val.atomicStore!(mo.toCore)(newVal);
66+
}
67+
68+
/// Store using SC access
69+
alias opAssign = store;
70+
71+
/** Atomically increment the value
72+
Params:
73+
mo = Memory order
74+
mod = Value to add to atomic
75+
76+
Returns: The old stored value
77+
*/
78+
T fadd(MemoryOrder mo = MemoryOrder.seq)(T mod) shared
79+
{
80+
return atomicFetchAdd!(mo.toCore)(val, mod);
81+
}
82+
83+
/** Atomically decrement the value
84+
Params:
85+
mo = Memory order
86+
mod = Value to decrement from atomic
87+
88+
Returns: The old stored value
89+
*/
90+
T fsub(MemoryOrder mo = MemoryOrder.seq)(T mod) shared
91+
{
92+
return atomicFetchSub!(mo.toCore)(val, mod);
93+
}
94+
95+
/** Atomically swap the value
96+
Params:
97+
mo = Memory order
98+
desired = New value to store
99+
100+
Returns: The old stored value
101+
*/
102+
T exchange(MemoryOrder mo = MemoryOrder.seq)(T desired) shared
103+
{
104+
return atomicExchange!(mo.toCore)(&val, desired);
105+
}
106+
107+
/** Compare and swap
108+
Params:
109+
mo = Memory order on success
110+
fmo = Memory order on failure
111+
oldVal = Expected value to preform the swap
112+
newVal = New value to store if condition holds
113+
114+
Returns: If the value was swapped
115+
*/
116+
bool cas(MemoryOrder mo = MemoryOrder.seq, MemoryOrder fmo = MemoryOrder.seq)(T oldVal, T newVal) shared
117+
{
118+
return atomicCas!(mo.toCore, fmo.toCore)(ptr, oldVal, newVal);
119+
}
120+
121+
/** Compare and swap (May fail even if current value is equal to oldVal)
122+
Params:
123+
mo = Memory order on success
124+
fmo = Memory order on failure
125+
oldVal = Expected value to preform the swap
126+
newVal = New value to store if condition holds
127+
128+
Returns: If the value was swapped
129+
*/
130+
bool casWeak(MemoryOrder mo = MemoryOrder.seq, MemoryOrder fmo = MemoryOrder.seq)(T oldVal,
131+
T newVal) shared
132+
{
133+
return atomicCasWeak!(mo.toCore, fmo.toCore)(ptr, oldVal, newVal);
134+
}
135+
136+
/** Op assign with SC semantics
137+
Params:
138+
op = Assignment operator
139+
rhs = Value to assign
140+
141+
Returns: Computation result
142+
*/
143+
T opOpAssign(string op)(T rhs) shared
144+
{
145+
return val.atomicOp!(op ~ `=`)(rhs);
146+
}
147+
148+
/** Implicit conversion to FADD with SC access
149+
Params:
150+
op = ++
151+
152+
Returns: Pre-incremented value
153+
*/
154+
T opUnary(string op)() shared if (op == `++`)
155+
{
156+
return fadd(1);
157+
}
158+
159+
/** Implicit conversion to FSUB with SC access
160+
Params:
161+
op = --
162+
163+
Returns: Pre-decremented value
164+
*/
165+
T opUnary(string op)() shared if (op == `--`)
166+
{
167+
return fsub(1);
168+
}
169+
170+
/** Dereference atomic pointer with SC access
171+
Params:
172+
op = *
173+
174+
Returns: Reference to the pointed location
175+
*/
176+
auto ref opUnary(string op)() shared if (op == `*`)
177+
{
178+
return *(load);
179+
}
180+
}
181+
182+
static import core.atomic;
183+
/**
184+
* Specifies the memory ordering semantics of an atomic operation.
185+
*
186+
* See_Also:
187+
* $(HTTP en.cppreference.com/w/cpp/atomic/memory_order)
188+
*/
189+
enum MemoryOrder
190+
{
191+
/**
192+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#monotonic, LLVM AtomicOrdering.Monotonic)
193+
* and C++11/C11 `memory_order_relaxed`.
194+
*/
195+
rlx = cast(int) core.atomic.MemoryOrder.raw,
196+
/**
197+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquire, LLVM AtomicOrdering.Acquire)
198+
* and C++11/C11 `memory_order_acquire`.
199+
*/
200+
acq = cast(int) core.atomic.MemoryOrder.acq,
201+
/**
202+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#release, LLVM AtomicOrdering.Release)
203+
* and C++11/C11 `memory_order_release`.
204+
*/
205+
rel = cast(int) core.atomic.MemoryOrder.rel,
206+
/**
207+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquirerelease, LLVM AtomicOrdering.AcquireRelease)
208+
* and C++11/C11 `memory_order_acq_rel`.
209+
*/
210+
acq_rel = cast(int) core.atomic.MemoryOrder.acq_rel,
211+
/**
212+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#sequentiallyconsistent, LLVM AtomicOrdering.SequentiallyConsistent)
213+
* and C++11/C11 `memory_order_seq_cst`.
214+
*/
215+
seq = cast(int) core.atomic.MemoryOrder.seq,
216+
}
217+
218+
private auto toCore(MemoryOrder mo)
219+
{
220+
static import core.atomic;
221+
return cast(core.atomic.MemoryOrder) mo;
222+
}
223+
224+
@safe unittest
225+
{
226+
shared Atomic!int a;
227+
assert(a == 0);
228+
assert(a.load == 0);
229+
assert(a.fadd!(MemoryOrder.rlx)(5) == 0);
230+
assert(a.load!(MemoryOrder.acq) == 5);
231+
assert(!a.casWeak(4, 5));
232+
assert(!a.cas(4, 5));
233+
assert(a.cas!(MemoryOrder.rel, MemoryOrder.acq)(5, 4));
234+
assert(a.fsub!(MemoryOrder.acq_rel)(2) == 4);
235+
assert(a.exchange!(MemoryOrder.acq_rel)(3) == 2);
236+
assert(a.load!(MemoryOrder.rlx) == 3);
237+
a.store!(MemoryOrder.rel)(7);
238+
assert(a.load == 7);
239+
a = 32;
240+
assert(a == 32);
241+
a += 5;
242+
assert(a == 37);
243+
assert(a++ == 37);
244+
assert(a == 38);
245+
}
246+
247+
// static array of shared atomics
248+
@safe unittest
249+
{
250+
static shared(Atomic!int)[5] arr;
251+
arr[4] = 4;
252+
assert(arr[4].load == 4);
253+
}
254+
255+
@system unittest
256+
{
257+
import core.thread : Thread;
258+
259+
shared(Atomic!int)[2] arr;
260+
261+
void reltest() @safe
262+
{
263+
arr[0].store!(MemoryOrder.rel)(1);
264+
arr[1].store!(MemoryOrder.rel)(1);
265+
}
266+
267+
void acqtest() @safe
268+
{
269+
while (arr[1].load!(MemoryOrder.acq) != 1) { }
270+
assert(arr[0].load!(MemoryOrder.acq) == 1);
271+
}
272+
273+
auto t1 = new Thread(&acqtest);
274+
auto t2 = new Thread(&reltest);
275+
t2.start;
276+
t1.start;
277+
t2.join;
278+
t1.join;
279+
}
280+
281+
@safe unittest
282+
{
283+
shared Atomic!(shared(int)) a = 5;
284+
assert(a.load == shared(int)(5));
285+
a = 2;
286+
assert(a == 2);
287+
}
288+
289+
@safe unittest
290+
{
291+
shared Atomic!(shared(int)*) ptr = new shared(int);
292+
*ptr.load!(MemoryOrder.rlx)() = 5;
293+
assert(*ptr.load == 5);
294+
*(ptr.load) = 42;
295+
assert(*ptr.load == 42);
296+
}
297+
298+
@safe unittest
299+
{
300+
shared Atomic!(shared(int)*) ptr = new shared(int);
301+
*ptr = 5;
302+
assert(*ptr == 5);
303+
*ptr = 42;
304+
assert(*ptr == 42);
305+
}
306+
307+
@safe unittest
308+
{
309+
//shared Atomic!(shared(Atomic!(int))*) ptr = new shared(Atomic!int);
310+
}
311+
312+
private enum bool isAggregateType(T) = is(T == struct) || is(T == union)
313+
|| is(T == class) || is(T == interface);
314+
private enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;

0 commit comments

Comments
 (0)