anvil/tasks/
block_listener.rs1use crate::shutdown::Shutdown;
4use futures::{FutureExt, Stream, StreamExt};
5use std::{
6 pin::Pin,
7 task::{Context, Poll},
8};
9
10pub struct BlockListener<St, F, Fut> {
15 stream: St,
16 task_factory: F,
17 task: Option<Pin<Box<Fut>>>,
18 stream_done: bool,
19 on_shutdown: Shutdown,
20}
21
22impl<St, F, Fut> BlockListener<St, F, Fut>
23where
24 St: Stream,
25 F: Fn(<St as Stream>::Item) -> Fut,
26{
27 pub const fn new(on_shutdown: Shutdown, block_stream: St, task_factory: F) -> Self {
28 Self { stream: block_stream, task_factory, task: None, stream_done: false, on_shutdown }
29 }
30}
31
32impl<St, F, Fut> Future for BlockListener<St, F, Fut>
33where
34 St: Stream + Unpin,
35 F: Fn(<St as Stream>::Item) -> Fut + Unpin + Send + Sync + 'static,
36 Fut: Future<Output = ()> + Send,
37{
38 type Output = ();
39
40 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
41 let pin = self.get_mut();
42
43 if pin.on_shutdown.poll_unpin(cx).is_ready() {
44 return Poll::Ready(());
45 }
46
47 if let Some(mut task) = pin.task.take()
48 && task.poll_unpin(cx).is_pending()
49 {
50 pin.task = Some(task);
51 return Poll::Pending;
52 }
53
54 if pin.stream_done {
55 return Poll::Ready(());
56 }
57
58 let mut block = None;
59 loop {
61 match pin.stream.poll_next_unpin(cx) {
62 Poll::Ready(Some(next_block)) => block = Some(next_block),
63 Poll::Ready(None) => {
64 pin.stream_done = true;
65 break;
66 }
67 Poll::Pending => break,
68 }
69 }
70
71 if let Some(block) = block {
72 pin.task = Some(Box::pin((pin.task_factory)(block)));
73 }
74
75 if let Some(mut task) = pin.task.take()
76 && task.poll_unpin(cx).is_pending()
77 {
78 pin.task = Some(task);
79 }
80
81 if pin.stream_done && pin.task.is_none() { Poll::Ready(()) } else { Poll::Pending }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::shutdown;
89 use futures::{
90 channel::{mpsc, oneshot},
91 task::noop_waker,
92 };
93 use parking_lot::Mutex;
94 use std::sync::Arc;
95
96 #[test]
97 fn waits_for_active_task_before_processing_latest_block() {
98 let (_signal, shutdown) = shutdown::signal();
99 let (tx, rx) = mpsc::unbounded();
100 let (release_tx, release_rx) = oneshot::channel();
101 let release_rx = Arc::new(Mutex::new(Some(release_rx)));
102 let processed = Arc::new(Mutex::new(Vec::new()));
103 let task_release_rx = release_rx;
104 let task_processed = processed.clone();
105 let mut listener = Box::pin(BlockListener::new(shutdown, rx, move |block| {
106 let release_rx = task_release_rx.clone();
107 let processed = task_processed.clone();
108 async move {
109 let release_rx = (block == 1).then(|| release_rx.lock().take().unwrap());
110 if let Some(release_rx) = release_rx {
111 let _ = release_rx.await;
112 }
113 processed.lock().push(block);
114 }
115 }));
116 let waker = noop_waker();
117 let mut cx = Context::from_waker(&waker);
118
119 tx.unbounded_send(1).unwrap();
120 assert!(listener.as_mut().poll(&mut cx).is_pending());
121
122 tx.unbounded_send(2).unwrap();
123 tx.unbounded_send(3).unwrap();
124 assert!(listener.as_mut().poll(&mut cx).is_pending());
125 assert!(processed.lock().is_empty());
126
127 drop(tx);
128 release_tx.send(()).unwrap();
129 assert!(listener.as_mut().poll(&mut cx).is_ready());
130 assert_eq!(*processed.lock(), [1, 3]);
131 }
132}