Skip to content

Commit 0c495d0

Browse files
authored
sync.stdatomic: fix bug with add() and sub() returning the new values, add voidptr support, add swap() and compare_and_swap() (#24685)
1 parent 174065f commit 0c495d0

File tree

5 files changed

+321
-91
lines changed

5 files changed

+321
-91
lines changed

thirdparty/stdatomic/nix/atomic.h

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -693,35 +693,35 @@ extern inline unsigned long long __aarch64_ldeor8_relax(unsigned long long*ptr,
693693

694694
// Since V might be confused with "generic" C functions either we provide special versions
695695
// for gcc/clang, too
696-
static inline unsigned long long atomic_load_u64(unsigned long long* x) {
697-
return atomic_load_explicit((_Atomic (unsigned long long)*)x, memory_order_seq_cst);
696+
static inline unsigned long long atomic_load_u64(uint64_t* x) {
697+
return atomic_load_explicit((_Atomic (uint64_t)*)x, memory_order_seq_cst);
698698
}
699-
static inline void atomic_store_u64(unsigned long long* x, unsigned long long y) {
700-
atomic_store_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
699+
static inline void atomic_store_u64(uint64_t* x, uint64_t y) {
700+
atomic_store_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
701701
}
702-
static inline int atomic_compare_exchange_weak_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) {
703-
return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned long long)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
702+
static inline int atomic_compare_exchange_weak_u64(uint64_t* x, uint64_t* expected, uint64_t y) {
703+
return (int)atomic_compare_exchange_weak_explicit((_Atomic(uint64_t)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
704704
}
705-
static inline int atomic_compare_exchange_strong_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) {
706-
return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned long long)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
705+
static inline int atomic_compare_exchange_strong_u64(uint64_t* x, uint64_t* expected, uint64_t y) {
706+
return (int)atomic_compare_exchange_strong_explicit((_Atomic(uint64_t)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
707707
}
708-
static inline unsigned long long atomic_exchange_u64(unsigned long long* x, unsigned long long y) {
709-
return atomic_exchange_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
708+
static inline unsigned long long atomic_exchange_u64(uint64_t* x, uint64_t y) {
709+
return atomic_exchange_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
710710
}
711-
static inline unsigned long long atomic_fetch_add_u64(unsigned long long* x, unsigned long long y) {
712-
return atomic_fetch_add_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
711+
static inline unsigned long long atomic_fetch_add_u64(uint64_t* x, uint64_t y) {
712+
return atomic_fetch_add_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
713713
}
714-
static inline unsigned long long atomic_fetch_sub_u64(unsigned long long* x, unsigned long long y) {
715-
return atomic_fetch_sub_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
714+
static inline unsigned long long atomic_fetch_sub_u64(uint64_t* x, uint64_t y) {
715+
return atomic_fetch_sub_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
716716
}
717-
static inline unsigned long long atomic_fetch_and_u64(unsigned long long* x, unsigned long long y) {
718-
return atomic_fetch_and_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
717+
static inline unsigned long long atomic_fetch_and_u64(uint64_t* x, uint64_t y) {
718+
return atomic_fetch_and_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
719719
}
720-
static inline unsigned long long atomic_fetch_or_u64(unsigned long long* x, unsigned long long y) {
721-
return atomic_fetch_or_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
720+
static inline unsigned long long atomic_fetch_or_u64(uint64_t* x, uint64_t y) {
721+
return atomic_fetch_or_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
722722
}
723-
static inline unsigned long long atomic_fetch_xor_u64(unsigned long long* x, unsigned long long y) {
724-
return atomic_fetch_xor_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
723+
static inline unsigned long long atomic_fetch_xor_u64(uint64_t* x, uint64_t y) {
724+
return atomic_fetch_xor_explicit((_Atomic(uint64_t)*)x, y, memory_order_seq_cst);
725725
}
726726

727727

thirdparty/stdatomic/win/atomic.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,20 @@ static inline int atomic_compare_exchange_strong_u32(unsigned volatile * object,
294294

295295
#else
296296

297+
#define InterlockedExchange16 ManualInterlockedExchange16
297298
#define InterlockedExchangeAdd16 ManualInterlockedExchangeAdd16
298299

300+
static inline uint16_t ManualInterlockedExchange16(volatile uint16_t* object, uint16_t desired) {
301+
__asm__ __volatile__ (
302+
"xchgw %0, %1"
303+
: "+r" (desired),
304+
"+m" (*object)
305+
:
306+
: "memory"
307+
);
308+
return desired;
309+
}
310+
299311
static inline unsigned short ManualInterlockedExchangeAdd16(unsigned short volatile* Addend, unsigned short Value) {
300312
__asm__ __volatile__ (
301313
"lock xaddw %w[value], %[mem]"
@@ -385,12 +397,23 @@ static inline int atomic_compare_exchange_strong_u16(unsigned short volatile * o
385397

386398
#else
387399

400+
#define InterlockedExchange8 ManualInterlockedExchange8
388401
#define InterlockedCompareExchange8 ManualInterlockedCompareExchange8
389402
#define InterlockedExchangeAdd8 ManualInterlockedExchangeAdd8
390403
#define InterlockedOr8 ManualInterlockedOr8
391404
#define InterlockedXor8 ManualInterlockedXor8
392405
#define InterlockedAnd8 ManualInterlockedAnd8
393406

407+
static inline char ManualInterlockedExchange8(char volatile* object, char desired) {
408+
__asm__ __volatile__ (
409+
"xchgb %0, %1"
410+
: "+q" (desired), "+m" (*object)
411+
:
412+
: "memory"
413+
);
414+
return desired;
415+
}
416+
394417
static inline unsigned char ManualInterlockedCompareExchange8(unsigned char volatile * dest, unsigned char exchange, unsigned char comparand) {
395418
unsigned char result;
396419

vlib/sync/stdatomic/atomic.c.v

Lines changed: 166 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ pub struct AtomicVal[T] {
6969
// new_atomic creates a new atomic value of `T` type
7070
@[inline]
7171
pub fn new_atomic[T](val T) &AtomicVal[T] {
72-
// can't use `$if T is $int || T is bool` with $compile_error() now
73-
// see issue #24562
7472
$if T is $int {
7573
return &AtomicVal[T]{
7674
val: val
@@ -79,8 +77,12 @@ pub fn new_atomic[T](val T) &AtomicVal[T] {
7977
return &AtomicVal[T]{
8078
val: val
8179
}
80+
} $else $if T is voidptr {
81+
return &AtomicVal[T]{
82+
val: val
83+
}
8284
} $else {
83-
$compile_error('atomic: only support number and bool types')
85+
$compile_error('atomic: only support number, bool, and voidptr types')
8486
}
8587
return unsafe { nil }
8688
}
@@ -99,19 +101,24 @@ pub fn (mut a AtomicVal[T]) load() T {
99101
} $else $if T is u64 || T is i64 {
100102
return T(C.atomic_load_u64(voidptr(&a.val)))
101103
} $else $if T is int {
102-
// TODO: remove this test or a compile time support $if sizeof() ==
103104
if sizeof(int) == 4 {
104105
return int(C.atomic_load_u32(voidptr(&a.val)))
105106
} else {
106107
return int(C.atomic_load_u64(voidptr(&a.val)))
107108
}
108109
} $else $if T is isize || T is usize {
109-
// TODO: remove this test or a compile time support $if sizeof() ==
110110
if sizeof(isize) == 4 {
111111
return T(C.atomic_load_u32(voidptr(&a.val)))
112112
} else {
113113
return T(C.atomic_load_u64(voidptr(&a.val)))
114114
}
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+
}
115122
}
116123
return a.val
117124
}
@@ -120,90 +127,207 @@ pub fn (mut a AtomicVal[T]) load() T {
120127
@[inline]
121128
pub fn (mut a AtomicVal[T]) store(val T) {
122129
$if T is bool {
123-
C.atomic_store_byte(voidptr(&a.val), val)
130+
C.atomic_store_byte(voidptr(&a.val), u8(val))
124131
} $else $if T is u8 || T is i8 {
125-
C.atomic_store_byte(voidptr(&a.val), val)
132+
C.atomic_store_byte(voidptr(&a.val), u8(val))
126133
} $else $if T is u16 || T is i16 {
127-
C.atomic_store_u16(voidptr(&a.val), val)
134+
C.atomic_store_u16(voidptr(&a.val), u16(val))
128135
} $else $if T is u32 || T is i32 {
129-
C.atomic_store_u32(voidptr(&a.val), val)
136+
C.atomic_store_u32(voidptr(&a.val), u32(val))
130137
} $else $if T is u64 || T is i64 {
131-
C.atomic_store_u64(voidptr(&a.val), val)
138+
C.atomic_store_u64(voidptr(&a.val), u64(val))
132139
} $else $if T is int {
133-
// TODO: remove this test or a compile time support $if sizeof() ==
134140
if sizeof(int) == 4 {
135-
C.atomic_store_u32(voidptr(&a.val), val)
141+
C.atomic_store_u32(voidptr(&a.val), u32(val))
136142
} else {
137-
C.atomic_store_u64(voidptr(&a.val), val)
143+
C.atomic_store_u64(voidptr(&a.val), u64(val))
138144
}
139145
} $else $if T is isize || T is usize {
140-
// TODO: remove this test or a compile time support $if sizeof() ==
141146
if sizeof(isize) == 4 {
142-
C.atomic_store_u32(voidptr(&a.val), val)
147+
C.atomic_store_u32(voidptr(&a.val), u32(val))
143148
} else {
144-
C.atomic_store_u64(voidptr(&a.val), val)
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))
145157
}
146158
}
147159
}
148160

149-
// add adds the atomic value with `delta`
161+
// add adds the atomic value with `delta` and returns the previous value
150162
@[inline]
151163
pub fn (mut a AtomicVal[T]) add(delta T) T {
152164
$if T is bool {
153-
panic('atomic: can not add() a bool type')
165+
panic('atomic: add() not supported for bool type')
166+
} $else $if T is voidptr {
167+
panic('atomic: add() not supported for voidptr type')
154168
} $else $if T is u8 || T is i8 {
155-
C.atomic_fetch_add_byte(voidptr(&a.val), delta)
169+
old := C.atomic_fetch_add_byte(voidptr(&a.val), u8(delta))
170+
return T(old)
156171
} $else $if T is u16 || T is i16 {
157-
C.atomic_fetch_add_u16(voidptr(&a.val), delta)
172+
old := C.atomic_fetch_add_u16(voidptr(&a.val), u16(delta))
173+
return T(old)
158174
} $else $if T is u32 || T is i32 {
159-
C.atomic_fetch_add_u32(voidptr(&a.val), delta)
175+
old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
176+
return T(old)
160177
} $else $if T is u64 || T is i64 {
161-
C.atomic_fetch_add_u64(voidptr(&a.val), delta)
178+
old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
179+
return T(old)
162180
} $else $if T is int {
163-
// TODO: remove this test or a compile time support $if sizeof() ==
164181
if sizeof(int) == 4 {
165-
C.atomic_fetch_add_u32(voidptr(&a.val), delta)
182+
old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
183+
return T(old)
166184
} else {
167-
C.atomic_fetch_add_u64(voidptr(&a.val), delta)
185+
old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
186+
return T(old)
168187
}
169188
} $else $if T is isize || T is usize {
170-
// TODO: remove this test or a compile time support $if sizeof() ==
171189
if sizeof(isize) == 4 {
172-
C.atomic_fetch_add_u32(voidptr(&a.val), delta)
190+
old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
191+
return T(old)
173192
} else {
174-
C.atomic_fetch_add_u64(voidptr(&a.val), delta)
193+
old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
194+
return T(old)
175195
}
176196
}
177-
return T(a.val)
197+
panic('unreachable')
178198
}
179199

180-
// sub subs the atomic value with `delta`
200+
// sub subtracts the atomic value with `delta` and returns the previous value
181201
@[inline]
182202
pub fn (mut a AtomicVal[T]) sub(delta T) T {
183203
$if T is bool {
184-
panic('atomic: can not sub() a bool type')
204+
panic('atomic: sub() not supported for bool type')
205+
} $else $if T is voidptr {
206+
panic('atomic: sub() not supported for voidptr type')
185207
} $else $if T is u8 || T is i8 {
186-
C.atomic_fetch_sub_byte(voidptr(&a.val), delta)
208+
old := C.atomic_fetch_sub_byte(voidptr(&a.val), u8(delta))
209+
return T(old)
187210
} $else $if T is u16 || T is i16 {
188-
C.atomic_fetch_sub_u16(voidptr(&a.val), delta)
211+
old := C.atomic_fetch_sub_u16(voidptr(&a.val), u16(delta))
212+
return T(old)
189213
} $else $if T is u32 || T is i32 {
190-
C.atomic_fetch_sub_u32(voidptr(&a.val), delta)
214+
old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
215+
return T(old)
191216
} $else $if T is u64 || T is i64 {
192-
C.atomic_fetch_sub_u64(voidptr(&a.val), delta)
217+
old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
218+
return T(old)
193219
} $else $if T is int {
194-
// TODO: remove this test or a compile time support $if sizeof() ==
195220
if sizeof(int) == 4 {
196-
C.atomic_fetch_sub_u32(voidptr(&a.val), delta)
221+
old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
222+
return T(old)
197223
} else {
198-
C.atomic_fetch_sub_u64(voidptr(&a.val), delta)
224+
old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
225+
return T(old)
199226
}
200227
} $else $if T is isize || T is usize {
201-
// TODO: remove this test or a compile time support $if sizeof() ==
202228
if sizeof(isize) == 4 {
203-
C.atomic_fetch_sub_u32(voidptr(&a.val), delta)
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]
241+
pub 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)
204261
} else {
205-
C.atomic_fetch_sub_u64(voidptr(&a.val), delta)
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]
290+
pub 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))
206330
}
207331
}
208-
return T(a.val)
332+
panic('unreachable')
209333
}

0 commit comments

Comments
 (0)