Skip to content

Commit 5a55826

Browse files
committed
Add std.atomic support
1 parent de3931f commit 5a55826

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

std/atomic.d

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

0 commit comments

Comments
 (0)