mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
334 lines
9.6 KiB
V
334 lines
9.6 KiB
V
module stdatomic
|
|
|
|
// Implement the atomic operations. For now TCC does not support the atomic
|
|
// versions on nix so it uses locks to simulate the same behavior.
|
|
//
|
|
// On windows tcc can simulate with other atomic operations.
|
|
//
|
|
// Note: this implementations should be regarded as alpha stage and be tested
|
|
// much more.
|
|
|
|
// add_u64 adds provided delta as an atomic operation
|
|
@[inline]
|
|
pub fn add_u64(ptr &u64, delta int) u64 {
|
|
C.atomic_fetch_add_u64(voidptr(ptr), delta)
|
|
return *ptr
|
|
}
|
|
|
|
// sub_u64 subtracts provided delta as an atomic operation
|
|
@[inline]
|
|
pub fn sub_u64(ptr &u64, delta int) u64 {
|
|
C.atomic_fetch_sub_u64(voidptr(ptr), delta)
|
|
return *ptr
|
|
}
|
|
|
|
// add_i64 adds provided delta as an atomic operation
|
|
@[inline]
|
|
pub fn add_i64(ptr &i64, delta int) i64 {
|
|
C.atomic_fetch_add_u64(voidptr(ptr), delta)
|
|
return *ptr
|
|
}
|
|
|
|
// add_i64 subtracts provided delta as an atomic operation
|
|
@[inline]
|
|
pub fn sub_i64(ptr &i64, delta int) i64 {
|
|
C.atomic_fetch_sub_u64(voidptr(ptr), delta)
|
|
return *ptr
|
|
}
|
|
|
|
// atomic store/load operations have to be used when there might be another concurrent access
|
|
// atomicall set a value
|
|
@[inline]
|
|
pub fn store_u64(ptr &u64, val u64) {
|
|
C.atomic_store_u64(voidptr(ptr), val)
|
|
}
|
|
|
|
// atomicall get a value
|
|
@[inline]
|
|
pub fn load_u64(ptr &u64) u64 {
|
|
return C.atomic_load_u64(voidptr(ptr))
|
|
}
|
|
|
|
// atomicall set a value
|
|
@[inline]
|
|
pub fn store_i64(ptr &i64, val i64) {
|
|
C.atomic_store_u64(voidptr(ptr), val)
|
|
}
|
|
|
|
// atomicall get a value
|
|
@[inline]
|
|
pub fn load_i64(ptr &i64) i64 {
|
|
return i64(C.atomic_load_u64(voidptr(ptr)))
|
|
}
|
|
|
|
@[heap]
|
|
pub struct AtomicVal[T] {
|
|
val T
|
|
}
|
|
|
|
// new_atomic creates a new atomic value of `T` type
|
|
@[inline]
|
|
pub fn new_atomic[T](val T) &AtomicVal[T] {
|
|
$if T is $int {
|
|
return &AtomicVal[T]{
|
|
val: val
|
|
}
|
|
} $else $if T is bool {
|
|
return &AtomicVal[T]{
|
|
val: val
|
|
}
|
|
} $else $if T is voidptr {
|
|
return &AtomicVal[T]{
|
|
val: val
|
|
}
|
|
} $else {
|
|
$compile_error('atomic: only support number, bool, and voidptr types')
|
|
}
|
|
return unsafe { nil }
|
|
}
|
|
|
|
// load returns current value of the atomic value
|
|
@[inline]
|
|
pub fn (mut a AtomicVal[T]) load() T {
|
|
$if T is bool {
|
|
return C.atomic_load_byte(voidptr(&a.val)) != 0
|
|
} $else $if T is u8 || T is i8 {
|
|
return T(C.atomic_load_byte(voidptr(&a.val)))
|
|
} $else $if T is u16 || T is i16 {
|
|
return T(C.atomic_load_u16(voidptr(&a.val)))
|
|
} $else $if T is u32 || T is i32 {
|
|
return T(C.atomic_load_u32(voidptr(&a.val)))
|
|
} $else $if T is u64 || T is i64 {
|
|
return T(C.atomic_load_u64(voidptr(&a.val)))
|
|
} $else $if T is int {
|
|
if sizeof(int) == 4 {
|
|
return int(C.atomic_load_u32(voidptr(&a.val)))
|
|
} else {
|
|
return int(C.atomic_load_u64(voidptr(&a.val)))
|
|
}
|
|
} $else $if T is isize || T is usize {
|
|
if sizeof(isize) == 4 {
|
|
return T(C.atomic_load_u32(voidptr(&a.val)))
|
|
} else {
|
|
return T(C.atomic_load_u64(voidptr(&a.val)))
|
|
}
|
|
} $else $if T is voidptr {
|
|
// TODO: this should be $if sizeof(T) == 4
|
|
$if x32 {
|
|
return T(C.atomic_load_u32(voidptr(&a.val)))
|
|
} $else {
|
|
return T(C.atomic_load_u64(voidptr(&a.val)))
|
|
}
|
|
}
|
|
return a.val
|
|
}
|
|
|
|
// store updates the atomic value with `val`
|
|
@[inline]
|
|
pub fn (mut a AtomicVal[T]) store(val T) {
|
|
$if T is bool {
|
|
C.atomic_store_byte(voidptr(&a.val), u8(val))
|
|
} $else $if T is u8 || T is i8 {
|
|
C.atomic_store_byte(voidptr(&a.val), u8(val))
|
|
} $else $if T is u16 || T is i16 {
|
|
C.atomic_store_u16(voidptr(&a.val), u16(val))
|
|
} $else $if T is u32 || T is i32 {
|
|
C.atomic_store_u32(voidptr(&a.val), u32(val))
|
|
} $else $if T is u64 || T is i64 {
|
|
C.atomic_store_u64(voidptr(&a.val), u64(val))
|
|
} $else $if T is int {
|
|
if sizeof(int) == 4 {
|
|
C.atomic_store_u32(voidptr(&a.val), u32(val))
|
|
} else {
|
|
C.atomic_store_u64(voidptr(&a.val), u64(val))
|
|
}
|
|
} $else $if T is isize || T is usize {
|
|
if sizeof(isize) == 4 {
|
|
C.atomic_store_u32(voidptr(&a.val), u32(val))
|
|
} else {
|
|
C.atomic_store_u64(voidptr(&a.val), u64(val))
|
|
}
|
|
} $else $if T is voidptr {
|
|
// TODO: this should be $if sizeof(T) == 4
|
|
$if x32 {
|
|
C.atomic_store_u32(voidptr(&a.val), u32(val))
|
|
} $else {
|
|
C.atomic_store_u64(voidptr(&a.val), u64(val))
|
|
}
|
|
}
|
|
}
|
|
|
|
// add adds the atomic value with `delta` and returns the previous value
|
|
@[inline]
|
|
pub fn (mut a AtomicVal[T]) add(delta T) T {
|
|
$if T is bool {
|
|
panic('atomic: add() not supported for bool type')
|
|
} $else $if T is voidptr {
|
|
panic('atomic: add() not supported for voidptr type')
|
|
} $else $if T is u8 || T is i8 {
|
|
old := C.atomic_fetch_add_byte(voidptr(&a.val), u8(delta))
|
|
return T(old)
|
|
} $else $if T is u16 || T is i16 {
|
|
old := C.atomic_fetch_add_u16(voidptr(&a.val), u16(delta))
|
|
return T(old)
|
|
} $else $if T is u32 || T is i32 {
|
|
old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
|
|
return T(old)
|
|
} $else $if T is u64 || T is i64 {
|
|
old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
|
|
return T(old)
|
|
} $else $if T is int {
|
|
if sizeof(int) == 4 {
|
|
old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
|
|
return T(old)
|
|
} else {
|
|
old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
|
|
return T(old)
|
|
}
|
|
} $else $if T is isize || T is usize {
|
|
if sizeof(isize) == 4 {
|
|
old := C.atomic_fetch_add_u32(voidptr(&a.val), u32(delta))
|
|
return T(old)
|
|
} else {
|
|
old := C.atomic_fetch_add_u64(voidptr(&a.val), u64(delta))
|
|
return T(old)
|
|
}
|
|
}
|
|
panic('unreachable')
|
|
}
|
|
|
|
// sub subtracts the atomic value with `delta` and returns the previous value
|
|
@[inline]
|
|
pub fn (mut a AtomicVal[T]) sub(delta T) T {
|
|
$if T is bool {
|
|
panic('atomic: sub() not supported for bool type')
|
|
} $else $if T is voidptr {
|
|
panic('atomic: sub() not supported for voidptr type')
|
|
} $else $if T is u8 || T is i8 {
|
|
old := C.atomic_fetch_sub_byte(voidptr(&a.val), u8(delta))
|
|
return T(old)
|
|
} $else $if T is u16 || T is i16 {
|
|
old := C.atomic_fetch_sub_u16(voidptr(&a.val), u16(delta))
|
|
return T(old)
|
|
} $else $if T is u32 || T is i32 {
|
|
old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
|
|
return T(old)
|
|
} $else $if T is u64 || T is i64 {
|
|
old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
|
|
return T(old)
|
|
} $else $if T is int {
|
|
if sizeof(int) == 4 {
|
|
old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
|
|
return T(old)
|
|
} else {
|
|
old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
|
|
return T(old)
|
|
}
|
|
} $else $if T is isize || T is usize {
|
|
if sizeof(isize) == 4 {
|
|
old := C.atomic_fetch_sub_u32(voidptr(&a.val), u32(delta))
|
|
return T(old)
|
|
} else {
|
|
old := C.atomic_fetch_sub_u64(voidptr(&a.val), u64(delta))
|
|
return T(old)
|
|
}
|
|
}
|
|
panic('unreachable')
|
|
}
|
|
|
|
// swap sets the `new` value and returns the previous value
|
|
@[inline]
|
|
pub fn (mut a AtomicVal[T]) swap(new T) T {
|
|
$if T is bool {
|
|
old := C.atomic_exchange_byte(voidptr(&a.val), u8(new))
|
|
return old != 0
|
|
} $else $if T is u8 || T is i8 {
|
|
old := C.atomic_exchange_byte(voidptr(&a.val), u8(new))
|
|
return T(old)
|
|
} $else $if T is u16 || T is i16 {
|
|
old := C.atomic_exchange_u16(voidptr(&a.val), u16(new))
|
|
return T(old)
|
|
} $else $if T is u32 || T is i32 {
|
|
old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
|
|
return T(old)
|
|
} $else $if T is u64 || T is i64 {
|
|
old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
|
|
return T(old)
|
|
} $else $if T is int {
|
|
if sizeof(int) == 4 {
|
|
old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
|
|
return T(old)
|
|
} else {
|
|
old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
|
|
return T(old)
|
|
}
|
|
} $else $if T is isize || T is usize {
|
|
if sizeof(isize) == 4 {
|
|
old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
|
|
return T(old)
|
|
} else {
|
|
old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
|
|
return T(old)
|
|
}
|
|
} $else $if T is voidptr {
|
|
// TODO: this should be $if sizeof(T) == 4
|
|
$if x32 {
|
|
old := C.atomic_exchange_u32(voidptr(&a.val), u32(new))
|
|
return T(old)
|
|
} $else {
|
|
old := C.atomic_exchange_u64(voidptr(&a.val), u64(new))
|
|
return T(old)
|
|
}
|
|
}
|
|
panic('unreachable')
|
|
}
|
|
|
|
// compare_and_swap executes the compare-and-swap(CAS) operation
|
|
// if atomic value == `expected`, then it will be set to `new`, and return true
|
|
// else return false, and the atomic value remains unchanged
|
|
@[inline]
|
|
pub fn (mut a AtomicVal[T]) compare_and_swap(expected T, new T) bool {
|
|
$if T is bool {
|
|
mut exp := u8(expected)
|
|
return C.atomic_compare_exchange_strong_byte(voidptr(&a.val), &exp, u8(new))
|
|
} $else $if T is u8 || T is i8 {
|
|
mut exp := u8(expected)
|
|
return C.atomic_compare_exchange_strong_byte(voidptr(&a.val), &exp, u8(new))
|
|
} $else $if T is u16 || T is i16 {
|
|
mut exp := u16(expected)
|
|
return C.atomic_compare_exchange_strong_u16(voidptr(&a.val), &exp, u16(new))
|
|
} $else $if T is u32 || T is i32 {
|
|
mut exp := u32(expected)
|
|
return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
|
|
} $else $if T is u64 || T is i64 {
|
|
mut exp := u64(expected)
|
|
return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
|
|
} $else $if T is int {
|
|
if sizeof(int) == 4 {
|
|
mut exp := u32(expected)
|
|
return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
|
|
} else {
|
|
mut exp := u64(expected)
|
|
return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
|
|
}
|
|
} $else $if T is isize || T is usize {
|
|
if sizeof(isize) == 4 {
|
|
mut exp := u32(expected)
|
|
return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
|
|
} else {
|
|
mut exp := u64(expected)
|
|
return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
|
|
}
|
|
} $else $if T is voidptr {
|
|
// TODO: this should be $if sizeof(T) == 4
|
|
$if x32 {
|
|
mut exp := u32(expected)
|
|
return C.atomic_compare_exchange_strong_u32(voidptr(&a.val), &exp, u32(new))
|
|
} $else {
|
|
mut exp := u64(expected)
|
|
return C.atomic_compare_exchange_strong_u64(voidptr(&a.val), &exp, u64(new))
|
|
}
|
|
}
|
|
panic('unreachable')
|
|
}
|