Skip to main content

maelstrom_util/
sync.rs

1//! Functions that are useful for communicating between tasks and threads within a program.
2use anyhow::Result;
3use pin_project::pin_project;
4use std::{
5    future::Future,
6    num::NonZeroU32,
7    ops::ControlFlow,
8    pin::Pin,
9    sync::{Condvar, Mutex},
10    task::{Context, Poll},
11};
12use tokio::sync::{mpsc::UnboundedReceiver, oneshot};
13
14/// Read messages from a channel, calling an individual function on each one. Return when there are
15/// no more channel senders.
16pub async fn channel_reader<MessageT>(
17    mut channel: UnboundedReceiver<MessageT>,
18    mut processor: impl FnMut(MessageT) -> ControlFlow<Result<()>>,
19) -> Result<()> {
20    while let Some(x) = channel.recv().await {
21        if let ControlFlow::Break(result) = processor(x) {
22            return result;
23        }
24    }
25    Ok(())
26}
27
28// N.B. We'd use the ! type if it were stable.
29enum Never {}
30
31/// An `EventSender` wakes an [`EventReceiver`] when it is dropped. It is okay for the associated
32/// [`EventReceiver`] to be dropped before the `EventSender`.
33pub struct EventSender {
34    _sender: oneshot::Sender<Never>,
35}
36
37/// An `EventReceiver` waits until its associated [`EventSender`] is dropped.
38#[pin_project]
39pub struct EventReceiver(#[pin] oneshot::Receiver<Never>);
40
41impl Future for EventReceiver {
42    type Output = ();
43
44    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
45        self.project().0.poll(cx).map(drop)
46    }
47}
48
49/// Return a connected [`EventSender`] and [`EventReceiver`] pair.
50pub fn event() -> (EventSender, EventReceiver) {
51    let (_sender, receiver) = oneshot::channel();
52    (EventSender { _sender }, EventReceiver(receiver))
53}
54
55#[derive(Default)]
56pub struct Event {
57    completed: std::sync::Mutex<bool>,
58    condvar: std::sync::Condvar,
59}
60
61impl Event {
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    pub fn set(&self) {
67        *self.completed.lock().unwrap() = true;
68        self.condvar.notify_all();
69    }
70
71    pub fn wait(&self) {
72        let _guard = self
73            .condvar
74            .wait_while(self.completed.lock().unwrap(), |completed| !*completed)
75            .unwrap();
76    }
77
78    pub fn wait_and_unset(&self) {
79        let _guard = self
80            .condvar
81            .wait_while(self.completed.lock().unwrap(), |completed| {
82                if *completed {
83                    *completed = false;
84                    false
85                } else {
86                    true
87                }
88            })
89            .unwrap();
90    }
91
92    pub fn wait_timeout(&self, dur: std::time::Duration) -> std::sync::WaitTimeoutResult {
93        let (_guard, result) = self
94            .condvar
95            .wait_timeout_while(self.completed.lock().unwrap(), dur, |completed| !*completed)
96            .unwrap();
97        result
98    }
99
100    pub fn is_set(&self) -> bool {
101        *self.completed.lock().unwrap()
102    }
103}
104
105pub struct Pool<T> {
106    mutex: Mutex<PoolInner<T>>,
107    condvar: Condvar,
108}
109
110struct PoolInner<T> {
111    available_slots: u32,
112    available_items: Vec<T>,
113}
114
115impl<T> Pool<T> {
116    pub fn new(slots: NonZeroU32) -> Self {
117        Self {
118            mutex: Mutex::new(PoolInner {
119                available_slots: slots.into(),
120                available_items: Default::default(),
121            }),
122            condvar: Default::default(),
123        }
124    }
125
126    /// Run call a closure after obtaining a slot, potentially with a pre-built item.
127    ///
128    /// The calling thread will block until there is an available slot. Once there is, the provided
129    /// closure will be called, potentially with an item returned from a previously called closure.
130    /// Upon completion, if the closure returns a new item, the item will be provided to a
131    /// subsequent closure.
132    pub fn call_with_item<U, E>(
133        &self,
134        f: impl FnOnce(Option<T>) -> Result<(T, U), E>,
135    ) -> Result<U, E> {
136        let guard = self.mutex.lock().unwrap();
137        let mut guard = self
138            .condvar
139            .wait_while(guard, |inner| inner.available_slots == 0)
140            .unwrap();
141        guard.available_slots -= 1;
142        let item = guard.available_items.pop();
143        drop(guard);
144
145        let result = f(item);
146
147        let mut guard = self.mutex.lock().unwrap();
148        guard.available_slots += 1;
149        let result = result.map(|(item, ret)| {
150            guard.available_items.push(item);
151            ret
152        });
153        drop(guard);
154
155        self.condvar.notify_one();
156        result
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use anyhow::anyhow;
164    use core::time::Duration;
165    use tokio::{sync::mpsc, task, time};
166
167    #[tokio::test]
168    async fn no_messages() {
169        let (_, rx) = mpsc::unbounded_channel::<u8>();
170        let mut vec = vec![];
171        channel_reader(rx, |s| {
172            vec.push(s);
173            ControlFlow::Continue(())
174        })
175        .await
176        .unwrap();
177        assert!(vec.is_empty(), "{vec:?}");
178    }
179
180    #[tokio::test]
181    async fn one_messages() {
182        let (tx, rx) = mpsc::unbounded_channel();
183        task::spawn(async move { tx.send(1).unwrap() });
184        let mut vec = vec![];
185        channel_reader(rx, |s| {
186            vec.push(s);
187            ControlFlow::Continue(())
188        })
189        .await
190        .unwrap();
191
192        assert_eq!(vec, vec![1]);
193    }
194
195    #[tokio::test]
196    async fn three_messages() {
197        let (tx, rx) = mpsc::unbounded_channel();
198        task::spawn(async move {
199            tx.send(1).unwrap();
200            tx.send(2).unwrap();
201            tx.send(3).unwrap();
202        });
203        let mut vec = vec![];
204        channel_reader(rx, |s| {
205            vec.push(s);
206            ControlFlow::Continue(())
207        })
208        .await
209        .unwrap();
210
211        assert_eq!(vec, vec![1, 2, 3]);
212    }
213
214    #[tokio::test]
215    async fn three_messages_with_break_after_two() {
216        let (tx, rx) = mpsc::unbounded_channel();
217        task::spawn(async move {
218            tx.send(1).unwrap();
219            tx.send(2).unwrap();
220            tx.send(3).unwrap();
221        });
222        let mut vec = vec![];
223        channel_reader(rx, |s| {
224            vec.push(s);
225            if s == 2 {
226                ControlFlow::Break(Ok(()))
227            } else {
228                ControlFlow::Continue(())
229            }
230        })
231        .await
232        .unwrap();
233
234        assert_eq!(vec, vec![1, 2]);
235    }
236
237    #[tokio::test]
238    async fn three_messages_with_break_of_error_after_two() {
239        let (tx, rx) = mpsc::unbounded_channel();
240        task::spawn(async move {
241            tx.send(1).unwrap();
242            tx.send(2).unwrap();
243            tx.send(3).unwrap();
244        });
245        let mut vec = vec![];
246        let err = channel_reader(rx, |s| {
247            vec.push(s);
248            if s == 2 {
249                ControlFlow::Break(Err(anyhow!("test error")))
250            } else {
251                ControlFlow::Continue(())
252            }
253        })
254        .await
255        .unwrap_err();
256        assert_eq!(err.to_string(), "test error");
257
258        assert_eq!(vec, vec![1, 2]);
259    }
260
261    #[tokio::test]
262    async fn basic_event() {
263        let (event_sender, event_receiver) = event();
264        task::spawn(async move {
265            drop(event_sender);
266        });
267        event_receiver.await;
268    }
269
270    #[tokio::test]
271    async fn event_sent_before_received() {
272        let (event_sender, event_receiver) = event();
273        let (channel_sender, channel_receiver) = oneshot::channel();
274        task::spawn(async move {
275            drop(event_sender);
276            channel_sender.send(()).unwrap();
277        });
278        channel_receiver.await.unwrap();
279        event_receiver.await;
280    }
281
282    #[tokio::test]
283    async fn event_sent_after_received() {
284        let (event_sender, event_receiver) = event();
285        let (channel_sender, channel_receiver) = oneshot::channel();
286        task::spawn(async move {
287            channel_receiver.await.unwrap();
288            drop(event_sender);
289        });
290        channel_sender.send(()).unwrap();
291        time::sleep(Duration::from_millis(10)).await;
292        event_receiver.await;
293    }
294
295    #[tokio::test]
296    async fn event_sent_after_receiver_dropped() {
297        let (event_sender, event_receiver) = event();
298        let (channel_sender, channel_receiver) = oneshot::channel();
299        let handle = task::spawn(async move {
300            channel_receiver.await.unwrap();
301            drop(event_sender);
302        });
303        drop(event_receiver);
304        channel_sender.send(()).unwrap();
305        handle.await.unwrap();
306    }
307
308    #[test]
309    fn sync_event_wait() {
310        let event = Event::new();
311
312        std::thread::scope(|scope| {
313            let t1 = scope.spawn(|| event.wait());
314            let t2 = scope.spawn(|| event.wait());
315
316            std::thread::sleep(Duration::from_millis(10));
317            assert!(!t1.is_finished());
318            assert!(!t2.is_finished());
319
320            assert!(!event.is_set());
321            event.set();
322
323            t1.join().unwrap();
324            t2.join().unwrap();
325            assert!(event.is_set());
326        });
327    }
328
329    #[test]
330    fn sync_event_wait_timeout() {
331        let event = Event::new();
332
333        assert!(event.wait_timeout(Duration::from_millis(5)).timed_out());
334        event.set();
335        assert!(!event.wait_timeout(Duration::from_millis(5)).timed_out());
336    }
337
338    #[test]
339    fn sync_event_wait_and_unset() {
340        let event = Event::new();
341
342        std::thread::scope(|scope| {
343            let t1 = scope.spawn(|| event.wait_and_unset());
344            let t2 = scope.spawn(|| event.wait_and_unset());
345
346            event.set();
347            while !t1.is_finished() && !t2.is_finished() {
348                std::thread::sleep(Duration::from_millis(1));
349            }
350            assert!(!event.is_set());
351
352            event.set();
353            t1.join().unwrap();
354            t2.join().unwrap();
355
356            assert!(!event.is_set());
357        });
358    }
359}