v2 / vlib / sync / stdatomic / atomic.c.v
333 lines · 316 sloc · 9.58 KB · 0c495d07d74e01cfe9289d9ff0da2fc3ad23f38e
Raw
1module stdatomic
2
3// Implement the atomic operations. For now TCC does not support the atomic
4// versions on nix so it uses locks to simulate the same behavior.
5//
6// On windows tcc can simulate with other atomic operations.
7//
8// Note: this implementations should be regarded as alpha stage and be tested
9// much more.
10
11// add_u64 adds provided delta as an atomic operation
12@[inline]
13pub fn add_u64(ptr &u64, delta int) u64 {
14 C.atomic_fetch_add_u64(voidptr(ptr), delta)
15 return *ptr
16}
17
18// sub_u64 subtracts provided delta as an atomic operation
19@[inline]
20pub fn sub_u64(ptr &u64, delta int) u64 {
21 C.atomic_fetch_sub_u64(voidptr(ptr), delta)
22 return *ptr
23}
24
25// add_i64 adds provided delta as an atomic operation
26@[inline]
27pub fn add_i64(ptr &i64, delta int) i64 {
28 C.atomic_fetch_add_u64(voidptr(ptr), delta)
29 return *ptr
30}
31
32// add_i64 subtracts provided delta as an atomic operation
33@[inline]
34pub fn sub_i64(ptr &i64, delta int) i64 {
35 C.atomic_fetch_sub_u64(voidptr(ptr), delta)
36 return *ptr
37}
38
39// atomic store/load operations have to be used when there might be another concurrent access
40// atomicall set a value
41@[inline]
42pub fn store_u64(ptr &u64, val u64) {
43 C.atomic_store_u64(voidptr(ptr), val)
44}
45
46// atomicall get a value
47@[inline]
48pub fn load_u64(ptr &u64) u64 {
49 return C.atomic_load_u64(voidptr(ptr))
50}
51
52// atomicall set a value
53@[inline]
54pub fn store_i64(ptr &i64, val i64) {
55 C.atomic_store_u64(voidptr(ptr), val)
56}
57
58// atomicall get a value
59@[inline]
60pub fn load_i64(ptr &i64) i64 {
61 return i64(C.atomic_load_u64(voidptr(ptr)))
62}
63
64@[heap]
65pub struct AtomicVal[T] {
66 val T
67}
68
69// new_atomic creates a new atomic value of `T` type
70@[inline]
71pub fn new_atomic[T](val T) &AtomicVal[T] {
72 $if T is $int {
73 return &AtomicVal[T]{
74 val: val
75 }
76 } $else $if T is bool {
77 return &AtomicVal[T]{
78 val: val
79 }
80 } $else $if T is voidptr {
81 return &AtomicVal[T]{
82 val: val
83 }
84 } $else {
85 $compile_error('atomic: only support number, bool, and voidptr types')
86 }
87 return unsafe { nil }
88}
89
90// load returns current value of the atomic value
91@[inline]
92pub fn (mut a AtomicVal[T]) load() T {
93 $if T is bool {
94 return C.atomic_load_byte(voidptr(&a.val)) != 0
95 } $else $if T is u8 || T is i8 {
96 return T(C.atomic_load_byte(voidptr(&a.val)))
97 } $else $if T is u16 || T is i16 {
98 return T(C.atomic_load_u16(voidptr(&a.val)))
99 } $else $if T is u32 || T is i32 {
100 return T(C.atomic_load_u32(voidptr(&a.val)))
101 } $else $if T is u64 || T is i64 {
102 return T(C.atomic_load_u64(voidptr(&a.val)))
103 } $else $if T is int {
104 if sizeof(int) == 4 {
105 return int(C.atomic_load_u32(voidptr(&a.val)))
106 } else {
107 return int(C.atomic_load_u64(voidptr(&a.val)))
108 }
109 } $else $if T is isize || T is usize {
110 if sizeof(isize) == 4 {
111 return T(C.atomic_load_u32(voidptr(&a.val)))
112 } else {
113 return T(C.atomic_load_u64(voidptr(&a.val)))
114 }
115 } $else $if T is voidptr {
116 // TODO: this should be $if sizeof(T) == 4
117 $if x32 {
118 return T(C.atomic_load_u32(voidptr(&a.val)))
119 } $else {
120 return T(C.atomic_load_u64(voidptr(&a.val)))
121 }
122 }
123 return a.val
124}
125
126// store updates the atomic value with `val`
127@[inline]
128pub fn (mut a AtomicVal[T]) store(val T) {
129 $if T is bool {
130 C.atomic_store_byte(voidptr(&a.val), u8(val))
131 } $else $if T is u8 || T is i8 {
132 C.atomic_store_byte(voidptr(&a.val), u8(val))
133 } $else $if T is u16 || T is i16 {
134 C.atomic_store_u16(voidptr(&a.val), u16(val))
135 } $else $if T is u32 || T is i32 {
136 C.atomic_store_u32(voidptr(&a.val), u32(val))
137 } $else $if T is u64 || T is i64 {
138 C.atomic_store_u64(voidptr(&a.val), u64(val))
139 } $else $if T is int {
140 if sizeof(int) == 4 {
141 C.atomic_store_u32(voidptr(&a.val), u32(val))
142 } else {
143 C.atomic_store_u64(voidptr(&a.val), u64(val))
144 }
145 } $else $if T is isize || T is usize {
146 if sizeof(isize) == 4 {
147 C.atomic_store_u32(voidptr(&a.val), u32(val))
148 } else {
149 C.atomic_store_u64(voidptr(&a.val), u64(val))
150 }
151 } $else $if T is voidptr {
152 // TODO: this should be $if sizeof(T) == 4
153 $if x32 {
154 C.atomic_store_u32(voidptr(&a.val), u32(val))
155 } $else {
156 C.atomic_store_u64(voidptr(&a.val), u64(val))
157 }
158 }
159}
160
161// add adds the atomic value with `delta` and returns the previous value
162@[inline]
163pub fn (mut a AtomicVal[T]) add(delta T) T {
164 $if T is bool {
165 panic('atomic: add() not supported for bool type')
166 } $else $if T is voidptr {
167 panic('atomic: add() not supported for voidptr type')
168 } $else $if T is u8 || T is i8 {
169 old := C.atomic_fetch_add_byte(voidptr(&a.val), u8(delta))
170 return T(old)
171 } $else $if T is u16 || T is i16 {
172 old := C.atomic_fetch_add_u16(voidptr(&a.val), u16(delta))
173 return T(old)
174 } $else $if T is u32 || T is i32 {
175 old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
176 return T(old)
177 } $else $if T is u64 || T is i64 {
178 old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
179 return T(old)
180 } $else $if T is int {
181 if sizeof(int) == 4 {
182 old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
183 return T(old)
184 } else {
185 old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
186 return T(old)
187 }
188 } $else $if T is isize || T is usize {
189 if sizeof(isize) == 4 {
190 old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
191 return T(old)
192 } else {
193 old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
194 return T(old)
195 }
196 }
197 panic('unreachable')
198}
199
200// sub subtracts the atomic value with `delta` and returns the previous value
201@[inline]
202pub fn (mut a AtomicVal[T]) sub(delta T) T {
203 $if T is bool {
204 panic('atomic: sub() not supported for bool type')
205 } $else $if T is voidptr {
206 panic('atomic: sub() not supported for voidptr type')
207 } $else $if T is u8 || T is i8 {
208 old := C.atomic_fetch_sub_byte(voidptr(&a.val), u8(delta))
209 return T(old)
210 } $else $if T is u16 || T is i16 {
211 old := C.atomic_fetch_sub_u16(voidptr(&a.val), u16(delta))
212 return T(old)
213 } $else $if T is u32 || T is i32 {
214 old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
215 return T(old)
216 } $else $if T is u64 || T is i64 {
217 old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
218 return T(old)
219 } $else $if T is int {
220 if sizeof(int) == 4 {
221 old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
222 return T(old)
223 } else {
224 old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
225 return T(old)
226 }
227 } $else $if T is isize || T is usize {
228 if sizeof(isize) == 4 {
229 old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
230 return T(old)
231 } else {
232 old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
233 return T(old)
234 }
235 }
236 panic('unreachable')
237}
238
239// swap sets the `new` value and returns the previous value
240@[inline]
241pub fn (mut a AtomicVal[T]) swap(new T) T {
242 $if T is bool {
243 old := C.atomic_exchange_byte(voidptr(&a.val), u8(new))
244 return old != 0
245 } $else $if T is u8 || T is i8 {
246 old := C.atomic_exchange_byte(voidptr(&a.val), u8(new))
247 return T(old)
248 } $else $if T is u16 || T is i16 {
249 old := C.atomic_exchange_u16(voidptr(&a.val), u16(new))
250 return T(old)
251 } $else $if T is u32 || T is i32 {
252 old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
253 return T(old)
254 } $else $if T is u64 || T is i64 {
255 old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
256 return T(old)
257 } $else $if T is int {
258 if sizeof(int) == 4 {
259 old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
260 return T(old)
261 } else {
262 old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
263 return T(old)
264 }
265 } $else $if T is isize || T is usize {
266 if sizeof(isize) == 4 {
267 old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
268 return T(old)
269 } else {
270 old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
271 return T(old)
272 }
273 } $else $if T is voidptr {
274 // TODO: this should be $if sizeof(T) == 4
275 $if x32 {
276 old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
277 return T(old)
278 } $else {
279 old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
280 return T(old)
281 }
282 }
283 panic('unreachable')
284}
285
286// compare_and_swap executes the compare-and-swap(CAS) operation
287// if atomic value == `expected`, then it will be set to `new`, and return true
288// else return false, and the atomic value remains unchanged
289@[inline]
290pub fn (mut a AtomicVal[T]) compare_and_swap(expected T, new T) bool {
291 $if T is bool {
292 mut exp := u8(expected)
293 return C.atomic_compare_exchange_strong_byte(voidptr(&a.val), &exp, u8(new))
294 } $else $if T is u8 || T is i8 {
295 mut exp := u8(expected)
296 return C.atomic_compare_exchange_strong_byte(voidptr(&a.val), &exp, u8(new))
297 } $else $if T is u16 || T is i16 {
298 mut exp := u16(expected)
299 return C.atomic_compare_exchange_strong_u16(voidptr(&a.val), &exp, u16(new))
300 } $else $if T is u32 || T is i32 {
301 mut exp := u32(expected)
302 return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
303 } $else $if T is u64 || T is i64 {
304 mut exp := u64(expected)
305 return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
306 } $else $if T is int {
307 if sizeof(int) == 4 {
308 mut exp := u32(expected)
309 return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
310 } else {
311 mut exp := u64(expected)
312 return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
313 }
314 } $else $if T is isize || T is usize {
315 if sizeof(isize) == 4 {
316 mut exp := u32(expected)
317 return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
318 } else {
319 mut exp := u64(expected)
320 return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
321 }
322 } $else $if T is voidptr {
323 // TODO: this should be $if sizeof(T) == 4
324 $if x32 {
325 mut exp := u32(expected)
326 return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
327 } $else {
328 mut exp := u64(expected)
329 return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
330 }
331 }
332 panic('unreachable')
333}
334