1 module drm.atomic;
2 public import core.atomic : MemoryOrder;
3 
4 pragma(inline, true):
5 
6 /// Atomic data like std::atomic
7 struct Atomic(T) if (__traits(isIntegral, T) || isPointer!T) {
8 	import core.atomic : atomicLoad, atomicStore, atomicExchange, atomicFetchAdd,
9 		atomicFetchSub, atomicCas = cas, atomicCasWeak = casWeak, atomicOp;
10 
11 	private shared T val;
12 
13 	/// Constructor
14 	this(T init) shared {
15 		val.atomicStore(init);
16 	}
17 
18 	private shared(T)* ptr() shared {
19 		return &val;
20 	}
21 
22 	/// Load the value from the atomic location with SC access
23 	alias load this;
24 
25 	/// ditto
26 	T load(MemoryOrder mo = MemoryOrder.seq)() shared {
27 		return val.atomicLoad!mo;
28 	}
29 
30 	/// Store the value to the atomic location
31 	void store(MemoryOrder mo = MemoryOrder.seq)(T newVal) shared {
32 		return val.atomicStore!mo(newVal);
33 	}
34 
35 	/// Store using SC access
36 	alias opAssign = store;
37 
38 	/// Atomically increment the value
39 	T fadd(MemoryOrder mo = MemoryOrder.seq)(T mod) shared {
40 		return atomicFetchAdd(val, mod);
41 	}
42 
43 	/// Atomically decrement the value
44 	T fsub(MemoryOrder mo = MemoryOrder.seq)(T mod) shared {
45 		return atomicFetchSub(val, mod);
46 	}
47 
48 	/// Atomically swap the value
49 	T exchange(MemoryOrder mo = MemoryOrder.seq)(T desired) shared {
50 		return atomicExchange(&val, desired);
51 	}
52 
53 	/// Compare and swap
54 	bool cas(MemoryOrder mo = MemoryOrder.seq, MemoryOrder fmo = MemoryOrder.seq)(T oldVal, T newVal) shared {
55 		return atomicCas!(mo, fmo)(ptr, oldVal, newVal);
56 	}
57 
58 	/// ditto
59 	bool casWeak(MemoryOrder mo = MemoryOrder.seq, MemoryOrder fmo = MemoryOrder.seq)(T oldVal,
60 			T newVal) shared {
61 		return atomicCasWeak!(mo, fmo)(ptr, oldVal, newVal);
62 	}
63 
64 	/// Op assign with SC semantics
65 	T opOpAssign(string op)(T rhs) shared {
66 		return val.atomicOp!(op ~ `=`)(rhs);
67 	}
68 
69 	/// Implicit conversion to FADD and FSUB
70 	T opUnary(string op)() shared if (op == `++`) {
71 		return val.atomicOp!`+=`(1);
72 	}
73 
74 	T opUnary(string op)() shared if (op == `--`) {
75 		return val.atomicOp!`-=`(1);
76 	}
77 
78 	auto ref opUnary(string op)() shared if (op == `*`) {
79 		return *(load);
80 	}
81 }
82 
83 private alias TestAtomic = Atomic!(int);
84 
85 @safe unittest {
86 	shared Atomic!int a;
87 	assert(a == 0);
88 	assert(a.load == 0);
89 	assert(a.fadd!(MemoryOrder.raw)(5) == 0);
90 	assert(a.load!(MemoryOrder.acq) == 5);
91 	assert(!a.casWeak(4, 5));
92 	assert(!a.cas(4, 5));
93 	assert(a.cas!(MemoryOrder.rel, MemoryOrder.acq)(5, 4));
94 	assert(a.fsub!(MemoryOrder.acq_rel)(2) == 4);
95 	assert(a.exchange!(MemoryOrder.acq_rel)(3) == 2);
96 	assert(a.load!(MemoryOrder.raw) == 3);
97 	a.store!(MemoryOrder.rel)(7);
98 	assert(a.load == 7);
99 	a = 32;
100 	assert(a == 32);
101 	a += 5;
102 	assert(a == 37);
103 	assert(a++ == 37);
104 	assert(a == 38);
105 }
106 
107 // static array of shared atomics
108 @safe unittest {
109 	static shared(Atomic!int)[5] arr;
110 	arr[4] = 4;
111 	assert(arr[4].load == 4);
112 }
113 
114 unittest {
115 	import core.thread : Thread;
116 
117 	shared(Atomic!int)[2] arr;
118 
119 	void reltest() @safe {
120 		arr[0].store!(MemoryOrder.rel)(1);
121 		arr[1].store!(MemoryOrder.rel)(1);
122 	}
123 
124 	void acqtest() @safe {
125 		while (arr[1].load!(MemoryOrder.acq) != 1) {
126 		}
127 		assert(arr[0].load!(MemoryOrder.acq) == 1);
128 	}
129 
130 	auto t1 = new Thread(&acqtest);
131 	auto t2 = new Thread(&reltest);
132 	t2.start;
133 	t1.start;
134 	t2.join;
135 	t1.join;
136 }
137 
138 @safe unittest {
139 	shared Atomic!(shared(int)) a = 5;
140 	assert(a.load == shared(int)(5));
141 	a = 2;
142 	assert(a == 2);
143 }
144 
145 @safe unittest {
146 	shared Atomic!(shared(int)*) ptr = new shared(int);
147 	*ptr.load!(MemoryOrder.raw)() = 5;
148 	assert(*ptr.load == 5);
149 	*(ptr.load) = 42;
150 	assert(*ptr.load == 42);
151 }
152 
153 @safe unittest {
154 	shared Atomic!(shared(int)*) ptr = new shared(int);
155 	*ptr = 5;
156 	assert(*ptr == 5);
157 	*ptr = 42;
158 	assert(*ptr == 42);
159 }
160 
161 unittest {
162 	//shared Atomic!(shared(Atomic!(int))*) ptr = new shared(Atomic!int);
163 }
164 
165 private enum bool isAggregateType(T) = is(T == struct) || is(T == union)
166 	|| is(T == class) || is(T == interface);
167 private enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;