bombs 0.2.1

Efficient single-producer multi-consumer channel types.
Documentation
use super::{ Fuse, Bomb, Flame, spin, SPIN_ITERATIONS };

/// A type alias for [`MultiFuse<T>`].
pub type MultiFuze<T> = MultiFuse<T>;

/// A reusable bomb.
/// *How did they manufacture this?*
///
/// The [`MultiFuse<T>`] for this `Bomb` can be lit multiple times,
/// to transfer multiple data values sequentially.
/// The `Bomb` remains in an exploded state for one call per
/// data value sent. This means you can only retrieve sent data once,
/// as then the `Bomb` will wait for the next data value. In contrast,
/// a one-time use [`Bomb<T>`] instead repeatedly returns the same one
/// data value for subsequent calls to [`exploded()`] after the [`Fuse`]
/// has been lit.
///
/// Like one-time use [`Bomb`]s, `MultiBomb`s can be safely cloned and sent between threads.
///
/// [`exploded()`]: ./struct.Bomb.html#method.exploded
#[derive(Clone)]
pub struct MultiBomb<T: Clone> {
    inner: Bomb<(MultiBomb<T>, T)>
}

/// A reusable fuse.
/// *It just grows back.*
///
/// This `MultiFuse` can be lit multiple times, and will explode all
/// [`MultiBomb<T>`] instances associated with it.
///
/// It is not necessary, but may be useful for your application, to wait for
/// all of the [`MultiBomb`]s to extinguish before lighting the `MultiFuse` again.
/// Slow [`MultiBomb`]s may be in an exploded state again immediately after
/// they have finished processing data, if the `MultiFuse` was lit again in between processing.
///
/// Like one-time use `Fuse`s, `MultiFuse`s cannot be cloned, but may be moved between threads.
///
/// # Dropping
///
/// Currently, [`MultiBomb`]s do not have internal logic to detect `MultiFuse` drops.
/// If the `MultiFuse` is dropped without any additional logic, all active [`MultiBomb`]s will wait
/// indefinitely to explode.
pub struct MultiFuse<T: Clone> {
    // TODO: Implement proper drop behaviour, read above for further info.
    inner: Fuse<(MultiBomb<T>, T)>
}

impl<T: Clone> MultiBomb<T> {
    /// Creates a new single but reusable producer [`MultiFuse<T>`], and a multi-consumer `MultiBomb<T>`.
    ///
    /// Instances of `MultiBomb<T>` may be safely cloned and sent between threads.
    pub fn new() -> (MultiFuse<T>, MultiBomb<T>) {
        let fuse = MultiFuse::new();

        let bomb = Self {
            inner: Bomb {
                data: fuse.inner.data.clone(),
            }
        };

        (fuse, bomb)
    }

    /// Returns `Some` if the `MultiBomb` has exploded.
    ///
    /// Once the `MultiBomb` has exploded, this function
    /// will return the value only once, and subsequent
    /// calls will check for the next explosion/data value sent.
    ///
    /// # Dropping
    ///
    /// If the `MultiFuse` is dropped, this function will
    /// **always** return `None`, which indistinguishable from
    /// waiting for data. You must implement your own drop
    /// behaviour to avoid this.
    ///
    /// See [`MultiFuse`] for more details.
    pub fn exploded(&mut self) -> Option<T> {
        let (new_bomb, value) = match self.inner.exploded() {
            None => return None,

            // Clone MultiBomb to increment strong counter.
            // Clone data value to properly construct/deconstruct
            // on this thread.
            Some(data) => data.clone()
        };

        self.inner = new_bomb.inner;

        Some(value)
    }

    /// Blocks the current thread and waits to receive data from explosion.
    ///
    /// If the `Bomb` has already exploded, this method returns immediately, and
    /// does not block.
    ///
    /// # Dropping
    ///
    /// If the `MultiFuse` is dropped, this function will
    /// block **indefinitely**, and is indistinguishable from
    /// waiting for data. You must implement your own drop
    /// behaviour to avoid this.
    ///
    /// See [`MultiFuse`] for more details.
    pub fn wait_for_explosion(&mut self) -> T {
        // Spin a couple times to wait and check for explosion.
        // Return the value if it has exploded while spinning.
        if let Some(value) = spin(SPIN_ITERATIONS, || self.exploded()) {
            return value;
        }

        let (new_bomb, value) = self.inner.wait_for_explosion().clone();

        self.inner = new_bomb.inner;

        value
    }
}

impl<T: Clone> MultiFuse<T> {
    fn new() -> Self {
        Self {
            inner: Fuse::new(),
        }
    }

    /// Ignites the fuse.
    ///
    /// Explodes all [`MultiBomb`]s associated to this `MultiFuse`.
    /// Each [`MultiBomb`] receives `value`.
    ///
    /// Alias to [`light`](#method.light)
    pub fn ignite(&mut self, value: T) -> Flame {
        self.light(value)
    }

    /// Lights the fuse.
    ///
    /// Explodes all [`MultiBomb`]s associated to this `MultiFuse`.
    /// Each [`MultiBomb`] receives `value`.
    pub fn light(&mut self, value: T) -> Flame {
        // Create new fuse and bomb.
        let (fuse, bomb) = MultiBomb::new();

        // Replace old fuse with new.
        let old_fuse = std::mem::replace(&mut self.inner, fuse.inner);

        // Light old fuse, passing a new bomb with it.
        old_fuse.light((bomb, value))
    }
}