housekeeping 0.0.3

A concurrent memory reclaimer for periodic cleanups.
Documentation
//! The top-level data structure.

use core::mem::{self, MaybeUninit};

use alloc::sync::Arc;

use crate::{Guard, RawGuard, SimpleBatch, qsbr, stack::Stack};

//----------- Cleanups ---------------------------------------------------------

/// Deferred cleanup actions.
///
/// [`Cleanups`] holds actions for cleaning up resources (e.g. heap allocations)
/// that were shared between threads. When such a resource is removed from
/// global view, it cannot be cleaned up immediately because some threads may
/// still be using it. The cleanup actions for such resources can be deferred
/// into a [`Cleanups`], so they can be safely executed once the resources are
/// guaranteed to be unused.
///
/// Cleanup actions are grouped into _batches_. Users can define custom batch
/// types (which can hold custom cleanup action types) and use them with the
/// generic parameter `B`.
///
/// See [the crate-level documentation](crate) for more information, including
/// examples and a usage guide.
pub struct Cleanups<B = SimpleBatch> {
    /// The QSBR schedule.
    pub(crate) schedule: qsbr::Schedule,

    /// Batches deferred during each phase.
    pub(crate) deferred: [Stack<B>; 3],

    /// Batches re-used during each phase.
    pub(crate) reused: [Stack<B>; 3],

    /// Temporary batch slots during each phase.
    pub(crate) slots: [Stack<MaybeUninit<B>>; 3],
}

impl<B> Cleanups<B> {
    /// Construct a new [`Cleanups`].
    pub fn new() -> Self {
        Self {
            schedule: qsbr::Schedule::new(),
            deferred: Default::default(),
            reused: Default::default(),
            slots: Default::default(),
        }
    }

    /// Register a [`Guard`] against this [`Cleanups`].
    ///
    /// This is a convenience function for [`Guard::new()`].
    ///
    /// ## Panics
    ///
    /// Panics if `usize::MAX / 2` (or more) guards are registered against a
    /// single [`Cleanups`].
    pub fn register(self: Arc<Self>) -> Guard<B> {
        Guard::new(self)
    }

    /// Register a [`RawGuard`] against this [`Cleanups`].
    ///
    /// This is a convenience function for [`RawGuard::new()`].
    ///
    /// ## Panics
    ///
    /// Panics if `usize::MAX / 2` (or more) guards are registered against a
    /// single [`Cleanups`].
    pub fn register_raw(&self) -> RawGuard<B> {
        RawGuard::new(self)
    }

    /// Extract all leftover batches.
    ///
    /// In case batches are left over when the [`Cleanups`] is being dropped,
    /// this method can be used to extract and execute them.
    pub fn take_leftovers(&mut self) -> impl IntoIterator<Item = B> {
        mem::take(&mut self.deferred)
            .into_iter()
            .chain(mem::take(&mut self.reused))
            .flatten()
    }
}

impl<B> Default for Cleanups<B> {
    fn default() -> Self {
        Self::new()
    }
}