1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
use crate::types::sync::Arc;
use crate::{ Fuse, Bomb, Flame };
/// 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>) {
#[cfg(not(loom))] // Weak not implemented in loom.
let _counter = Arc::new(());
let fuse = MultiFuse::new(#[cfg(not(loom))] &_counter);
let bomb = Self {
inner: Bomb {
data: fuse.inner.data.clone(),
#[cfg(not(loom))] // Weak not implemented in loom.
_counter,
}
};
(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)
}
}
impl<T: Clone> MultiFuse<T> {
fn new(#[cfg(not(loom))] _counter: &Arc<()>) -> Self {
Self {
inner: Fuse::new(#[cfg(not(loom))] _counter),
}
}
/// 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))
}
}