atomic-primitive 0.1.3

Traits for primitive atomic types
Documentation
use crate::{
    Atomic, AtomicPrimitive, PrimitiveAtomic, PrimitiveAtomicInteger, PrimitiveAtomicSigned,
    PrimitiveAtomicUnsigned,
};
use core::sync::atomic::{
    AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicIsize, AtomicU8, AtomicU16, AtomicU32,
    AtomicUsize, Ordering,
};
#[cfg(target_has_atomic = "64")]
use core::sync::atomic::{AtomicI64, AtomicU64};

#[test]
fn test_atomic_primitive_mapping() {
    let a: Atomic<u8> = 42u8.to_atomic();
    assert_eq!(a.load(Ordering::Relaxed), 42);

    #[cfg(target_has_atomic = "64")]
    {
        let a: Atomic<u64> = 123u64.to_atomic();
        assert_eq!(a.load(Ordering::Relaxed), 123);
    }

    let a: Atomic<bool> = true.to_atomic();
    assert!(a.load(Ordering::Relaxed));

    let a: Atomic<usize> = 999usize.to_atomic();
    assert_eq!(a.load(Ordering::Relaxed), 999);
}

#[test]
fn test_primitive_atomic_basic() {
    let a = AtomicU32::new(10);
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 10);
    PrimitiveAtomic::store(&a, 20, Ordering::Relaxed);
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 20);
    assert_eq!(PrimitiveAtomic::swap(&a, 30, Ordering::Relaxed), 20);
    assert_eq!(PrimitiveAtomic::into_inner(a), 30);
}

#[cfg(target_has_atomic = "64")]
#[test]
fn test_primitive_atomic_basic_u64() {
    let a = AtomicU64::new(10);
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 10);
    PrimitiveAtomic::store(&a, 20, Ordering::Relaxed);
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 20);
    assert_eq!(PrimitiveAtomic::swap(&a, 30, Ordering::Relaxed), 20);
    assert_eq!(PrimitiveAtomic::into_inner(a), 30);
}

#[test]
fn test_primitive_atomic_cas() {
    let a = AtomicU32::new(5);
    assert_eq!(
        PrimitiveAtomic::compare_exchange(&a, 5, 10, Ordering::SeqCst, Ordering::SeqCst),
        Ok(5)
    );
    assert_eq!(
        PrimitiveAtomic::compare_exchange(&a, 5, 20, Ordering::SeqCst, Ordering::SeqCst),
        Err(10)
    );
}

#[test]
fn test_primitive_atomic_integer() {
    let a = AtomicU32::new(10);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_add(&a, 5, Ordering::Relaxed),
        10
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 15);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_sub(&a, 3, Ordering::Relaxed),
        15
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 12);
}

#[cfg(target_has_atomic = "64")]
#[test]
fn test_primitive_atomic_integer_u64() {
    let a = AtomicU64::new(10);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_add(&a, 5, Ordering::Relaxed),
        10
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 15);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_sub(&a, 3, Ordering::Relaxed),
        15
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 12);
}

#[test]
fn test_bits_constant() {
    assert_eq!(<AtomicU8 as PrimitiveAtomicInteger>::BITS, 8);
    assert_eq!(<AtomicU16 as PrimitiveAtomicInteger>::BITS, 16);
    assert_eq!(<AtomicU32 as PrimitiveAtomicInteger>::BITS, 32);
    #[cfg(target_has_atomic = "64")]
    assert_eq!(<AtomicU64 as PrimitiveAtomicInteger>::BITS, 64);
    assert_eq!(<AtomicUsize as PrimitiveAtomicInteger>::BITS, usize::BITS);
    assert_eq!(<AtomicI8 as PrimitiveAtomicInteger>::BITS, 8);
    assert_eq!(<AtomicI16 as PrimitiveAtomicInteger>::BITS, 16);
    assert_eq!(<AtomicI32 as PrimitiveAtomicInteger>::BITS, 32);
    #[cfg(target_has_atomic = "64")]
    assert_eq!(<AtomicI64 as PrimitiveAtomicInteger>::BITS, 64);
    assert_eq!(<AtomicIsize as PrimitiveAtomicInteger>::BITS, isize::BITS);
}

#[test]
fn test_fetch_bitwise() {
    let a = AtomicU8::new(0b1100);
    assert_eq!(
        PrimitiveAtomic::fetch_or(&a, 0b0011, Ordering::Relaxed),
        0b1100
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 0b1111);
    assert_eq!(
        PrimitiveAtomic::fetch_and(&a, 0b1010, Ordering::Relaxed),
        0b1111
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 0b1010);
    assert_eq!(
        PrimitiveAtomic::fetch_xor(&a, 0b1111, Ordering::Relaxed),
        0b1010
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 0b0101);
}

#[test]
fn test_get_mut() {
    let mut a = AtomicU32::new(42);
    *PrimitiveAtomic::get_mut(&mut a) = 100;
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 100);
}

#[cfg(target_has_atomic = "64")]
#[test]
fn test_get_mut_u64() {
    let mut a = AtomicU64::new(42);
    *PrimitiveAtomic::get_mut(&mut a) = 100;
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 100);
}

#[test]
fn test_fetch_update() {
    let a = AtomicU32::new(10);
    let result = PrimitiveAtomic::fetch_update(&a, Ordering::SeqCst, Ordering::SeqCst, |x| {
        if x < 20 { Some(x + 5) } else { None }
    });
    assert_eq!(result, Ok(10));
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 15);
}

#[test]
fn test_fetch_nand() {
    let a = AtomicU8::new(0b1100);
    assert_eq!(
        PrimitiveAtomic::fetch_nand(&a, 0b1010, Ordering::Relaxed),
        0b1100
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), !0b1000);
}

#[test]
fn test_as_ptr() {
    let a = AtomicU32::new(42);
    let ptr = PrimitiveAtomic::as_ptr(&a);
    assert!(!ptr.is_null());
    // SAFETY: the pointer is valid for the lifetime of `a`.
    assert_eq!(unsafe { *ptr }, 42);
}

#[test]
fn test_compare_exchange_weak() {
    let a = AtomicU32::new(5);
    // compare_exchange_weak may spuriously fail, so loop until success.
    loop {
        match PrimitiveAtomic::compare_exchange_weak(&a, 5, 10, Ordering::SeqCst, Ordering::SeqCst)
        {
            Ok(prev) => {
                assert_eq!(prev, 5);
                break;
            }
            Err(prev) => {
                assert_eq!(prev, 5);
            }
        }
    }
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 10);
}

#[test]
fn test_fetch_max_min() {
    let a = AtomicI32::new(10);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_max(&a, 20, Ordering::Relaxed),
        10
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 20);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_max(&a, 5, Ordering::Relaxed),
        20
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 20);

    assert_eq!(
        PrimitiveAtomicInteger::fetch_min(&a, 15, Ordering::Relaxed),
        20
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 15);
    assert_eq!(
        PrimitiveAtomicInteger::fetch_min(&a, 25, Ordering::Relaxed),
        15
    );
    assert_eq!(PrimitiveAtomic::load(&a, Ordering::Relaxed), 15);
}

fn _assert_primitive_atomic<T: PrimitiveAtomic>() {}
fn _assert_primitive_atomic_integer<T: PrimitiveAtomicInteger>() {}
fn _assert_primitive_atomic_signed<T: PrimitiveAtomicSigned>() {}
fn _assert_primitive_atomic_unsigned<T: PrimitiveAtomicUnsigned>() {}
fn _assert_atomic_primitive<T: AtomicPrimitive>() {}

#[test]
fn test_trait_bounds() {
    _assert_primitive_atomic::<AtomicBool>();
    _assert_primitive_atomic::<AtomicU8>();
    #[cfg(target_has_atomic = "64")]
    _assert_primitive_atomic::<AtomicI64>();

    _assert_primitive_atomic_integer::<AtomicU8>();
    _assert_primitive_atomic_integer::<AtomicI32>();
    _assert_primitive_atomic_integer::<AtomicUsize>();

    _assert_primitive_atomic_signed::<AtomicI8>();
    _assert_primitive_atomic_signed::<AtomicI16>();
    _assert_primitive_atomic_signed::<AtomicIsize>();

    _assert_primitive_atomic_unsigned::<AtomicU8>();
    _assert_primitive_atomic_unsigned::<AtomicUsize>();

    _assert_atomic_primitive::<bool>();
    #[cfg(target_has_atomic = "64")]
    _assert_atomic_primitive::<u64>();
    _assert_atomic_primitive::<usize>();
}