bombs/blocking/multi.rs
1use super::{ Fuse, Bomb, Flame, spin, SPIN_ITERATIONS };
2
3/// A type alias for [`MultiFuse<T>`].
4pub type MultiFuze<T> = MultiFuse<T>;
5
6/// A reusable bomb.
7/// *How did they manufacture this?*
8///
9/// The [`MultiFuse<T>`] for this `Bomb` can be lit multiple times,
10/// to transfer multiple data values sequentially.
11/// The `Bomb` remains in an exploded state for one call per
12/// data value sent. This means you can only retrieve sent data once,
13/// as then the `Bomb` will wait for the next data value. In contrast,
14/// a one-time use [`Bomb<T>`] instead repeatedly returns the same one
15/// data value for subsequent calls to [`exploded()`] after the [`Fuse`]
16/// has been lit.
17///
18/// Like one-time use [`Bomb`]s, `MultiBomb`s can be safely cloned and sent between threads.
19///
20/// [`exploded()`]: ./struct.Bomb.html#method.exploded
21#[derive(Clone)]
22pub struct MultiBomb<T: Clone> {
23 inner: Bomb<(MultiBomb<T>, T)>
24}
25
26/// A reusable fuse.
27/// *It just grows back.*
28///
29/// This `MultiFuse` can be lit multiple times, and will explode all
30/// [`MultiBomb<T>`] instances associated with it.
31///
32/// It is not necessary, but may be useful for your application, to wait for
33/// all of the [`MultiBomb`]s to extinguish before lighting the `MultiFuse` again.
34/// Slow [`MultiBomb`]s may be in an exploded state again immediately after
35/// they have finished processing data, if the `MultiFuse` was lit again in between processing.
36///
37/// Like one-time use `Fuse`s, `MultiFuse`s cannot be cloned, but may be moved between threads.
38///
39/// # Dropping
40///
41/// Currently, [`MultiBomb`]s do not have internal logic to detect `MultiFuse` drops.
42/// If the `MultiFuse` is dropped without any additional logic, all active [`MultiBomb`]s will wait
43/// indefinitely to explode.
44pub struct MultiFuse<T: Clone> {
45 // TODO: Implement proper drop behaviour, read above for further info.
46 inner: Fuse<(MultiBomb<T>, T)>
47}
48
49impl<T: Clone> MultiBomb<T> {
50 /// Creates a new single but reusable producer [`MultiFuse<T>`], and a multi-consumer `MultiBomb<T>`.
51 ///
52 /// Instances of `MultiBomb<T>` may be safely cloned and sent between threads.
53 pub fn new() -> (MultiFuse<T>, MultiBomb<T>) {
54 let fuse = MultiFuse::new();
55
56 let bomb = Self {
57 inner: Bomb {
58 data: fuse.inner.data.clone(),
59 }
60 };
61
62 (fuse, bomb)
63 }
64
65 /// Returns `Some` if the `MultiBomb` has exploded.
66 ///
67 /// Once the `MultiBomb` has exploded, this function
68 /// will return the value only once, and subsequent
69 /// calls will check for the next explosion/data value sent.
70 ///
71 /// # Dropping
72 ///
73 /// If the `MultiFuse` is dropped, this function will
74 /// **always** return `None`, which indistinguishable from
75 /// waiting for data. You must implement your own drop
76 /// behaviour to avoid this.
77 ///
78 /// See [`MultiFuse`] for more details.
79 pub fn exploded(&mut self) -> Option<T> {
80 let (new_bomb, value) = match self.inner.exploded() {
81 None => return None,
82
83 // Clone MultiBomb to increment strong counter.
84 // Clone data value to properly construct/deconstruct
85 // on this thread.
86 Some(data) => data.clone()
87 };
88
89 self.inner = new_bomb.inner;
90
91 Some(value)
92 }
93
94 /// Blocks the current thread and waits to receive data from explosion.
95 ///
96 /// If the `Bomb` has already exploded, this method returns immediately, and
97 /// does not block.
98 ///
99 /// # Dropping
100 ///
101 /// If the `MultiFuse` is dropped, this function will
102 /// block **indefinitely**, and is indistinguishable from
103 /// waiting for data. You must implement your own drop
104 /// behaviour to avoid this.
105 ///
106 /// See [`MultiFuse`] for more details.
107 pub fn wait_for_explosion(&mut self) -> T {
108 // Spin a couple times to wait and check for explosion.
109 // Return the value if it has exploded while spinning.
110 if let Some(value) = spin(SPIN_ITERATIONS, || self.exploded()) {
111 return value;
112 }
113
114 let (new_bomb, value) = self.inner.wait_for_explosion().clone();
115
116 self.inner = new_bomb.inner;
117
118 value
119 }
120}
121
122impl<T: Clone> MultiFuse<T> {
123 fn new() -> Self {
124 Self {
125 inner: Fuse::new(),
126 }
127 }
128
129 /// Ignites the fuse.
130 ///
131 /// Explodes all [`MultiBomb`]s associated to this `MultiFuse`.
132 /// Each [`MultiBomb`] receives `value`.
133 ///
134 /// Alias to [`light`](#method.light)
135 pub fn ignite(&mut self, value: T) -> Flame {
136 self.light(value)
137 }
138
139 /// Lights the fuse.
140 ///
141 /// Explodes all [`MultiBomb`]s associated to this `MultiFuse`.
142 /// Each [`MultiBomb`] receives `value`.
143 pub fn light(&mut self, value: T) -> Flame {
144 // Create new fuse and bomb.
145 let (fuse, bomb) = MultiBomb::new();
146
147 // Replace old fuse with new.
148 let old_fuse = std::mem::replace(&mut self.inner, fuse.inner);
149
150 // Light old fuse, passing a new bomb with it.
151 old_fuse.light((bomb, value))
152 }
153}