soft-cycle 0.2.0

Async controller for coordinating soft restarts and graceful shutdowns with shared listeners
Documentation
//! Atomic payload types and the [`Payload`] trait for use with [`crate::SoftCycleController`].

use std::sync::atomic::{
    AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, AtomicU8,
    AtomicU16, AtomicU32, AtomicU64, AtomicUsize, Ordering,
};

/// Trait for atomic wrapper types used as the storage for controller payloads.
///
/// Types implementing this trait support atomic load and store of an inner primitive;
/// other operations and orderings are not used by the controller. Only payload types
/// that can be represented by such an atomic type are usable with [`SoftCycleController`](crate::SoftCycleController).
/// This trait is unrelated to the `AtomicPrimitive` trait in `std::sync::atomic`.
pub trait AtomicPrimitive: Sync + Send + Sized {
    /// The primitive type that this atomic type is wrapping.
    type InnerPrimitive: Default;

    /// Creates a new atomic wrapper with the given value.
    fn new(value: Self::InnerPrimitive) -> Self;
    /// Loads the value from the atomic wrapper.
    fn load(&self) -> Self::InnerPrimitive;
    /// Stores the value into the atomic wrapper.
    fn store(&self, value: Self::InnerPrimitive);

    /// Creates a new atomic wrapper with the default value of the primitive
    /// type.
    fn new_default() -> Self {
        Self::new(Default::default())
    }
}

macro_rules! impl_atomic_primitive {
    ($atomic:ty, $inner:ty) => {
        impl AtomicPrimitive for $atomic {
            type InnerPrimitive = $inner;

            fn new(value: $inner) -> Self {
                <$atomic>::new(value)
            }

            fn load(&self) -> $inner {
                self.load(Ordering::Relaxed)
            }

            fn store(&self, value: $inner) {
                self.store(value, Ordering::Relaxed)
            }
        }
    };
}

impl_atomic_primitive!(AtomicBool, bool);
impl_atomic_primitive!(AtomicU8, u8);
impl_atomic_primitive!(AtomicI8, i8);
impl_atomic_primitive!(AtomicU16, u16);
impl_atomic_primitive!(AtomicI16, i16);
impl_atomic_primitive!(AtomicU32, u32);
impl_atomic_primitive!(AtomicI32, i32);
impl_atomic_primitive!(AtomicU64, u64);
impl_atomic_primitive!(AtomicI64, i64);
impl_atomic_primitive!(AtomicUsize, usize);
impl_atomic_primitive!(AtomicIsize, isize);

impl<T> AtomicPrimitive for AtomicPtr<T> {
    type InnerPrimitive = *mut T;

    fn new(value: *mut T) -> Self {
        AtomicPtr::new(value)
    }

    fn load(&self) -> *mut T {
        self.load(Ordering::Relaxed)
    }

    fn store(&self, value: *mut T) {
        self.store(value, Ordering::Relaxed)
    }
}

/// Atomic storage for `*const T`. Used as [`Payload::UnderlyingAtomic`] for `*const T`.
#[derive(Debug, Default)]
pub struct AtomicConstPtr<T> {
    p: AtomicPtr<T>,
}

impl<T> AtomicPrimitive for AtomicConstPtr<T> {
    type InnerPrimitive = *const T;

    fn new(value: *const T) -> Self {
        // SAFETY: `p` will never be dereferenced, so casting value to *mut T is
        // safe.
        Self {
            p: AtomicPtr::new(value as *mut T),
        }
    }

    fn load(&self) -> *const T {
        self.p.load(Ordering::Relaxed) as *const T
    }

    fn store(&self, value: *const T) {
        // SAFETY: `p` will never be dereferenced, so casting value to *mut T is
        // safe.
        self.p.store(value as *mut T, Ordering::Relaxed)
    }
}

/// Atomic storage for `()`. Used as [`Payload::UnderlyingAtomic`] for the unit type.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AtomicUnit;

impl AtomicPrimitive for AtomicUnit {
    type InnerPrimitive = ();

    fn new(_: ()) -> Self {
        Self
    }

    fn load(&self) -> Self::InnerPrimitive {}

    fn store(&self, _: Self::InnerPrimitive) {}
}

/// Trait for payload types that can be stored and loaded atomically by the
/// [`SoftCycleController`](crate::SoftCycleController).
///
/// A type implements `Payload` by specifying an [`UnderlyingAtomic`](Payload::UnderlyingAtomic)
/// that implements [`AtomicPrimitive`], and implementing `From`/`Into` for the atomic’s inner
/// type. Only types representable in a single atomic slot (e.g. primitives, pointers) are
/// supported; the controller does not allocate or clone payloads.
pub trait Payload:
    From<<Self::UnderlyingAtomic as AtomicPrimitive>::InnerPrimitive>
    + Into<<Self::UnderlyingAtomic as AtomicPrimitive>::InnerPrimitive>
    + Copy
{
    type UnderlyingAtomic: AtomicPrimitive;
}

macro_rules! impl_payload {
    ($inner:ty, $atomic:ty) => {
        impl Payload for $inner {
            type UnderlyingAtomic = $atomic;
        }
    };
}

impl_payload!((), AtomicUnit);
impl_payload!(bool, AtomicBool);
impl_payload!(u8, AtomicU8);
impl_payload!(i8, AtomicI8);
impl_payload!(u16, AtomicU16);
impl_payload!(i16, AtomicI16);
impl_payload!(u32, AtomicU32);
impl_payload!(i32, AtomicI32);
impl_payload!(u64, AtomicU64);
impl_payload!(i64, AtomicI64);
impl_payload!(usize, AtomicUsize);
impl_payload!(isize, AtomicIsize);

impl<T> Payload for *mut T {
    type UnderlyingAtomic = AtomicPtr<T>;
}

impl<T> Payload for *const T {
    type UnderlyingAtomic = AtomicConstPtr<T>;
}