use std::sync::atomic::Ordering;
use wgsl_rs_ir as ir;
use crate::std::Wgsl;
pub trait AtomicScalar {
type AtomicType: Default;
}
impl AtomicScalar for u32 {
type AtomicType = std::sync::atomic::AtomicU32;
}
impl AtomicScalar for i32 {
type AtomicType = std::sync::atomic::AtomicI32;
}
#[derive(Debug)]
pub struct Atomic<T: AtomicScalar> {
pub(crate) inner: T::AtomicType,
}
impl<T: Wgsl + AtomicScalar> Wgsl for Atomic<T>
where
T::AtomicType: std::any::Any + Send + Sync,
{
fn to_ir() -> wgsl_rs_ir::Type {
ir::Type::Atomic {
elem: Box::new(T::to_ir()),
}
}
}
impl<T: AtomicScalar> Default for Atomic<T> {
fn default() -> Self {
Self {
inner: <T as AtomicScalar>::AtomicType::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AtomicCompareExchangeResult<T> {
pub old_value: T,
pub exchanged: bool,
}
pub fn atomic_load(p: &Atomic<u32>) -> u32 {
p.inner.load(Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_load_i32(p: &Atomic<i32>) -> i32 {
p.inner.load(Ordering::Relaxed)
}
pub fn atomic_store(p: &Atomic<u32>, v: u32) {
p.inner.store(v, Ordering::Relaxed);
}
#[doc(hidden)]
#[inline]
pub fn atomic_store_i32(p: &Atomic<i32>, v: i32) {
p.inner.store(v, Ordering::Relaxed);
}
pub fn atomic_add(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_add(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_add_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_add(v, Ordering::Relaxed)
}
pub fn atomic_sub(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_sub(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_sub_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_sub(v, Ordering::Relaxed)
}
pub fn atomic_max(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_max(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_max_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_max(v, Ordering::Relaxed)
}
pub fn atomic_min(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_min(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_min_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_min(v, Ordering::Relaxed)
}
pub fn atomic_and(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_and(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_and_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_and(v, Ordering::Relaxed)
}
pub fn atomic_or(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_or(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_or_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_or(v, Ordering::Relaxed)
}
pub fn atomic_xor(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.fetch_xor(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_xor_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.fetch_xor(v, Ordering::Relaxed)
}
pub fn atomic_exchange(p: &Atomic<u32>, v: u32) -> u32 {
p.inner.swap(v, Ordering::Relaxed)
}
#[doc(hidden)]
#[inline]
pub fn atomic_exchange_i32(p: &Atomic<i32>, v: i32) -> i32 {
p.inner.swap(v, Ordering::Relaxed)
}
pub fn atomic_compare_exchange_weak(
p: &Atomic<u32>,
cmp: u32,
v: u32,
) -> AtomicCompareExchangeResult<u32> {
match p
.inner
.compare_exchange_weak(cmp, v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(old_value) => AtomicCompareExchangeResult {
old_value,
exchanged: true,
},
Err(old_value) => AtomicCompareExchangeResult {
old_value,
exchanged: false,
},
}
}
#[doc(hidden)]
#[inline]
pub fn atomic_compare_exchange_weak_i32(
p: &Atomic<i32>,
cmp: i32,
v: i32,
) -> AtomicCompareExchangeResult<i32> {
match p
.inner
.compare_exchange_weak(cmp, v, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(old_value) => AtomicCompareExchangeResult {
old_value,
exchanged: true,
},
Err(old_value) => AtomicCompareExchangeResult {
old_value,
exchanged: false,
},
}
}
#[cfg(test)]
mod atomic_tests {
use super::*;
#[test]
fn test_atomic_load_store_u32() {
let a: Atomic<u32> = Atomic::default();
assert_eq!(atomic_load(&a), 0);
atomic_store(&a, 42);
assert_eq!(atomic_load(&a), 42);
atomic_store(&a, u32::MAX);
assert_eq!(atomic_load(&a), u32::MAX);
}
#[test]
fn test_atomic_add_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 10);
let old = atomic_add(&a, 5);
assert_eq!(old, 10);
assert_eq!(atomic_load(&a), 15);
atomic_store(&a, u32::MAX);
let old = atomic_add(&a, 1);
assert_eq!(old, u32::MAX);
assert_eq!(atomic_load(&a), 0); }
#[test]
fn test_atomic_sub_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 10);
let old = atomic_sub(&a, 3);
assert_eq!(old, 10);
assert_eq!(atomic_load(&a), 7);
atomic_store(&a, 0);
let old = atomic_sub(&a, 1);
assert_eq!(old, 0);
assert_eq!(atomic_load(&a), u32::MAX); }
#[test]
fn test_atomic_max_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 10);
let old = atomic_max(&a, 15);
assert_eq!(old, 10);
assert_eq!(atomic_load(&a), 15);
let old = atomic_max(&a, 5);
assert_eq!(old, 15);
assert_eq!(atomic_load(&a), 15);
}
#[test]
fn test_atomic_min_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 10);
let old = atomic_min(&a, 5);
assert_eq!(old, 10);
assert_eq!(atomic_load(&a), 5);
let old = atomic_min(&a, 15);
assert_eq!(old, 5);
assert_eq!(atomic_load(&a), 5);
}
#[test]
fn test_atomic_and_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 0b1111);
let old = atomic_and(&a, 0b1010);
assert_eq!(old, 0b1111);
assert_eq!(atomic_load(&a), 0b1010);
}
#[test]
fn test_atomic_or_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 0b1010);
let old = atomic_or(&a, 0b0101);
assert_eq!(old, 0b1010);
assert_eq!(atomic_load(&a), 0b1111);
}
#[test]
fn test_atomic_xor_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 0b1111);
let old = atomic_xor(&a, 0b1010);
assert_eq!(old, 0b1111);
assert_eq!(atomic_load(&a), 0b0101);
}
#[test]
fn test_atomic_exchange_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 10);
let old = atomic_exchange(&a, 20);
assert_eq!(old, 10);
assert_eq!(atomic_load(&a), 20);
}
#[test]
fn test_atomic_compare_exchange_weak_u32() {
let a: Atomic<u32> = Atomic::default();
atomic_store(&a, 10);
loop {
let result = atomic_compare_exchange_weak(&a, 10, 20);
if result.exchanged {
assert_eq!(result.old_value, 10);
break;
}
assert_eq!(result.old_value, 10);
}
assert_eq!(atomic_load(&a), 20);
let result = atomic_compare_exchange_weak(&a, 999, 30);
assert_eq!(result.old_value, 20);
assert!(!result.exchanged);
assert_eq!(atomic_load(&a), 20);
}
#[test]
fn test_atomic_load_store_i32() {
let a: Atomic<i32> = Atomic::default();
assert_eq!(atomic_load_i32(&a), 0);
atomic_store_i32(&a, 42);
assert_eq!(atomic_load_i32(&a), 42);
atomic_store_i32(&a, -42);
assert_eq!(atomic_load_i32(&a), -42);
atomic_store_i32(&a, i32::MIN);
assert_eq!(atomic_load_i32(&a), i32::MIN);
atomic_store_i32(&a, i32::MAX);
assert_eq!(atomic_load_i32(&a), i32::MAX);
}
#[test]
fn test_atomic_add_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 10);
let old = atomic_add_i32(&a, 5);
assert_eq!(old, 10);
assert_eq!(atomic_load_i32(&a), 15);
let old = atomic_add_i32(&a, -20);
assert_eq!(old, 15);
assert_eq!(atomic_load_i32(&a), -5);
}
#[test]
fn test_atomic_sub_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 10);
let old = atomic_sub_i32(&a, 3);
assert_eq!(old, 10);
assert_eq!(atomic_load_i32(&a), 7);
let old = atomic_sub_i32(&a, -5);
assert_eq!(old, 7);
assert_eq!(atomic_load_i32(&a), 12);
}
#[test]
fn test_atomic_max_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, -10);
let old = atomic_max_i32(&a, 5);
assert_eq!(old, -10);
assert_eq!(atomic_load_i32(&a), 5);
let old = atomic_max_i32(&a, -20);
assert_eq!(old, 5);
assert_eq!(atomic_load_i32(&a), 5);
}
#[test]
fn test_atomic_min_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 10);
let old = atomic_min_i32(&a, -5);
assert_eq!(old, 10);
assert_eq!(atomic_load_i32(&a), -5);
let old = atomic_min_i32(&a, 15);
assert_eq!(old, -5);
assert_eq!(atomic_load_i32(&a), -5);
}
#[test]
fn test_atomic_and_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 0b1111);
let old = atomic_and_i32(&a, 0b1010);
assert_eq!(old, 0b1111);
assert_eq!(atomic_load_i32(&a), 0b1010);
}
#[test]
fn test_atomic_or_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 0b1010);
let old = atomic_or_i32(&a, 0b0101);
assert_eq!(old, 0b1010);
assert_eq!(atomic_load_i32(&a), 0b1111);
}
#[test]
fn test_atomic_xor_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 0b1111);
let old = atomic_xor_i32(&a, 0b1010);
assert_eq!(old, 0b1111);
assert_eq!(atomic_load_i32(&a), 0b0101);
}
#[test]
fn test_atomic_exchange_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, 10);
let old = atomic_exchange_i32(&a, -20);
assert_eq!(old, 10);
assert_eq!(atomic_load_i32(&a), -20);
}
#[test]
fn test_atomic_compare_exchange_weak_i32() {
let a: Atomic<i32> = Atomic::default();
atomic_store_i32(&a, -10);
loop {
let result = atomic_compare_exchange_weak_i32(&a, -10, 20);
if result.exchanged {
assert_eq!(result.old_value, -10);
break;
}
assert_eq!(result.old_value, -10);
}
assert_eq!(atomic_load_i32(&a), 20);
let result = atomic_compare_exchange_weak_i32(&a, 999, 30);
assert_eq!(result.old_value, 20);
assert!(!result.exchanged);
assert_eq!(atomic_load_i32(&a), 20);
}
#[test]
fn test_atomic_compare_exchange_result_struct() {
let result_u32 = AtomicCompareExchangeResult {
old_value: 42u32,
exchanged: true,
};
assert_eq!(result_u32.old_value, 42);
assert!(result_u32.exchanged);
let result_i32 = AtomicCompareExchangeResult {
old_value: -42i32,
exchanged: false,
};
assert_eq!(result_i32.old_value, -42);
assert!(!result_i32.exchanged);
let result1 = AtomicCompareExchangeResult {
old_value: 10u32,
exchanged: true,
};
let result2 = AtomicCompareExchangeResult {
old_value: 10u32,
exchanged: true,
};
assert_eq!(result1, result2);
}
}