maillon 0.1.0

A concurrent intrusive list for building synchronization primitives.
Documentation
use core::{
    marker::PhantomData, mem, mem::ManuallyDrop, ptr, ptr::NonNull, sync::atomic::Ordering,
};

#[allow(unused_imports)]
use crate::msrv::StrictProvenance;
use crate::{
    loom::{
        AtomicPtrExt,
        sync::atomic::{AtomicPtr, AtomicUsize},
    },
    msrv,
};

pub trait OptionNonNullExt<T> {
    #[allow(clippy::wrong_self_convention)]
    fn as_ptr(self) -> *mut T;
}

impl<T> OptionNonNullExt<T> for Option<NonNull<T>> {
    fn as_ptr(self) -> *mut T {
        self.map_or(ptr::null_mut(), NonNull::as_ptr)
    }
}

pub fn defer(f: impl FnOnce()) -> impl Drop {
    struct Defer<F: FnOnce()>(ManuallyDrop<F>);
    impl<F: FnOnce()> Drop for Defer<F> {
        fn drop(&mut self) {
            unsafe { ManuallyDrop::take(&mut self.0)() };
        }
    }
    Defer(ManuallyDrop::new(f))
}

#[inline]
pub fn abort_on_unwind<R>(f: impl FnOnce() -> R) -> R {
    #[cold]
    #[inline(never)]
    fn panic_on_unwind() -> ! {
        panic!("unwinding is not allowed here");
    }
    let bomb = defer(|| panic_on_unwind());
    let res = f();
    mem::forget(bomb);
    res
}

pub trait AtomicPtrImpl<T>: AtomicPtrExt<T> + Send + Sync {
    fn new(ptr: *mut T) -> Self;
    fn load(&self, order: Ordering) -> *mut T;
    fn store(&self, ptr: *mut T, order: Ordering);
}

impl<T> AtomicPtrImpl<T> for AtomicPtr<T> {
    fn new(ptr: *mut T) -> Self {
        AtomicPtr::new(ptr)
    }
    fn load(&self, order: Ordering) -> *mut T {
        AtomicPtr::load(self, order)
    }
    fn store(&self, ptr: *mut T, order: Ordering) {
        AtomicPtr::store(self, ptr, order);
    }
}

pub struct ExposedAtomicPtr<T>(AtomicUsize, PhantomData<fn() -> *mut T>);

impl<T> ExposedAtomicPtr<T> {
    #[allow(clippy::declare_interior_mutable_const)]
    #[cfg(not(loom))]
    pub const NULL: Self = Self(AtomicUsize::new(0), PhantomData);
}

impl<T> AtomicPtrImpl<T> for ExposedAtomicPtr<T> {
    #[allow(clippy::incompatible_msrv, unstable_name_collisions)]
    fn new(ptr: *mut T) -> Self {
        Self(AtomicUsize::new(ptr.addr()), PhantomData)
    }
    fn load(&self, order: Ordering) -> *mut T {
        msrv::ptr::with_exposed_provenance_mut(self.0.load(order))
    }
    #[allow(clippy::incompatible_msrv, unstable_name_collisions)]
    fn store(&self, ptr: *mut T, order: Ordering) {
        self.0.store(ptr.addr(), order);
    }
}

impl<T> AtomicPtrExt<T> for ExposedAtomicPtr<T> {
    fn load_mut(&mut self) -> *mut T {
        #[cfg(not(loom))]
        let addr = *self.0.get_mut();
        #[cfg(loom)]
        let addr = self.0.with_mut(|addr| *addr);
        msrv::ptr::with_exposed_provenance_mut(addr)
    }
    #[allow(clippy::incompatible_msrv, unstable_name_collisions)]
    fn store_mut(&mut self, ptr: *mut T) {
        #[cfg(not(loom))]
        let () = *self.0.get_mut() = ptr.addr();
        #[cfg(loom)]
        self.0.with_mut(|addr| *addr = ptr.addr());
    }
}