#![no_std]
#![deny(clippy::correctness)]
#![deny(
clippy::perf,
clippy::complexity,
clippy::style,
clippy::nursery,
clippy::pedantic,
clippy::clone_on_ref_ptr,
clippy::decimal_literal_representation,
clippy::float_cmp_const,
clippy::missing_docs_in_private_items,
clippy::multiple_inherent_impl,
clippy::unwrap_used,
clippy::cargo_common_metadata,
clippy::used_underscore_binding
)]
#[cfg(target_has_atomic = "ptr")]
pub use inner::*;
#[cfg(target_has_atomic = "ptr")]
mod inner {
extern crate alloc;
use alloc::boxed::Box;
use core::fmt::{Debug, Formatter};
use core::ops::{Deref, DerefMut};
use core::ptr::null_mut;
use core::sync::atomic::Ordering::SeqCst;
use core::sync::atomic::{AtomicPtr, Ordering};
pub enum BorrowSwapResult<'a, T: Sync + Send + 'static, E: Sync + Send + 'static> {
BorrowSwapped(BorrowGuard<'a, T>),
CellWasEmpty(E),
}
impl<T: Sync + Send + Debug + 'static, E: Sync + Send + Debug + 'static> Debug
for BorrowSwapResult<'_, T, E>
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
BorrowSwapResult::BorrowSwapped(inner) => {
f.write_fmt(format_args!("BorrowSwapped({inner:?})"))
}
BorrowSwapResult::CellWasEmpty(inner) => {
f.write_fmt(format_args!("CellWasEmpty({inner:?})"))
}
}
}
}
impl<'a, T: Sync + Send + 'static, E: Sync + Send + 'static> BorrowSwapResult<'a, T, E> {
pub fn unwrap(self) -> BorrowGuard<'a, T> {
match self {
BorrowSwapResult::BorrowSwapped(inner) => inner,
BorrowSwapResult::CellWasEmpty(_) => {
panic!("unwrap called on BorrowSwapResult::CellWasEmpty()")
}
}
}
pub const fn is_ok(&self) -> bool {
matches!(self, BorrowSwapResult::BorrowSwapped(_))
}
pub const fn is_err(&self) -> bool {
matches!(self, BorrowSwapResult::CellWasEmpty(_))
}
pub fn unwrap_err(self) -> E {
match self {
BorrowSwapResult::BorrowSwapped(_) => {
panic!("unwrap_err called on BorrowSwapResult::BorrowSwapped()")
}
BorrowSwapResult::CellWasEmpty(inner) => inner,
}
}
pub fn map_err<X: Sync + Send + 'static>(
self,
f: impl FnOnce(E) -> X,
) -> BorrowSwapResult<'a, T, X> {
match self {
BorrowSwapResult::BorrowSwapped(inner) => {
BorrowSwapResult::BorrowSwapped::<T, X>(inner)
}
BorrowSwapResult::CellWasEmpty(inner) => BorrowSwapResult::CellWasEmpty(f(inner)),
}
}
pub fn into_result(self) -> Result<BorrowGuard<'a, T>, E> {
match self {
BorrowSwapResult::BorrowSwapped(inner) => Ok(inner),
BorrowSwapResult::CellWasEmpty(inner) => Err(inner),
}
}
}
impl<'a, T: Sync + Send + 'static, E: Sync + Send + 'static> From<BorrowSwapResult<'a, T, E>>
for Result<BorrowGuard<'a, T>, E>
{
fn from(value: BorrowSwapResult<'a, T, E>) -> Self {
value.into_result()
}
}
pub enum PresentResult<T: Sync + Send + 'static> {
CellHadValue(T),
CellWasEmpty(T),
}
impl<T: Sync + Send + Debug + 'static> Debug for PresentResult<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::CellHadValue(inner) => f.write_fmt(format_args!("CellHadValue({inner:?})")),
Self::CellWasEmpty(inner) => f.write_fmt(format_args!("CellWasEmpty({inner:?})")),
}
}
}
impl<T: Sync + Send + 'static> PresentResult<T> {
pub fn unwrap(self) -> T {
match self {
Self::CellHadValue(inner) | Self::CellWasEmpty(inner) => inner,
}
}
pub fn unwrap_empty(self) -> T {
match self {
Self::CellHadValue(_) => {
panic!("unwrap_empty() on PresentResult::CellHadValue")
}
Self::CellWasEmpty(inner) => inner,
}
}
pub fn unwrap_value(self) -> T {
match self {
Self::CellHadValue(inner) => inner,
Self::CellWasEmpty(_) => {
panic!("unwrap_value() on PresentResult::CellWasEmpty")
}
}
}
pub const fn is_value(&self) -> bool {
matches!(self, Self::CellHadValue(_))
}
pub const fn is_empty(&self) -> bool {
matches!(self, Self::CellWasEmpty(_))
}
pub fn map<X: Sync + Send + 'static>(self, f: impl FnOnce(T) -> X) -> PresentResult<X> {
match self {
Self::CellHadValue(inner) => PresentResult::CellHadValue(f(inner)),
Self::CellWasEmpty(inner) => PresentResult::CellWasEmpty(f(inner)),
}
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct AtomicPointerCell<T: Sync + Send + 'static>(AtomicPtr<T>);
impl<T: Sync + Send + 'static> AtomicPointerCell<T> {
#[must_use]
pub const fn new() -> Self {
Self(AtomicPtr::new(null_mut()))
}
#[must_use]
pub const unsafe fn from_ptr(ptr: AtomicPtr<T>) -> Self {
Self(ptr)
}
#[must_use]
pub const unsafe fn from_ptr_slice(value: &mut [*mut T]) -> &mut [Self] {
core::slice::from_raw_parts_mut(value.as_mut_ptr().cast::<Self>(), value.len())
}
#[must_use]
pub const unsafe fn from_slice(value: &[AtomicPtr<T>]) -> &[Self] {
core::slice::from_raw_parts(value.as_ptr().cast::<Self>(), value.len())
}
#[must_use]
pub fn from_value(value: T) -> Self {
Self(AtomicPtr::new(Box::into_raw(Box::new(value))))
}
#[must_use]
pub fn from_box(value: Box<T>) -> Self {
Self(AtomicPtr::new(Box::into_raw(value)))
}
#[must_use]
pub const fn into_inner(self) -> AtomicPtr<T> {
self.0
}
#[must_use]
pub const fn as_ptr_cell(&self) -> AtomicPtrCell<'_, T> {
AtomicPtrCell::from_internal(&self.0)
}
pub fn is_empty(&self) -> bool {
self.as_ptr_cell().is_empty()
}
pub fn swap(&self, value: T) -> Option<T> {
self.as_ptr_cell().swap(value)
}
pub fn swap_boxed(&self, value: Box<T>) -> Option<Box<T>> {
self.as_ptr_cell().swap_boxed(value)
}
pub fn take(&self) -> Option<T> {
self.take_boxed().map(|v| *v)
}
pub fn take_boxed(&self) -> Option<Box<T>> {
self.as_ptr_cell().take_boxed()
}
pub fn set(&self, value: T) {
self.as_ptr_cell().set(value);
}
pub fn set_boxed(&self, value: Box<T>) {
self.as_ptr_cell().set_boxed(value);
}
pub fn set_if_absent(&self, value: T) -> Option<T> {
self.as_ptr_cell().set_if_absent(value)
}
pub fn set_if_absent_boxed(&self, value: Box<T>) -> Option<Box<T>> {
self.as_ptr_cell().set_if_absent_boxed(value)
}
pub fn set_if_present(&self, value: T) -> PresentResult<T> {
self.as_ptr_cell().set_if_present(value)
}
pub fn set_if_present_boxed(&self, value: Box<T>) -> PresentResult<Box<T>> {
self.as_ptr_cell().set_if_present_boxed(value)
}
pub fn borrow(&self) -> Option<BorrowGuard<'_, T>> {
self.as_ptr_cell().borrow()
}
pub fn borrow_or_else(&self, f: impl FnOnce() -> T) -> BorrowGuard<'_, T> {
self.as_ptr_cell().borrow_or_else(f)
}
pub fn borrow_or_else_boxed(&self, f: impl FnOnce() -> Box<T>) -> BorrowGuard<'_, T> {
self.as_ptr_cell().borrow_or_else_boxed(f)
}
pub fn borrow_swap(&self, value: T) -> BorrowSwapResult<T, T> {
self.as_ptr_cell().borrow_swap(value)
}
pub fn borrow_swap_boxed(&self, value: Box<T>) -> BorrowSwapResult<T, Box<T>> {
self.as_ptr_cell().borrow_swap_boxed(value)
}
pub const unsafe fn inner(&self) -> &AtomicPtr<T> {
&self.0
}
pub const unsafe fn inner_mut(&mut self) -> &mut AtomicPtr<T> {
&mut self.0
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct AtomicPtrCell<'a, T: Sync + Send + 'static>(&'a AtomicPtr<T>);
impl<'a, T: Sync + Send + 'static> AtomicPtrCell<'a, T> {
pub const unsafe fn new(value: &'a AtomicPtr<T>) -> Self {
Self(value)
}
pub const unsafe fn from_ptr(value: *mut *mut T) -> Self {
Self(AtomicPtr::from_ptr(value))
}
pub const unsafe fn from_slice<'b>(value: &'b [&'a AtomicPtr<T>]) -> &'b [Self] {
core::slice::from_raw_parts(value.as_ptr().cast::<AtomicPtrCell<T>>(), value.len())
}
pub const unsafe fn from_ptr_slice(value: &[*mut *mut T]) -> &[Self] {
core::slice::from_raw_parts(value.as_ptr().cast::<AtomicPtrCell<T>>(), value.len())
}
pub(crate) const fn from_internal(value: &'a AtomicPtr<T>) -> Self {
Self(value)
}
fn inner_from_raw(value: *mut T) -> Option<Box<T>> {
if value.is_null() {
return None;
}
Some(unsafe { Box::from_raw(value) })
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.load(SeqCst).is_null()
}
#[must_use]
pub fn swap(&self, value: T) -> Option<T> {
self.swap_boxed(Box::new(value)).map(|v| *v)
}
#[must_use]
pub fn swap_boxed(&self, value: Box<T>) -> Option<Box<T>> {
Self::inner_from_raw(self.0.swap(Box::into_raw(value), SeqCst))
}
#[must_use = "use take_boxed() instead if you want to just discard the value as it is just slightly faster."]
pub fn take(&self) -> Option<T> {
self.take_boxed().map(|v| *v)
}
#[allow(clippy::must_use_candidate)]
pub fn take_boxed(&self) -> Option<Box<T>> {
Self::inner_from_raw(self.0.swap(null_mut(), SeqCst))
}
pub fn set(&self, value: T) {
self.set_boxed(Box::new(value));
}
pub fn set_boxed(&self, value: Box<T>) {
_ = self.swap_boxed(value);
}
#[allow(clippy::must_use_candidate)]
pub fn set_if_absent(&self, value: T) -> Option<T> {
self.set_if_absent_boxed(Box::new(value)).map(|v| *v)
}
#[allow(clippy::must_use_candidate)]
pub fn set_if_absent_boxed(&self, value: Box<T>) -> Option<Box<T>> {
let raw = Box::into_raw(value);
if self
.0
.compare_exchange(null_mut(), raw, SeqCst, Ordering::Acquire)
.is_err()
{
return Some(unsafe { Box::from_raw(raw) });
};
None
}
#[allow(clippy::must_use_candidate)]
pub fn set_if_present(&self, value: T) -> PresentResult<T> {
self.set_if_present_boxed(Box::new(value)).map(|v| *v)
}
#[allow(clippy::must_use_candidate)]
pub fn set_if_present_boxed(&self, value: Box<T>) -> PresentResult<Box<T>> {
let raw = Box::into_raw(value);
let mut cur = self.0.load(SeqCst);
loop {
if cur.is_null() {
return PresentResult::CellWasEmpty(unsafe { Box::from_raw(raw) });
}
cur = match self.0.compare_exchange(cur, raw, SeqCst, SeqCst) {
Ok(actual) => {
return PresentResult::CellHadValue(unsafe { Box::from_raw(actual) })
}
Err(actual) => actual,
};
}
}
#[must_use]
pub fn borrow(&self) -> Option<BorrowGuard<'a, T>> {
self.take_boxed().map(|b| BorrowGuard(Some(b), self.0))
}
pub fn borrow_or_else(&self, f: impl FnOnce() -> T) -> BorrowGuard<'a, T> {
self.borrow_or_else_boxed(|| Box::new(f()))
}
pub fn borrow_or_else_boxed(&self, f: impl FnOnce() -> Box<T>) -> BorrowGuard<'a, T> {
self.borrow()
.unwrap_or_else(|| BorrowGuard(Some(f()), self.0))
}
pub fn borrow_swap(&self, value: T) -> BorrowSwapResult<'a, T, T> {
self.borrow_swap_boxed(Box::new(value)).map_err(|v| *v)
}
#[must_use]
pub fn borrow_swap_boxed(&self, value: Box<T>) -> BorrowSwapResult<'a, T, Box<T>> {
match self.set_if_present_boxed(value) {
PresentResult::CellHadValue(value) => {
BorrowSwapResult::BorrowSwapped(BorrowGuard(Some(value), self.0))
}
PresentResult::CellWasEmpty(np) => BorrowSwapResult::CellWasEmpty(np),
}
}
#[must_use]
pub const unsafe fn inner(&self) -> &'a AtomicPtr<T> {
self.0
}
}
pub struct BorrowGuard<'a, T: Sync + Send + 'static>(Option<Box<T>>, &'a AtomicPtr<T>);
impl<T: Sync + Send + 'static> BorrowGuard<'_, T> {
pub fn discard(mut self) {
self.0 = None;
}
pub fn discard_if_absent(mut self) {
_ = AtomicPtrCell::from_internal(self.1)
.set_if_present_boxed(unsafe { self.0.take().unwrap_unchecked() });
self.0 = None;
}
pub fn try_swap(&mut self) -> bool {
let boxed = unsafe { self.0.take().unwrap_unchecked() };
match AtomicPtrCell::from_internal(self.1).set_if_present_boxed(boxed) {
PresentResult::CellHadValue(bx) => {
self.0 = Some(bx);
true
}
PresentResult::CellWasEmpty(bx) => {
self.0 = Some(bx);
false
}
}
}
pub fn force(mut self) {
AtomicPtrCell::from_internal(self.1)
.set_boxed(unsafe { self.0.take().unwrap_unchecked() });
self.0 = None;
}
#[allow(clippy::must_use_candidate)]
pub fn try_drop(mut self) -> Option<Self> {
if let Some(me) = AtomicPtrCell::from_internal(self.1)
.set_if_absent_boxed(unsafe { self.0.take().unwrap_unchecked() })
{
self.0 = Some(me);
return Some(self);
}
None
}
#[must_use]
pub fn into_inner(mut self) -> T {
unsafe { *self.0.take().unwrap_unchecked() }
}
#[must_use]
pub fn into_inner_box(mut self) -> Box<T> {
unsafe { self.0.take().unwrap_unchecked() }
}
}
impl<T: Sync + Send + 'static> Drop for BorrowGuard<'_, T> {
fn drop(&mut self) {
if let Some(boxed) = self.0.take() {
_ = AtomicPtrCell::from_internal(self.1).set_if_absent_boxed(boxed);
}
}
}
impl<T: Sync + Send + 'static> Deref for BorrowGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref().unwrap_unchecked() }
}
}
impl<T: Sync + Send + 'static> DerefMut for BorrowGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut().unwrap_unchecked() }
}
}
impl<T: Sync + Send + Debug + 'static> Debug for BorrowGuard<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("BorrowGuard({:?}, {:?})", self.0, self.1))
}
}
impl<T: Sync + Send + 'static> From<T> for AtomicPointerCell<T> {
fn from(value: T) -> Self {
Self::from_value(value)
}
}
impl<T: Sync + Send + 'static> Default for AtomicPointerCell<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Sync + Send + 'static> From<AtomicPointerCell<T>> for AtomicPtr<T> {
fn from(value: AtomicPointerCell<T>) -> Self {
value.into_inner()
}
}
impl<'a, T: Sync + Send + 'static> From<&'a AtomicPointerCell<T>> for AtomicPtrCell<'a, T> {
fn from(value: &'a AtomicPointerCell<T>) -> Self {
value.as_ptr_cell()
}
}
impl<T: Sync + Send + 'static> Clone for AtomicPtrCell<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Sync + Send + 'static> Copy for AtomicPtrCell<'_, T> {}
}