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))
    }
}