Skip to content

Commit 4c573d6

Browse files
committed
Add std.atomic support
1 parent d6af824 commit 4c573d6

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed

std/atomic.d

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

0 commit comments

Comments
 (0)