Skip to content

Commit 49a2221

Browse files
committed
Add std.atomic support
1 parent 3922f4f commit 49a2221

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

std/atomic.d

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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+
struct Atomic(T) if (__traits(isIntegral, T) || isPointer!T) {
14+
import core.atomic : atomicLoad, atomicStore, atomicExchange, atomicFetchAdd,
15+
atomicFetchSub, atomicCas = cas, atomicCasWeak = casWeak, atomicOp;
16+
17+
private T val;
18+
19+
/// Constructor
20+
this(T init) shared {
21+
val.atomicStore(init);
22+
}
23+
24+
private shared(T)* ptr() shared {
25+
return &val;
26+
}
27+
28+
/// Load the value from the atomic location with SC access
29+
alias load this;
30+
31+
/// ditto
32+
T load(MemoryOrder mo = MemoryOrder.seq)() shared {
33+
return val.atomicLoad!(mo.toCore);
34+
}
35+
36+
/// Store the value to the atomic location
37+
void store(MemoryOrder mo = MemoryOrder.seq)(T newVal) shared {
38+
return val.atomicStore!(mo.toCore)(newVal);
39+
}
40+
41+
/// Store using SC access
42+
alias opAssign = store;
43+
44+
/// Atomically increment the value
45+
T fadd(MemoryOrder mo = MemoryOrder.seq)(T mod) shared {
46+
return atomicFetchAdd!(mo.toCore)(val, mod);
47+
}
48+
49+
/// Atomically decrement the value
50+
T fsub(MemoryOrder mo = MemoryOrder.seq)(T mod) shared {
51+
return atomicFetchSub!(mo.toCore)(val, mod);
52+
}
53+
54+
/// Atomically swap the value
55+
T exchange(MemoryOrder mo = MemoryOrder.seq)(T desired) shared {
56+
return atomicExchange!(mo.toCore)(&val, desired);
57+
}
58+
59+
/// Compare and swap
60+
bool cas(MemoryOrder mo = MemoryOrder.seq, MemoryOrder fmo = MemoryOrder.seq)(T oldVal, T newVal) shared {
61+
return atomicCas!(mo.toCore, fmo.toCore)(ptr, oldVal, newVal);
62+
}
63+
64+
/// ditto
65+
bool casWeak(MemoryOrder mo = MemoryOrder.seq, MemoryOrder fmo = MemoryOrder.seq)(T oldVal,
66+
T newVal) shared {
67+
return atomicCasWeak!(mo.toCore, fmo.toCore)(ptr, oldVal, newVal);
68+
}
69+
70+
/// Op assign with SC semantics
71+
T opOpAssign(string op)(T rhs) shared {
72+
return val.atomicOp!(op ~ `=`)(rhs);
73+
}
74+
75+
/// Implicit conversion to FADD and FSUB
76+
T opUnary(string op)() shared if (op == `++`) {
77+
return val.atomicOp!`+=`(1);
78+
}
79+
80+
T opUnary(string op)() shared if (op == `--`) {
81+
return val.atomicOp!`-=`(1);
82+
}
83+
84+
auto ref opUnary(string op)() shared if (op == `*`) {
85+
return *(load);
86+
}
87+
}
88+
89+
static import core.atomic;
90+
enum MemoryOrder{
91+
/**
92+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#monotonic, LLVM AtomicOrdering.Monotonic)
93+
* and C++11/C11 `memory_order_relaxed`.
94+
*/
95+
rlx = cast(int)core.atomic.MemoryOrder.raw,
96+
/**
97+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquire, LLVM AtomicOrdering.Acquire)
98+
* and C++11/C11 `memory_order_acquire`.
99+
*/
100+
acq = cast(int)core.atomic.MemoryOrder.acq,
101+
/**
102+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#release, LLVM AtomicOrdering.Release)
103+
* and C++11/C11 `memory_order_release`.
104+
*/
105+
rel = cast(int)core.atomic.MemoryOrder.rel,
106+
/**
107+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquirerelease, LLVM AtomicOrdering.AcquireRelease)
108+
* and C++11/C11 `memory_order_acq_rel`.
109+
*/
110+
acq_rel = cast(int)core.atomic.MemoryOrder.acq_rel,
111+
/**
112+
* Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#sequentiallyconsistent, LLVM AtomicOrdering.SequentiallyConsistent)
113+
* and C++11/C11 `memory_order_seq_cst`.
114+
*/
115+
seq = cast(int)core.atomic.MemoryOrder.seq,
116+
}
117+
118+
private auto toCore(MemoryOrder mo){
119+
static import core.atomic;
120+
return cast(core.atomic.MemoryOrder) mo;
121+
}
122+
123+
@safe unittest {
124+
shared Atomic!int a;
125+
assert(a == 0);
126+
assert(a.load == 0);
127+
assert(a.fadd!(MemoryOrder.rlx)(5) == 0);
128+
assert(a.load!(MemoryOrder.acq) == 5);
129+
assert(!a.casWeak(4, 5));
130+
assert(!a.cas(4, 5));
131+
assert(a.cas!(MemoryOrder.rel, MemoryOrder.acq)(5, 4));
132+
assert(a.fsub!(MemoryOrder.acq_rel)(2) == 4);
133+
assert(a.exchange!(MemoryOrder.acq_rel)(3) == 2);
134+
assert(a.load!(MemoryOrder.rlx) == 3);
135+
a.store!(MemoryOrder.rel)(7);
136+
assert(a.load == 7);
137+
a = 32;
138+
assert(a == 32);
139+
a += 5;
140+
assert(a == 37);
141+
assert(a++ == 37);
142+
assert(a == 38);
143+
}
144+
145+
// static array of shared atomics
146+
@safe unittest {
147+
static shared(Atomic!int)[5] arr;
148+
arr[4] = 4;
149+
assert(arr[4].load == 4);
150+
}
151+
152+
unittest {
153+
import core.thread : Thread;
154+
155+
shared(Atomic!int)[2] arr;
156+
157+
void reltest() @safe {
158+
arr[0].store!(MemoryOrder.rel)(1);
159+
arr[1].store!(MemoryOrder.rel)(1);
160+
}
161+
162+
void acqtest() @safe {
163+
while (arr[1].load!(MemoryOrder.acq) != 1) {
164+
}
165+
assert(arr[0].load!(MemoryOrder.acq) == 1);
166+
}
167+
168+
auto t1 = new Thread(&acqtest);
169+
auto t2 = new Thread(&reltest);
170+
t2.start;
171+
t1.start;
172+
t2.join;
173+
t1.join;
174+
}
175+
176+
@safe unittest {
177+
shared Atomic!(shared(int)) a = 5;
178+
assert(a.load == shared(int)(5));
179+
a = 2;
180+
assert(a == 2);
181+
}
182+
183+
@safe unittest {
184+
shared Atomic!(shared(int)*) ptr = new shared(int);
185+
*ptr.load!(MemoryOrder.rlx)() = 5;
186+
assert(*ptr.load == 5);
187+
*(ptr.load) = 42;
188+
assert(*ptr.load == 42);
189+
}
190+
191+
@safe unittest {
192+
shared Atomic!(shared(int)*) ptr = new shared(int);
193+
*ptr = 5;
194+
assert(*ptr == 5);
195+
*ptr = 42;
196+
assert(*ptr == 42);
197+
}
198+
199+
unittest {
200+
//shared Atomic!(shared(Atomic!(int))*) ptr = new shared(Atomic!int);
201+
}
202+
203+
private enum bool isAggregateType(T) = is(T == struct) || is(T == union)
204+
|| is(T == class) || is(T == interface);
205+
private enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;

0 commit comments

Comments
 (0)