Skip to main content

hotshot_task/
dependency.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7use std::future::Future;
8
9use async_broadcast::{Receiver, RecvError};
10use futures::{
11    FutureExt,
12    future::BoxFuture,
13    stream::{FuturesUnordered, StreamExt},
14};
15
16/// Type which describes the idea of waiting for a dependency to complete
17pub trait Dependency<T> {
18    /// Complete will wait until it gets some value `T` then return the value
19    fn completed(self) -> impl Future<Output = Option<T>> + Send;
20    /// Create an or dependency from this dependency and another
21    fn or<D: Dependency<T> + Send + 'static>(self, dep: D) -> OrDependency<T>
22    where
23        T: Send + Sync + Clone + 'static,
24        Self: Sized + Send + 'static,
25    {
26        let mut or = OrDependency::from_deps(vec![self]);
27        or.add_dep(dep);
28        or
29    }
30    /// Create an and dependency from this dependency and another
31    fn and<D: Dependency<T> + Send + 'static>(self, dep: D) -> AndDependency<T>
32    where
33        T: Send + Sync + Clone + 'static,
34        Self: Sized + Send + 'static,
35    {
36        let mut and = AndDependency::from_deps(vec![self]);
37        and.add_dep(dep);
38        and
39    }
40}
41
42/// Defines a dependency that completes when all of its deps complete
43pub struct AndDependency<T> {
44    /// Dependencies being combined
45    deps: Vec<BoxFuture<'static, Option<T>>>,
46}
47impl<T: Clone + Send + Sync> Dependency<Vec<T>> for AndDependency<T> {
48    /// Returns a vector of all of the results from it's dependencies.
49    /// The results will be in a random order
50    async fn completed(self) -> Option<Vec<T>> {
51        let futures = FuturesUnordered::from_iter(self.deps);
52        futures
53            .collect::<Vec<Option<T>>>()
54            .await
55            .into_iter()
56            .collect()
57    }
58}
59
60impl<T: Clone + Send + Sync + 'static> AndDependency<T> {
61    /// Create from a vec of deps
62    #[must_use]
63    pub fn from_deps(deps: Vec<impl Dependency<T> + Send + 'static>) -> Self {
64        let mut pinned = vec![];
65        for dep in deps {
66            pinned.push(dep.completed().boxed());
67        }
68        Self { deps: pinned }
69    }
70    /// Add another dependency
71    pub fn add_dep(&mut self, dep: impl Dependency<T> + Send + 'static) {
72        self.deps.push(dep.completed().boxed());
73    }
74    /// Add multiple dependencies
75    pub fn add_deps(&mut self, deps: AndDependency<T>) {
76        for dep in deps.deps {
77            self.deps.push(dep);
78        }
79    }
80}
81
82/// Defines a dependency that completes when one of it's dependencies completes
83pub struct OrDependency<T> {
84    /// Dependencies being combined
85    deps: Vec<BoxFuture<'static, Option<T>>>,
86}
87impl<T: Clone + Send + Sync> Dependency<T> for OrDependency<T> {
88    /// Returns the value of the first completed dependency
89    async fn completed(self) -> Option<T> {
90        let mut futures = FuturesUnordered::from_iter(self.deps);
91        loop {
92            let maybe = futures.next().await?;
93            if maybe.is_some() {
94                return maybe;
95            }
96        }
97    }
98}
99
100impl<T: Clone + Send + Sync + 'static> OrDependency<T> {
101    /// Creat an `OrDependency` from a vec of dependencies
102    #[must_use]
103    pub fn from_deps(deps: Vec<impl Dependency<T> + Send + 'static>) -> Self {
104        let mut pinned = vec![];
105        for dep in deps {
106            pinned.push(dep.completed().boxed());
107        }
108        Self { deps: pinned }
109    }
110    /// Add another dependency
111    pub fn add_dep(&mut self, dep: impl Dependency<T> + Send + 'static) {
112        self.deps.push(dep.completed().boxed());
113    }
114}
115
116/// A dependency that listens on a channel for an event
117/// that matches what some value it wants.
118pub struct EventDependency<T: Clone + Send + Sync> {
119    /// Channel of incoming events
120    pub(crate) event_rx: Receiver<T>,
121
122    /// Closure which returns true if the incoming `T` is the
123    /// thing that completes this dependency
124    pub(crate) match_fn: Box<dyn Fn(&T) -> bool + Send>,
125
126    /// The potentially externally completed dependency. If the dependency was seeded from an event
127    /// message, we can mark it as already done in lieu of other events still pending.
128    completed_dependency: Option<T>,
129
130    cancel_receiver: Receiver<()>,
131
132    dependency_name: String,
133}
134
135impl<T: Clone + Send + Sync + 'static> EventDependency<T> {
136    /// Create a new `EventDependency`
137    #[must_use]
138    pub fn new(
139        receiver: Receiver<T>,
140        cancel_receiver: Receiver<()>,
141        dependency_name: String,
142        match_fn: Box<dyn Fn(&T) -> bool + Send>,
143    ) -> Self {
144        Self {
145            event_rx: receiver,
146            match_fn: Box::new(match_fn),
147            completed_dependency: None,
148            cancel_receiver,
149            dependency_name,
150        }
151    }
152
153    /// Mark a dependency as completed.
154    pub fn mark_as_completed(&mut self, dependency: T) {
155        self.completed_dependency = Some(dependency);
156    }
157}
158
159impl<T: Clone + Send + Sync + 'static> Dependency<T> for EventDependency<T> {
160    async fn completed(mut self) -> Option<T> {
161        if let Some(dependency) = self.completed_dependency {
162            return Some(dependency);
163        }
164        loop {
165            if let Some(dependency) = self.completed_dependency {
166                return Some(dependency);
167            }
168
169            tokio::select! {
170                recv_event = self.event_rx.recv() => {
171                    match recv_event {
172                        Ok(event) => {
173                            if (self.match_fn)(&event) {
174                                return Some(event);
175                            }
176                        },
177                        Err(RecvError::Overflowed(n)) => {
178                            tracing::error!("Dependency Task overloaded, skipping {} events", n);
179                        },
180                        Err(RecvError::Closed) => {
181                            return None;
182                        },
183                    }
184                }
185                _ = self.cancel_receiver.recv() => {
186                   tracing::warn!("{} dependency cancelled", self.dependency_name);
187                   return None;
188                }
189            }
190        }
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use async_broadcast::{Receiver, broadcast};
197
198    use super::{AndDependency, Dependency, EventDependency, OrDependency};
199
200    fn eq_dep(
201        rx: Receiver<usize>,
202        cancel_rx: Receiver<()>,
203        dep_name: String,
204        val: usize,
205    ) -> EventDependency<usize> {
206        EventDependency {
207            event_rx: rx,
208            match_fn: Box::new(move |v| *v == val),
209            completed_dependency: None,
210            dependency_name: dep_name,
211            cancel_receiver: cancel_rx,
212        }
213    }
214
215    #[tokio::test(flavor = "multi_thread")]
216    async fn it_works() {
217        let (tx, rx) = broadcast(10);
218        let (_cancel_tx, cancel_rx) = broadcast(1);
219
220        let mut deps = vec![];
221        for i in 0..5 {
222            tx.broadcast(i).await.unwrap();
223            deps.push(eq_dep(
224                rx.clone(),
225                cancel_rx.clone(),
226                format!("it_works {i}"),
227                5,
228            ));
229        }
230
231        let and = AndDependency::from_deps(deps);
232        tx.broadcast(5).await.unwrap();
233        let result = and.completed().await;
234        assert_eq!(result, Some(vec![5; 5]));
235    }
236
237    #[tokio::test(flavor = "multi_thread")]
238    async fn or_dep() {
239        let (tx, rx) = broadcast(10);
240        let (_cancel_tx, cancel_rx) = broadcast(1);
241
242        tx.broadcast(5).await.unwrap();
243        let mut deps = vec![];
244        for i in 0..5 {
245            deps.push(eq_dep(
246                rx.clone(),
247                cancel_rx.clone(),
248                format!("or_dep {i}"),
249                5,
250            ));
251        }
252        let or = OrDependency::from_deps(deps);
253        let result = or.completed().await;
254        assert_eq!(result, Some(5));
255    }
256
257    #[tokio::test(flavor = "multi_thread")]
258    async fn and_or_dep() {
259        let (tx, rx) = broadcast(10);
260        let (_cancel_tx, cancel_rx) = broadcast(1);
261
262        tx.broadcast(1).await.unwrap();
263        tx.broadcast(2).await.unwrap();
264        tx.broadcast(3).await.unwrap();
265        tx.broadcast(5).await.unwrap();
266        tx.broadcast(6).await.unwrap();
267
268        let or1 = OrDependency::from_deps(
269            [
270                eq_dep(
271                    rx.clone(),
272                    cancel_rx.clone(),
273                    format!("and_or_dep or1 {}", 4),
274                    4,
275                ),
276                eq_dep(
277                    rx.clone(),
278                    cancel_rx.clone(),
279                    format!("and_or_dep or1 {}", 6),
280                    6,
281                ),
282            ]
283            .into(),
284        );
285        let or2 = OrDependency::from_deps(
286            [
287                eq_dep(
288                    rx.clone(),
289                    cancel_rx.clone(),
290                    format!("and_or_dep or2 {}", 4),
291                    4,
292                ),
293                eq_dep(
294                    rx.clone(),
295                    cancel_rx.clone(),
296                    format!("and_or_dep or2 {}", 5),
297                    5,
298                ),
299            ]
300            .into(),
301        );
302        let and = AndDependency::from_deps([or1, or2].into());
303        let result = and.completed().await;
304        assert_eq!(result, Some(vec![6, 5]));
305    }
306
307    #[tokio::test(flavor = "multi_thread")]
308    async fn or_and_dep() {
309        let (tx, rx) = broadcast(10);
310        let (_cancel_tx, cancel_rx) = broadcast(1);
311
312        tx.broadcast(1).await.unwrap();
313        tx.broadcast(2).await.unwrap();
314        tx.broadcast(3).await.unwrap();
315        tx.broadcast(4).await.unwrap();
316        tx.broadcast(5).await.unwrap();
317
318        let and1 = eq_dep(
319            rx.clone(),
320            cancel_rx.clone(),
321            format!("or_and_dep and1 {}", 4),
322            4,
323        )
324        .and(eq_dep(
325            rx.clone(),
326            cancel_rx.clone(),
327            format!("or_and_dep and1 {}", 6),
328            6,
329        ));
330        let and2 = eq_dep(
331            rx.clone(),
332            cancel_rx.clone(),
333            format!("or_and_dep and2 {}", 4),
334            4,
335        )
336        .and(eq_dep(
337            rx.clone(),
338            cancel_rx.clone(),
339            format!("or_and_dep and2 {}", 5),
340            5,
341        ));
342        let or = and1.or(and2);
343        let result = or.completed().await;
344        assert_eq!(result, Some(vec![4, 5]));
345    }
346
347    #[tokio::test(flavor = "multi_thread")]
348    async fn many_and_dep() {
349        let (tx, rx) = broadcast(10);
350        let (_cancel_tx, cancel_rx) = broadcast(1);
351
352        tx.broadcast(1).await.unwrap();
353        tx.broadcast(2).await.unwrap();
354        tx.broadcast(3).await.unwrap();
355        tx.broadcast(4).await.unwrap();
356        tx.broadcast(5).await.unwrap();
357        tx.broadcast(6).await.unwrap();
358
359        let mut and1 = eq_dep(
360            rx.clone(),
361            cancel_rx.clone(),
362            format!("many_and_dep and1 {}", 4),
363            4,
364        )
365        .and(eq_dep(
366            rx.clone(),
367            cancel_rx.clone(),
368            format!("many_and_dep and1 {}", 6),
369            6,
370        ));
371        let and2 = eq_dep(
372            rx.clone(),
373            cancel_rx.clone(),
374            format!("many_and_dep and2 {}", 4),
375            4,
376        )
377        .and(eq_dep(
378            rx.clone(),
379            cancel_rx.clone(),
380            format!("many_and_dep and2 {}", 5),
381            5,
382        ));
383        and1.add_deps(and2);
384        let result = and1.completed().await;
385        assert_eq!(result, Some(vec![4, 6, 4, 5]));
386    }
387
388    #[tokio::test(flavor = "multi_thread")]
389    async fn cancel_event_dep() {
390        let (tx, rx) = broadcast(10);
391        let (cancel_tx, cancel_rx) = broadcast(1);
392
393        for i in 0..=5 {
394            tx.broadcast(i).await.unwrap();
395        }
396        cancel_tx.broadcast(()).await.unwrap();
397        let dep = eq_dep(
398            rx.clone(),
399            cancel_rx.clone(),
400            format!("cancel_event_dep {}", 6),
401            6,
402        );
403        let result = dep.completed().await;
404        assert_eq!(result, None);
405    }
406
407    #[tokio::test(flavor = "multi_thread")]
408    async fn drop_cancel_dep() {
409        let (tx, rx) = broadcast(10);
410        let (cancel_tx, cancel_rx) = broadcast(1);
411
412        for i in 0..=5 {
413            tx.broadcast(i).await.unwrap();
414        }
415        drop(cancel_tx);
416        let dep = eq_dep(
417            rx.clone(),
418            cancel_rx.clone(),
419            format!("drop_cancel_dep {}", 6),
420            6,
421        );
422        let result = dep.completed().await;
423        assert_eq!(result, None);
424    }
425
426    #[tokio::test(flavor = "multi_thread")]
427    async fn cancel_and_dep() {
428        let (tx, rx) = broadcast(10);
429        let (cancel_tx, cancel_rx) = broadcast(1);
430
431        let mut deps = vec![];
432        for i in 0..=5 {
433            tx.broadcast(i).await.unwrap();
434            deps.push(eq_dep(
435                rx.clone(),
436                cancel_rx.clone(),
437                format!("cancel_and_dep {i}"),
438                i,
439            ))
440        }
441        deps.push(eq_dep(
442            rx.clone(),
443            cancel_rx.clone(),
444            format!("cancel_and_dep {}", 6),
445            6,
446        ));
447        cancel_tx.broadcast(()).await.unwrap();
448        let result = AndDependency::from_deps(deps).completed().await;
449        assert_eq!(result, None);
450    }
451
452    #[tokio::test(flavor = "multi_thread")]
453    async fn cancel_or_dep() {
454        let (_, rx) = broadcast(10);
455        let (cancel_tx, cancel_rx) = broadcast(1);
456
457        let mut deps = vec![];
458        for i in 0..=5 {
459            deps.push(eq_dep(
460                rx.clone(),
461                cancel_rx.clone(),
462                format!("cancel_event_dep {i}"),
463                i,
464            ))
465        }
466        cancel_tx.broadcast(()).await.unwrap();
467        let result = OrDependency::from_deps(deps).completed().await;
468        assert_eq!(result, None);
469    }
470}