#![warn(missing_docs)]
#![no_std]
#![cfg_attr(
feature = "nightly", feature(const_fn, cfg_target_has_atomic, atomic_min_max)
)]
#[cfg(any(test, feature = "std"))]
#[macro_use]
extern crate std;
pub use core::sync::atomic::{fence, Ordering};
use core::cell::UnsafeCell;
use core::fmt;
#[cfg(feature = "std")]
use std::panic::RefUnwindSafe;
mod fallback;
mod ops;
pub struct Atomic<T: Copy> {
v: UnsafeCell<T>,
}
unsafe impl<T: Copy + Send> Sync for Atomic<T> {}
#[cfg(feature = "std")]
impl<T: Copy + RefUnwindSafe> RefUnwindSafe for Atomic<T> {}
impl<T: Copy + Default> Default for Atomic<T> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T: Copy + fmt::Debug> fmt::Debug for Atomic<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Atomic")
.field(&self.load(Ordering::SeqCst))
.finish()
}
}
impl<T: Copy> Atomic<T> {
#[inline]
#[cfg(feature = "nightly")]
pub const fn new(v: T) -> Atomic<T> {
Atomic {
v: UnsafeCell::new(v),
}
}
#[inline]
#[cfg(not(feature = "nightly"))]
pub fn new(v: T) -> Atomic<T> {
Atomic {
v: UnsafeCell::new(v),
}
}
#[inline]
#[cfg(feature = "nightly")]
pub const fn is_lock_free() -> bool {
ops::atomic_is_lock_free::<T>()
}
#[inline]
#[cfg(not(feature = "nightly"))]
pub fn is_lock_free() -> bool {
ops::atomic_is_lock_free::<T>()
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.v.get() }
}
#[inline]
pub fn into_inner(self) -> T {
self.v.into_inner()
}
#[inline]
pub fn load(&self, order: Ordering) -> T {
unsafe { ops::atomic_load(self.v.get(), order) }
}
#[inline]
pub fn store(&self, val: T, order: Ordering) {
unsafe {
ops::atomic_store(self.v.get(), val, order);
}
}
#[inline]
pub fn swap(&self, val: T, order: Ordering) -> T {
unsafe { ops::atomic_swap(self.v.get(), val, order) }
}
#[inline]
pub fn compare_exchange(
&self,
current: T,
new: T,
success: Ordering,
failure: Ordering,
) -> Result<T, T> {
unsafe { ops::atomic_compare_exchange(self.v.get(), current, new, success, failure) }
}
#[inline]
pub fn compare_exchange_weak(
&self,
current: T,
new: T,
success: Ordering,
failure: Ordering,
) -> Result<T, T> {
unsafe { ops::atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) }
}
}
impl Atomic<bool> {
#[inline]
pub fn fetch_and(&self, val: bool, order: Ordering) -> bool {
unsafe { ops::atomic_and(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_or(&self, val: bool, order: Ordering) -> bool {
unsafe { ops::atomic_or(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool {
unsafe { ops::atomic_xor(self.v.get(), val, order) }
}
}
macro_rules! atomic_ops_common {
($($t:ty)*) => ($(
impl Atomic<$t> {
#[inline]
pub fn fetch_add(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_add(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_sub(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_sub(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_and(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_and(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_or(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_or(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_xor(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_xor(self.v.get(), val, order) }
}
}
)*);
}
macro_rules! atomic_ops_signed {
($($t:ty)*) => (
atomic_ops_common!{ $($t)* }
$(
impl Atomic<$t> {
#[inline]
pub fn fetch_min(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_min(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_max(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_max(self.v.get(), val, order) }
}
}
)*
);
}
macro_rules! atomic_ops_unsigned {
($($t:ty)*) => (
atomic_ops_common!{ $($t)* }
$(
impl Atomic<$t> {
#[inline]
pub fn fetch_min(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_umin(self.v.get(), val, order) }
}
#[inline]
pub fn fetch_max(&self, val: $t, order: Ordering) -> $t {
unsafe { ops::atomic_umax(self.v.get(), val, order) }
}
}
)*
);
}
atomic_ops_signed!{ i8 i16 i32 i64 isize i128 }
atomic_ops_unsigned!{ u8 u16 u32 u64 usize u128 }
#[cfg(test)]
mod tests {
use core::mem;
use Atomic;
use Ordering::*;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
struct Foo(u8, u8);
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
struct Bar(u64, u64);
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
struct Quux(u32);
#[test]
fn atomic_bool() {
let a = Atomic::new(false);
assert_eq!(Atomic::<bool>::is_lock_free(), cfg!(feature = "nightly"));
assert_eq!(format!("{:?}", a), "Atomic(false)");
assert_eq!(a.load(SeqCst), false);
a.store(true, SeqCst);
assert_eq!(a.swap(false, SeqCst), true);
assert_eq!(a.compare_exchange(true, false, SeqCst, SeqCst), Err(false));
assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false));
assert_eq!(a.fetch_and(false, SeqCst), true);
assert_eq!(a.fetch_or(true, SeqCst), false);
assert_eq!(a.fetch_xor(false, SeqCst), true);
assert_eq!(a.load(SeqCst), true);
}
#[test]
fn atomic_i8() {
let a = Atomic::new(0i8);
assert_eq!(
Atomic::<i8>::is_lock_free(),
cfg!(any(
target_pointer_width = "8",
all(feature = "nightly", target_has_atomic = "8")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(-56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), -74);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(-25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_i16() {
let a = Atomic::new(0i16);
assert_eq!(
Atomic::<i16>::is_lock_free(),
cfg!(any(
target_pointer_width = "16",
all(feature = "nightly", target_has_atomic = "16")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(-56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 182);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(-25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_i32() {
let a = Atomic::new(0i32);
assert_eq!(
Atomic::<i32>::is_lock_free(),
cfg!(any(
target_pointer_width = "32",
all(feature = "nightly", target_has_atomic = "32")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(-56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 182);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(-25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_i64() {
let a = Atomic::new(0i64);
assert_eq!(
Atomic::<i64>::is_lock_free(),
cfg!(any(
target_pointer_width = "64",
all(feature = "nightly", target_has_atomic = "64")
)) && mem::align_of::<i64>() == 8
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(-56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 182);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(-25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_i128() {
let a = Atomic::new(0i128);
assert_eq!(
Atomic::<i128>::is_lock_free(),
cfg!(any(
target_pointer_width = "128",
all(feature = "nightly", target_has_atomic = "128")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(-56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 182);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(-25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_isize() {
let a = Atomic::new(0isize);
assert!(Atomic::<isize>::is_lock_free());
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(-56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 182);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(-25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_u8() {
let a = Atomic::new(0u8);
assert_eq!(
Atomic::<u8>::is_lock_free(),
cfg!(any(
target_pointer_width = "8",
all(feature = "nightly", target_has_atomic = "8")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 70);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_u16() {
let a = Atomic::new(0u16);
assert_eq!(
Atomic::<u16>::is_lock_free(),
cfg!(any(
target_pointer_width = "16",
all(feature = "nightly", target_has_atomic = "16")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 70);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_u32() {
let a = Atomic::new(0u32);
assert_eq!(
Atomic::<u32>::is_lock_free(),
cfg!(any(
target_pointer_width = "32",
all(feature = "nightly", target_has_atomic = "32")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 70);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_u64() {
let a = Atomic::new(0u64);
assert_eq!(
Atomic::<u64>::is_lock_free(),
cfg!(any(
target_pointer_width = "64",
all(feature = "nightly", target_has_atomic = "64")
)) && mem::align_of::<u64>() == 8
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 70);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_u128() {
let a = Atomic::new(0u128);
assert_eq!(
Atomic::<u128>::is_lock_free(),
cfg!(any(
target_pointer_width = "128",
all(feature = "nightly", target_has_atomic = "128")
))
);
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 70);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_usize() {
let a = Atomic::new(0usize);
assert!(Atomic::<usize>::is_lock_free());
assert_eq!(format!("{:?}", a), "Atomic(0)");
assert_eq!(a.load(SeqCst), 0);
a.store(1, SeqCst);
assert_eq!(a.swap(2, SeqCst), 1);
assert_eq!(a.compare_exchange(5, 45, SeqCst, SeqCst), Err(2));
assert_eq!(a.compare_exchange(2, 3, SeqCst, SeqCst), Ok(2));
assert_eq!(a.fetch_add(123, SeqCst), 3);
assert_eq!(a.fetch_sub(56, SeqCst), 126);
assert_eq!(a.fetch_and(7, SeqCst), 70);
assert_eq!(a.fetch_or(64, SeqCst), 6);
assert_eq!(a.fetch_xor(1, SeqCst), 70);
assert_eq!(a.fetch_min(30, SeqCst), 71);
assert_eq!(a.fetch_max(25, SeqCst), 30);
assert_eq!(a.load(SeqCst), 30);
}
#[test]
fn atomic_foo() {
let a = Atomic::default();
assert_eq!(Atomic::<Foo>::is_lock_free(), false);
assert_eq!(format!("{:?}", a), "Atomic(Foo(0, 0))");
assert_eq!(a.load(SeqCst), Foo(0, 0));
a.store(Foo(1, 1), SeqCst);
assert_eq!(a.swap(Foo(2, 2), SeqCst), Foo(1, 1));
assert_eq!(
a.compare_exchange(Foo(5, 5), Foo(45, 45), SeqCst, SeqCst),
Err(Foo(2, 2))
);
assert_eq!(
a.compare_exchange(Foo(2, 2), Foo(3, 3), SeqCst, SeqCst),
Ok(Foo(2, 2))
);
assert_eq!(a.load(SeqCst), Foo(3, 3));
}
#[test]
fn atomic_bar() {
let a = Atomic::default();
assert_eq!(Atomic::<Bar>::is_lock_free(), false);
assert_eq!(format!("{:?}", a), "Atomic(Bar(0, 0))");
assert_eq!(a.load(SeqCst), Bar(0, 0));
a.store(Bar(1, 1), SeqCst);
assert_eq!(a.swap(Bar(2, 2), SeqCst), Bar(1, 1));
assert_eq!(
a.compare_exchange(Bar(5, 5), Bar(45, 45), SeqCst, SeqCst),
Err(Bar(2, 2))
);
assert_eq!(
a.compare_exchange(Bar(2, 2), Bar(3, 3), SeqCst, SeqCst),
Ok(Bar(2, 2))
);
assert_eq!(a.load(SeqCst), Bar(3, 3));
}
#[test]
fn atomic_quxx() {
let a = Atomic::default();
assert_eq!(
Atomic::<Quux>::is_lock_free(),
cfg!(any(feature = "nightly", target_pointer_width = "32"))
);
assert_eq!(format!("{:?}", a), "Atomic(Quux(0))");
assert_eq!(a.load(SeqCst), Quux(0));
a.store(Quux(1), SeqCst);
assert_eq!(a.swap(Quux(2), SeqCst), Quux(1));
assert_eq!(
a.compare_exchange(Quux(5), Quux(45), SeqCst, SeqCst),
Err(Quux(2))
);
assert_eq!(
a.compare_exchange(Quux(2), Quux(3), SeqCst, SeqCst),
Ok(Quux(2))
);
assert_eq!(a.load(SeqCst), Quux(3));
}
}