#![cfg_attr(not(test), no_std)]
#![doc = include_str!("../README.md")]
use core::{
marker::PhantomData,
sync::atomic::{AtomicPtr, Ordering},
};
fn load(ordering: Ordering) -> Ordering {
match ordering {
Ordering::Relaxed | Ordering::Acquire => Ordering::Acquire,
Ordering::AcqRel => Ordering::AcqRel,
Ordering::SeqCst => Ordering::SeqCst,
Ordering::Release => panic!("Release ordering cannot be used for loads"),
_ => unimplemented!("{ordering:?} is not supported"),
}
}
fn store(ordering: Ordering) -> Ordering {
match ordering {
Ordering::Relaxed | Ordering::Release => Ordering::Release,
Ordering::AcqRel => Ordering::AcqRel,
Ordering::SeqCst => Ordering::SeqCst,
Ordering::Acquire => panic!("Acquire ordering cannot be used for stores"),
_ => unimplemented!("{ordering:?} is not supported"),
}
}
fn load_store(ordering: Ordering) -> Ordering {
match ordering {
Ordering::Relaxed | Ordering::Release | Ordering::Acquire | Ordering::AcqRel => {
Ordering::AcqRel
}
Ordering::SeqCst => Ordering::SeqCst,
_ => unimplemented!("{ordering:?} is not supported"),
}
}
pub struct RefSwap<'a, T> {
ptr: AtomicPtr<T>,
phantom: PhantomData<&'a T>,
}
impl<'a, T> RefSwap<'a, T> {
pub const fn new(data: &'a T) -> Self {
Self {
ptr: AtomicPtr::new(data as *const _ as *mut _),
phantom: PhantomData,
}
}
#[deprecated(note = "Use `compare_exchange` or `compare_exchange_weak` instead")]
pub fn compare_and_swap(&self, current: &'a T, new: &'a T, order: Ordering) -> &'a T {
#[allow(deprecated)]
let ptr = self.ptr.compare_and_swap(
current as *const _ as *mut _,
new as *const _ as *mut _,
load_store(order),
);
unsafe { &*ptr }
}
pub fn compare_exchange(
&self,
current: &'a T,
new: &'a T,
success: Ordering,
failure: Ordering,
) -> Result<&'a T, &'a T> {
let res = self.ptr.compare_exchange(
current as *const _ as *mut _,
new as *const _ as *mut _,
load_store(success),
load(failure),
);
res.map(|ptr| unsafe { &*ptr })
.map_err(|ptr| unsafe { &*ptr })
}
pub fn compare_exchange_weak(
&self,
current: &'a T,
new: &'a T,
success: Ordering,
failure: Ordering,
) -> Result<&'a T, &'a T> {
let res = self.ptr.compare_exchange_weak(
current as *const _ as *mut _,
new as *const _ as *mut _,
load_store(success),
load(failure),
);
res.map(|ptr| unsafe { &*ptr })
.map_err(|ptr| unsafe { &*ptr })
}
pub fn get_mut<'s>(&'s mut self) -> &'s mut &'a T {
let res: &'s mut *mut T = self.ptr.get_mut();
unsafe { &mut *(res as *mut *mut T as *mut &'a T) }
}
pub fn into_inner(self) -> &'a T {
let res = self.ptr.into_inner();
unsafe { &*res }
}
pub fn fetch_update<F: FnMut(&'a T) -> Option<&'a T>>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<&'a T, &'a T> {
self.ptr
.fetch_update(load_store(set_order), load(fetch_order), |ptr| {
f(unsafe { &*ptr }).map(|r| r as *const _ as *mut _)
})
.map(|ptr| unsafe { &*ptr })
.map_err(|ptr| unsafe { &*ptr })
}
pub fn load(&self, order: Ordering) -> &'a T {
let res = self.ptr.load(load(order));
unsafe { &*res }
}
pub fn store(&self, ptr: &'a T, order: Ordering) {
self.ptr.store(ptr as *const _ as *mut _, store(order));
}
pub fn swap(&self, ptr: &'a T, order: Ordering) -> &'a T {
let res = self.ptr.swap(ptr as *const _ as *mut _, load_store(order));
unsafe { &*res }
}
}
pub struct OptionRefSwap<'a, T> {
ptr: AtomicPtr<T>,
phantom: PhantomData<&'a T>,
}
const fn opt_to_ptr<T>(ptr: Option<&T>) -> *mut T {
match ptr {
Some(r) => r as *const _ as *mut _,
None => core::ptr::null_mut(),
}
}
unsafe fn ptr_to_opt<'a, T>(ptr: *mut T) -> Option<&'a T> {
if ptr.is_null() {
None
} else {
Some(unsafe { &*ptr })
}
}
impl<'a, T> OptionRefSwap<'a, T> {
pub const fn new(data: Option<&'a T>) -> Self {
Self {
ptr: AtomicPtr::new(opt_to_ptr(data)),
phantom: PhantomData,
}
}
#[deprecated(note = "Use `compare_exchange` or `compare_exchange_weak` instead")]
pub fn compare_and_swap(
&self,
current: Option<&'a T>,
new: Option<&'a T>,
order: Ordering,
) -> Option<&'a T> {
#[allow(deprecated)]
let ptr =
self.ptr
.compare_and_swap(opt_to_ptr(current), opt_to_ptr(new), load_store(order));
unsafe { ptr_to_opt(ptr) }
}
pub fn compare_exchange(
&self,
current: Option<&'a T>,
new: Option<&'a T>,
success: Ordering,
failure: Ordering,
) -> Result<Option<&'a T>, Option<&'a T>> {
let res = self.ptr.compare_exchange(
opt_to_ptr(current),
opt_to_ptr(new),
load_store(success),
load(failure),
);
res.map(|ptr| unsafe { ptr_to_opt(ptr) })
.map_err(|ptr| unsafe { ptr_to_opt(ptr) })
}
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>> {
let res = self.ptr.compare_exchange_weak(
opt_to_ptr(current),
opt_to_ptr(new),
load_store(success),
load(failure),
);
res.map(|ptr| unsafe { ptr_to_opt(ptr) })
.map_err(|ptr| unsafe { ptr_to_opt(ptr) })
}
#[allow(unused)]
fn get_mut<'s>(&'s mut self) -> &'s mut Option<&'a T> {
let res: &'s mut *mut T = self.ptr.get_mut();
unsafe { core::mem::transmute(res) }
}
pub fn into_inner(self) -> &'a T {
let res = self.ptr.into_inner();
unsafe { &*res }
}
pub fn fetch_update<F: FnMut(Option<&'a T>) -> Option<Option<&'a T>>>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<Option<&'a T>, Option<&'a T>> {
self.ptr
.fetch_update(load_store(set_order), load(fetch_order), |ptr| {
f(unsafe { ptr_to_opt(ptr) }).map(opt_to_ptr)
})
.map(|ptr| unsafe { ptr_to_opt(ptr) })
.map_err(|ptr| unsafe { ptr_to_opt(ptr) })
}
pub fn load(&self, order: Ordering) -> Option<&'a T> {
let res = self.ptr.load(load(order));
unsafe { ptr_to_opt(res) }
}
pub fn store(&self, ptr: Option<&'a T>, order: Ordering) {
self.ptr.store(opt_to_ptr(ptr), store(order));
}
pub fn swap(&self, ptr: Option<&'a T>, order: Ordering) -> Option<&'a T> {
let res = self.ptr.swap(opt_to_ptr(ptr), load_store(order));
unsafe { ptr_to_opt(res) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused)]
fn variance<'a, 'b>(a: &'a u32, b: Option<&'b u32>) {
let r = RefSwap::new(a);
let stat: &'static u32 = &123;
r.store(stat, Ordering::Relaxed);
let r = OptionRefSwap::new(b);
let stat: Option<&'static u32> = Some(&123);
r.store(b, Ordering::Relaxed);
}
}