#![no_std]
use core::default::Default;
use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
#[repr(C)]
pub struct AtomicRef<'a, T: 'a> {
data: AtomicPtr<T>,
_marker: PhantomData<Invariant<'a, T>>,
}
struct Invariant<'a, T: 'a>(&'a mut &'a T);
#[inline(always)]
const fn from_opt<'a, T>(p: Option<&'a T>) -> *mut T {
match p {
Some(p) => p as *const T as *mut T,
None => ptr::null_mut(),
}
}
#[inline(always)]
unsafe fn to_opt<'a, T>(p: *mut T) -> Option<&'a T> {
p.as_ref()
}
#[inline]
fn enforce_load_ordering(order: Ordering) -> Ordering {
match order {
Ordering::Relaxed | Ordering::Acquire => Ordering::Acquire,
Ordering::SeqCst => Ordering::SeqCst,
Ordering::Release => panic!("there is no such thing as a release load"),
Ordering::AcqRel => panic!("there is no such thing as an acquire/release load"),
_ => panic!("unsupported memory ordering: {:?}", order),
}
}
#[inline]
fn enforce_store_ordering(order: Ordering) -> Ordering {
match order {
Ordering::Relaxed | Ordering::Release => Ordering::Release,
Ordering::SeqCst => Ordering::SeqCst,
Ordering::Acquire => panic!("there is no such thing as an acquire store"),
Ordering::AcqRel => panic!("there is no such thing as an acquire/release store"),
_ => panic!("unsupported memory ordering: {:?}", order),
}
}
#[inline]
fn enforce_swap_ordering(order: Ordering) -> Ordering {
match order {
Ordering::Relaxed | Ordering::Acquire | Ordering::Release | Ordering::AcqRel => {
Ordering::AcqRel
}
Ordering::SeqCst => Ordering::SeqCst,
_ => panic!("unsupported memory ordering: {:?}", order),
}
}
impl<'a, T> AtomicRef<'a, T> {
pub const fn new(p: Option<&'a T>) -> AtomicRef<'a, T> {
AtomicRef {
data: AtomicPtr::new(from_opt(p)),
_marker: PhantomData,
}
}
pub fn get_mut(&mut self) -> &mut Option<&'a T> {
debug_assert_eq!(mem::size_of::<Option<&'a T>>(), mem::size_of::<*mut T>());
unsafe { mem::transmute::<&mut *mut T, &mut Option<&'a T>>(self.data.get_mut()) }
}
pub fn into_inner(self) -> Option<&'a T> {
unsafe { to_opt(self.data.into_inner()) }
}
pub fn load(&self, ordering: Ordering) -> Option<&'a T> {
unsafe { to_opt(self.data.load(enforce_load_ordering(ordering))) }
}
pub fn store(&self, ptr: Option<&'a T>, order: Ordering) {
self.data
.store(from_opt(ptr), enforce_store_ordering(order))
}
pub fn swap(&self, p: Option<&'a T>, order: Ordering) -> Option<&'a T> {
unsafe { to_opt(self.data.swap(from_opt(p), enforce_swap_ordering(order))) }
}
#[allow(deprecated)]
pub fn compare_and_swap(
&self,
current: Option<&'a T>,
new: Option<&'a T>,
order: Ordering,
) -> Option<&'a T> {
unsafe {
to_opt(self.data.compare_and_swap(
from_opt(current),
from_opt(new),
enforce_swap_ordering(order),
))
}
}
pub fn compare_exchange(
&self,
current: Option<&'a T>,
new: Option<&'a T>,
success: Ordering,
failure: Ordering,
) -> Result<Option<&'a T>, Option<&'a T>> {
unsafe {
match self.data.compare_exchange(
from_opt(current),
from_opt(new),
enforce_swap_ordering(success),
enforce_load_ordering(failure),
) {
Ok(p) => Ok(to_opt(p)),
Err(p) => Err(to_opt(p)),
}
}
}
pub fn compare_exchange_weak(
&self,
current: Option<&'a T>,
new: Option<&'a T>,
success: Ordering,
failure: Ordering,
) -> Result<Option<&'a T>, Option<&'a T>> {
unsafe {
match self.data.compare_exchange_weak(
from_opt(current),
from_opt(new),
enforce_swap_ordering(success),
enforce_load_ordering(failure),
) {
Ok(p) => Ok(to_opt(p)),
Err(p) => Err(to_opt(p)),
}
}
}
}
impl<'a, T: fmt::Debug> fmt::Debug for AtomicRef<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.load(Ordering::SeqCst), f)
}
}
impl<'a, T> Default for AtomicRef<'a, T> {
fn default() -> AtomicRef<'a, T> {
AtomicRef::new(None)
}
}
impl<'a, T> From<Option<&'a T>> for AtomicRef<'a, T> {
fn from(other: Option<&'a T>) -> AtomicRef<'a, T> {
AtomicRef::new(other)
}
}
#[cfg(test)]
mod tests {
use super::AtomicRef;
use core::sync::atomic::Ordering;
static FOO: AtomicRef<i32> = AtomicRef::new(None);
static A: i32 = 10;
#[test]
fn it_works() {
assert!(FOO.load(Ordering::SeqCst) == None);
FOO.store(Some(&A), Ordering::SeqCst);
assert!(FOO.load(Ordering::SeqCst) == Some(&A));
assert!(FOO.load(Ordering::SeqCst).unwrap() as *const _ == &A as *const _);
}
}