Skip to main content

tor_async_utils/
oneshot_broadcast.rs

1//! A oneshot broadcast channel.
2//!
3//! The motivation for this channel type was to allow multiple
4//! receivers to either wait for something to finish,
5//! or to have an inexpensive method of checking if it has finished.
6//!
7//! See [`channel()`].
8
9use std::future::{Future, IntoFuture};
10use std::ops::Drop;
11use std::pin::Pin;
12use std::sync::{Arc, Mutex, OnceLock, Weak};
13use std::task::{Context, Poll, Waker, ready};
14
15use slotmap_careful::DenseSlotMap;
16
17slotmap_careful::new_key_type! { struct WakerKey; }
18
19/// A [oneshot broadcast][crate::oneshot_broadcast] sender.
20#[derive(Debug)]
21pub struct Sender<T> {
22    /// State shared with all [`Receiver`]s.
23    shared: Weak<Shared<T>>,
24}
25
26/// A [oneshot broadcast][crate::oneshot_broadcast] receiver.
27///
28/// The `Receiver` offers two methods for receiving the message:
29///
30/// 1. [`Receiver::into_future`]
31///     ```rust
32///     # use tor_async_utils::oneshot_broadcast::{channel, SenderDropped};
33///     # async fn x() -> Result<(), SenderDropped> {
34///     let (tx, rx) = channel();
35///     tx.send(0);
36///     let message: u32 = rx.await.unwrap();
37///     # Ok(())
38///     # }
39///     ```
40///
41/// 2. [`Receiver::borrowed`]
42///     ```rust
43///     # use tor_async_utils::oneshot_broadcast::{channel, SenderDropped};
44///     # async fn x() -> Result<(), SenderDropped> {
45///     let (tx, rx) = channel();
46///     tx.send(0);
47///     let message: &u32 = rx.borrowed().await.unwrap();
48///     # Ok(())
49///     # }
50///     ```
51#[derive(Clone, Debug)]
52pub struct Receiver<T> {
53    /// State shared with the sender and all other receivers.
54    shared: Arc<Shared<T>>,
55}
56
57/// State shared between the sender and receivers.
58/// Correctness:
59///
60/// Sending a message:
61///  - set the message OnceLock (A)
62///  - acquire the wakers Mutex
63///  - take all wakers (B)
64///  - release the wakers Mutex (C)
65///  - wake all wakers
66///
67/// Polling:
68///  - if message was set, return it (fast path)
69///  - acquire the wakers Mutex (D)
70///  - if message was set, return it (E)
71///  - add waker (F)
72///  - release the wakers Mutex
73///
74/// When the wakers Mutex is released at (C), a release-store operation is performed by the Mutex,
75/// which means that the message set at (A) will be seen by all future acquire-load operations by
76/// that same Mutex. More specifically, after (C) has occurred and when the same mutex is acquired at
77/// (D), the message set at (A) is guaranteed to be visible at (E). This means that after the wakers
78/// are taken at (B), no future wakers will be added at (F) and no waker will be "lost".
79#[derive(Debug)]
80struct Shared<T> {
81    /// The message sent from the [`Sender`] to the [`Receiver`]s.
82    msg: OnceLock<Result<T, SenderDropped>>,
83    /// The wakers waiting for a value to be sent.
84    /// Will be set to `Err` after the wakers have been woken.
85    // the `Result` isn't technically needed here,
86    // but we use it to help detect bugs;
87    // see `WakersAlreadyWoken` for details
88    wakers: Mutex<Result<DenseSlotMap<WakerKey, Waker>, WakersAlreadyWoken>>,
89}
90
91/// The future from [`Receiver::borrowed`].
92///
93/// Will be ready, yielding `&'a T`,
94/// when the sender sends a message or is dropped.
95#[derive(Debug)]
96pub struct BorrowedReceiverFuture<'a, T> {
97    /// State shared with the sender and all other receivers.
98    shared: &'a Shared<T>,
99    /// The key for any waker that we've added to [`Shared::wakers`].
100    waker_key: Option<WakerKey>,
101}
102
103/// The future from [`Receiver::into_future`].
104///
105/// Will be ready, yielding a clone of `T`,
106/// when the sender sends a message or is dropped.
107// Both `ReceiverFuture` and `BorrowedReceiverFuture` have similar fields
108// but there's no nice way to deduplicated them.
109// It would have been nice if we could store a `BorrowedReceiverFuture`
110// holding a reference to our `Arc<Shared>`,
111// but that would be a self-referential struct,
112// so we need to duplicate the fields here instead.
113#[derive(Debug)]
114pub struct ReceiverFuture<T> {
115    /// State shared with the sender and all other receivers.
116    shared: Arc<Shared<T>>,
117    /// The key for any waker that we've added to [`Shared::wakers`].
118    waker_key: Option<WakerKey>,
119}
120
121/// The wakers have already been woken.
122///
123/// This is used to help detect if we're trying to access the wakers after they've already been
124/// woken, which likely indicates a bug. For example, it is a bug if a receiver attempts to add a
125/// waker after the sender has already sent its message and woken the wakers, since the new waker
126/// would never be woken.
127#[derive(Copy, Clone, Debug)]
128struct WakersAlreadyWoken;
129
130/// The message has already been set, and we can't set it again.
131#[derive(Copy, Clone, Debug, thiserror::Error)]
132#[error("the message was already set")]
133struct MessageAlreadySet;
134
135/// The sender was dropped, so the channel is closed.
136#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
137#[error("the sender was dropped")]
138#[allow(clippy::exhaustive_structs)]
139pub struct SenderDropped;
140
141/// All the receivers were dropped, so the channel is closed.
142#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
143#[error("all the receivers were dropped")]
144#[allow(clippy::exhaustive_structs)]
145pub struct AllReceiversDropped;
146
147/// Create a new oneshot broadcast channel.
148///
149/// ```rust
150/// # use tor_async_utils::oneshot_broadcast::{channel, SenderDropped};
151/// # async fn x() -> Result<(), SenderDropped> {
152/// let (tx, rx) = channel();
153/// let rx_clone = rx.clone();
154/// tx.send(0_u8);
155/// assert_eq!(rx.await, Ok(0));
156/// assert_eq!(rx_clone.await, Ok(0));
157/// # Ok(())
158/// # }
159/// ```
160pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
161    let shared = Arc::new(Shared {
162        msg: OnceLock::new(),
163        wakers: Mutex::new(Ok(DenseSlotMap::with_key())),
164    });
165
166    let sender = Sender {
167        shared: Arc::downgrade(&shared),
168    };
169
170    let receiver = Receiver { shared };
171
172    (sender, receiver)
173}
174
175impl<T> Sender<T> {
176    /// Send the message to the [`Receiver`]s.
177    ///
178    /// The message may be lost if all receivers have been dropped.
179    pub fn send(self, msg: T) {
180        // set the message and inform the wakers
181        Self::send_and_wake(&self.shared, Ok(msg))
182            // this 'send()` method takes an owned self,
183            // and we don't send a message outside of here and the drop handler,
184            // so this shouldn't be possible
185            .expect("could not set the message");
186    }
187
188    /// Send the message, and wake and clear all wakers.
189    ///
190    /// If all receivers have been dropped, then always returns `Ok`.
191    ///
192    /// If the message was unable to be set, returns `Err(MessageAlreadySet)`.
193    fn send_and_wake(
194        shared: &Weak<Shared<T>>,
195        msg: Result<T, SenderDropped>,
196    ) -> Result<(), MessageAlreadySet> {
197        // Even if the `Weak` upgrade is successful,
198        // it's possible that the last receiver
199        // will be dropped during this `send_and_wake` method,
200        // in which case we will be holding the last `Arc`.
201        let Some(shared) = shared.upgrade() else {
202            // all receivers have dropped; nothing to do
203            return Ok(());
204        };
205
206        // set the message
207        shared.msg.set(msg).or(Err(MessageAlreadySet))?;
208
209        let mut wakers = {
210            let mut wakers = shared.wakers.lock().expect("poisoned");
211            // Take the wakers and drop the mutex guard, releasing the lock.
212            //
213            // We could just drain the wakers map in-place here, but instead we replace the map with
214            // an explicit `WakersAlreadyWoken` state to help catch bugs if something tries adding a
215            // new waker later after we've already woken the wakers.
216            //
217            // The above `msg.set()` will only ever succeed once,
218            // which means that we should only end up here once.
219            std::mem::replace(&mut *wakers, Err(WakersAlreadyWoken))
220                .expect("wakers were taken more than once")
221        };
222
223        // Once we drop the mutex guard, which does a release-store on its own atomic, any other
224        // code which later acquires the wakers mutex is guaranteed to see the msg as "set".
225        // See comments on `Shared`.
226
227        // Wake while not holding the lock.
228        // Since the lock is used in `ReceiverFuture::poll` and `ReceiverFuture::drop` and
229        // should not block for long periods of time,
230        // we'd prefer not to run third-party waker code here while holding the mutex,
231        // even if `wake` should typically be fast.
232        for (_key, waker) in wakers.drain() {
233            waker.wake();
234        }
235
236        Ok(())
237    }
238
239    /// Returns `true` if all [`Receiver`]s (and all futures created from the receivers) have been
240    /// dropped.
241    ///
242    /// This can be useful to skip doing extra work to generate the message if the message will be
243    /// discarded anyways.
244    // This is for external use.
245    // It is not always valid to call this internally.
246    // For example when we've done a `Weak::upgrade` internally, like in `send_and_wake`,
247    // this won't return the correct value.
248    pub fn is_cancelled(&self) -> bool {
249        self.shared.strong_count() == 0
250    }
251
252    /// Subscribe to this channel, creating a new receiver.
253    ///
254    /// Returns an error if all the existing [`Receiver`]s (and all futures created
255    /// from the receivers) have already been dropped.
256    pub fn subscribe(&self) -> Result<Receiver<T>, AllReceiversDropped> {
257        Ok(Receiver {
258            shared: self.shared.upgrade().ok_or(AllReceiversDropped)?,
259        })
260    }
261}
262
263impl<T> Drop for Sender<T> {
264    fn drop(&mut self) {
265        // set an error message to indicate that the sender was dropped and inform the wakers;
266        // it's fine if setting the message fails since it might have been set previously during a
267        // `send()`
268        let _ = Self::send_and_wake(&self.shared, Err(SenderDropped));
269    }
270}
271
272impl<T> Receiver<T> {
273    /// Receive a borrowed message from the [`Sender`].
274    ///
275    /// This may be more efficient than [`Receiver::into_future`]
276    /// and doesn't require `T: Clone`.
277    ///
278    /// This is cancellation-safe.
279    pub fn borrowed(&self) -> BorrowedReceiverFuture<'_, T> {
280        BorrowedReceiverFuture {
281            shared: &self.shared,
282            waker_key: None,
283        }
284    }
285
286    /// The receiver is ready.
287    ///
288    /// If `true`, the [`Sender`] has either sent its message or been dropped.
289    pub fn is_ready(&self) -> bool {
290        self.shared.msg.get().is_some()
291    }
292}
293
294impl<T: Clone> IntoFuture for Receiver<T> {
295    type Output = Result<T, SenderDropped>;
296    type IntoFuture = ReceiverFuture<T>;
297
298    /// This future is cancellation-safe.
299    fn into_future(self) -> Self::IntoFuture {
300        ReceiverFuture {
301            shared: self.shared,
302            waker_key: None,
303        }
304    }
305}
306
307impl<'a, T> Future for BorrowedReceiverFuture<'a, T> {
308    type Output = Result<&'a T, SenderDropped>;
309
310    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
311        let self_ = self.get_mut();
312        receiver_fut_poll(self_.shared, &mut self_.waker_key, cx.waker())
313    }
314}
315
316impl<T> Drop for BorrowedReceiverFuture<'_, T> {
317    fn drop(&mut self) {
318        receiver_fut_drop(self.shared, &mut self.waker_key);
319    }
320}
321
322impl<T: Clone> Future for ReceiverFuture<T> {
323    type Output = Result<T, SenderDropped>;
324
325    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
326        let self_ = self.get_mut();
327        let poll = receiver_fut_poll(&self_.shared, &mut self_.waker_key, cx.waker());
328        Poll::Ready(ready!(poll)).map_ok(Clone::clone)
329    }
330}
331
332impl<T> Drop for ReceiverFuture<T> {
333    fn drop(&mut self) {
334        receiver_fut_drop(&self.shared, &mut self.waker_key);
335    }
336}
337
338/// The shared poll implementation for receiver futures.
339fn receiver_fut_poll<'a, T>(
340    shared: &'a Shared<T>,
341    waker_key: &mut Option<WakerKey>,
342    new_waker: &Waker,
343) -> Poll<Result<&'a T, SenderDropped>> {
344    // if the message was already set, return it
345    if let Some(msg) = shared.msg.get() {
346        return Poll::Ready(msg.as_ref().or(Err(SenderDropped)));
347    }
348
349    let mut wakers = shared.wakers.lock().expect("poisoned");
350
351    // check again now that we've acquired the mutex
352    if let Some(msg) = shared.msg.get() {
353        return Poll::Ready(msg.as_ref().or(Err(SenderDropped)));
354    }
355
356    // we have acquired the wakers mutex and checked that the message wasn't set,
357    // so we know that wakers have not yet been woken
358    // and it's okay to add our waker to the wakers map
359    let wakers = wakers.as_mut().expect("wakers were already woken");
360
361    match waker_key {
362        // we have added a waker previously
363        Some(waker_key) => {
364            // replace the old entry
365            let waker = wakers
366                .get_mut(*waker_key)
367                // the waker is only removed from the map by our drop handler,
368                // so the waker should never be missing
369                .expect("waker key is missing from map");
370            waker.clone_from(new_waker);
371        }
372        // we have never added a waker
373        None => {
374            // add a new entry
375            let new_key = wakers.insert(new_waker.clone());
376            *waker_key = Some(new_key);
377        }
378    }
379
380    Poll::Pending
381}
382
383/// The shared drop implementation for receiver futures.
384fn receiver_fut_drop<T>(shared: &Shared<T>, waker_key: &mut Option<WakerKey>) {
385    if let Some(waker_key) = waker_key.take() {
386        let mut wakers = shared.wakers.lock().expect("poisoned");
387        if let Ok(wakers) = wakers.as_mut() {
388            let waker = wakers.remove(waker_key);
389            // this is the only place that removes the waker from the map,
390            // so the waker should never be missing
391            debug_assert!(waker.is_some(), "the waker key was not found");
392        }
393    }
394}
395
396#[cfg(test)]
397mod test {
398    #![allow(clippy::unwrap_used)]
399
400    use super::*;
401
402    use futures::future::FutureExt;
403    use tor_rtcompat::SpawnExt;
404
405    impl<T> Shared<T> {
406        /// Count the number of wakers.
407        fn count_wakers(&self) -> usize {
408            self.wakers
409                .lock()
410                .expect("poisoned")
411                .as_ref()
412                .map(|x| x.len())
413                .unwrap_or(0)
414        }
415    }
416
417    #[test]
418    fn standard_usage() {
419        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
420            let (tx, rx) = channel();
421            tx.send(0_u8);
422            assert_eq!(rx.borrowed().await, Ok(&0));
423
424            let (tx, rx) = channel();
425            tx.send(0_u8);
426            assert_eq!(rx.await, Ok(0));
427        });
428    }
429
430    #[test]
431    fn immediate_drop() {
432        let _ = channel::<()>();
433
434        let (tx, rx) = channel::<()>();
435        drop(tx);
436        drop(rx);
437
438        let (tx, rx) = channel::<()>();
439        drop(rx);
440        drop(tx);
441    }
442
443    #[test]
444    fn drop_sender() {
445        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
446            let (tx, rx_1) = channel::<u8>();
447
448            let rx_2 = rx_1.clone();
449            drop(tx);
450            let rx_3 = rx_1.clone();
451            assert_eq!(rx_1.borrowed().await, Err(SenderDropped));
452            assert_eq!(rx_2.borrowed().await, Err(SenderDropped));
453            assert_eq!(rx_3.borrowed().await, Err(SenderDropped));
454        });
455    }
456
457    #[test]
458    fn clone_before_send() {
459        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
460            let (tx, rx_1) = channel();
461
462            let rx_2 = rx_1.clone();
463            tx.send(0_u8);
464            assert_eq!(rx_1.borrowed().await, Ok(&0));
465            assert_eq!(rx_2.borrowed().await, Ok(&0));
466        });
467    }
468
469    #[test]
470    fn clone_after_send() {
471        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
472            let (tx, rx_1) = channel();
473
474            tx.send(0_u8);
475            let rx_2 = rx_1.clone();
476            assert_eq!(rx_1.borrowed().await, Ok(&0));
477            assert_eq!(rx_2.borrowed().await, Ok(&0));
478        });
479    }
480
481    #[test]
482    fn clone_after_borrowed() {
483        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
484            let (tx, rx_1) = channel();
485
486            tx.send(0_u8);
487            assert_eq!(rx_1.borrowed().await, Ok(&0));
488            let rx_2 = rx_1.clone();
489            assert_eq!(rx_2.borrowed().await, Ok(&0));
490        });
491    }
492
493    #[test]
494    fn drop_one_receiver() {
495        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
496            let (tx, rx_1) = channel();
497
498            let rx_2 = rx_1.clone();
499            drop(rx_1);
500            tx.send(0_u8);
501            assert_eq!(rx_2.borrowed().await, Ok(&0));
502        });
503    }
504
505    #[test]
506    fn drop_all_receivers() {
507        let (tx, rx_1) = channel();
508
509        let rx_2 = rx_1.clone();
510        drop(rx_1);
511        drop(rx_2);
512        tx.send(0_u8);
513    }
514
515    #[test]
516    fn drop_fut() {
517        let (_tx, rx) = channel::<u8>();
518        let fut = rx.borrowed();
519        assert_eq!(rx.shared.count_wakers(), 0);
520        drop(fut);
521        assert_eq!(rx.shared.count_wakers(), 0);
522
523        // drop after sending
524        let (tx, rx) = channel();
525        tx.send(0_u8);
526        let fut = rx.borrowed();
527        assert_eq!(rx.shared.count_wakers(), 0);
528        drop(fut);
529        assert_eq!(rx.shared.count_wakers(), 0);
530
531        // drop after polling once
532        let (_tx, rx) = channel::<u8>();
533        let mut fut = Box::pin(rx.borrowed());
534        assert_eq!(rx.shared.count_wakers(), 0);
535        assert_eq!(fut.as_mut().now_or_never(), None);
536        assert_eq!(rx.shared.count_wakers(), 1);
537        drop(fut);
538        assert_eq!(rx.shared.count_wakers(), 0);
539
540        // drop after polling once and send
541        let (tx, rx) = channel();
542        let mut fut = Box::pin(rx.borrowed());
543        assert_eq!(rx.shared.count_wakers(), 0);
544        assert_eq!(fut.as_mut().now_or_never(), None);
545        assert_eq!(rx.shared.count_wakers(), 1);
546        tx.send(0_u8);
547        assert_eq!(rx.shared.count_wakers(), 0);
548        drop(fut);
549    }
550
551    #[test]
552    fn drop_owned_fut() {
553        let (_tx, rx) = channel::<u8>();
554        let fut = rx.clone().into_future();
555        assert_eq!(rx.shared.count_wakers(), 0);
556        drop(fut);
557        assert_eq!(rx.shared.count_wakers(), 0);
558
559        // drop after sending
560        let (tx, rx) = channel();
561        tx.send(0_u8);
562        let fut = rx.clone().into_future();
563        assert_eq!(rx.shared.count_wakers(), 0);
564        drop(fut);
565        assert_eq!(rx.shared.count_wakers(), 0);
566
567        // drop after polling once
568        let (_tx, rx) = channel::<u8>();
569        let mut fut = Box::pin(rx.clone().into_future());
570        assert_eq!(rx.shared.count_wakers(), 0);
571        assert_eq!(fut.as_mut().now_or_never(), None);
572        assert_eq!(rx.shared.count_wakers(), 1);
573        drop(fut);
574        assert_eq!(rx.shared.count_wakers(), 0);
575
576        // drop after polling once and send
577        let (tx, rx) = channel();
578        let mut fut = Box::pin(rx.clone().into_future());
579        assert_eq!(rx.shared.count_wakers(), 0);
580        assert_eq!(fut.as_mut().now_or_never(), None);
581        assert_eq!(rx.shared.count_wakers(), 1);
582        tx.send(0_u8);
583        assert_eq!(rx.shared.count_wakers(), 0);
584        drop(fut);
585    }
586
587    #[test]
588    fn is_ready_after_send() {
589        let (tx, rx_1) = channel();
590        assert!(!rx_1.is_ready());
591        let rx_2 = rx_1.clone();
592        assert!(!rx_2.is_ready());
593
594        tx.send(0_u8);
595
596        assert!(rx_1.is_ready());
597        assert!(rx_2.is_ready());
598
599        let rx_3 = rx_1.clone();
600        assert!(rx_3.is_ready());
601    }
602
603    #[test]
604    fn is_ready_after_drop() {
605        let (tx, rx_1) = channel::<u8>();
606        assert!(!rx_1.is_ready());
607        let rx_2 = rx_1.clone();
608        assert!(!rx_2.is_ready());
609
610        drop(tx);
611
612        assert!(rx_1.is_ready());
613        assert!(rx_2.is_ready());
614
615        let rx_3 = rx_1.clone();
616        assert!(rx_3.is_ready());
617    }
618
619    #[test]
620    fn is_cancelled() {
621        let (tx, rx) = channel::<u8>();
622        assert!(!tx.is_cancelled());
623        drop(rx);
624        assert!(tx.is_cancelled());
625
626        let (tx, rx_1) = channel::<u8>();
627        assert!(!tx.is_cancelled());
628        let rx_2 = rx_1.clone();
629        drop(rx_1);
630        assert!(!tx.is_cancelled());
631        drop(rx_2);
632        assert!(tx.is_cancelled());
633    }
634
635    #[test]
636    fn recv_in_task() {
637        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
638            let (tx, rx) = channel();
639
640            let join = rt
641                .spawn_with_handle(async move {
642                    assert_eq!(rx.borrowed().await, Ok(&0));
643                    assert_eq!(rx.await, Ok(0));
644                })
645                .unwrap();
646
647            tx.send(0_u8);
648
649            join.await;
650        });
651    }
652
653    #[test]
654    fn recv_multiple_in_task() {
655        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
656            let (tx, rx) = channel();
657            let rx_1 = rx.clone();
658            let rx_2 = rx.clone();
659
660            let join_1 = rt
661                .spawn_with_handle(async move {
662                    assert_eq!(rx_1.borrowed().await, Ok(&0));
663                })
664                .unwrap();
665            let join_2 = rt
666                .spawn_with_handle(async move {
667                    assert_eq!(rx_2.await, Ok(0));
668                })
669                .unwrap();
670
671            tx.send(0_u8);
672
673            join_1.await;
674            join_2.await;
675            assert_eq!(rx.borrowed().await, Ok(&0));
676        });
677    }
678
679    #[test]
680    fn recv_multiple_times() {
681        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
682            let (tx, rx) = channel();
683            let rx_subscribed = tx.subscribe().unwrap();
684
685            tx.send(0_u8);
686            assert_eq!(rx.borrowed().await, Ok(&0));
687            assert_eq!(rx.borrowed().await, Ok(&0));
688            assert_eq!(rx.clone().await, Ok(0));
689            assert_eq!(rx.await, Ok(0));
690            assert_eq!(rx_subscribed.await, Ok(0));
691        });
692    }
693
694    #[test]
695    fn stress() {
696        // In general we don't have control over the runtime and where/when tasks are scheduled,
697        // so we try as best as possible to send the message while simultaneously creating new
698        // receivers and waiting on them.
699        // It's possible this might be entirely ineffective since we don't enforce any specific
700        // scheduler behaviour here,
701        // but in the worst case it's still a test with multiple receivers on different tasks,
702        // so is useful to have.
703        //
704        // The `test_with_various` helper uses `MockExecutor` with two different deterministic
705        // scheduling policies.
706        // At least at the time of writing,
707        // when this test uses `MockExecutor` with its "queue" scheduling policy
708        // the "send" occurs after 20 of the tasks have begun waiting.
709        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
710            let (tx, rx) = channel();
711
712            rt.spawn(async move {
713                // this tries to delay the send a little bit
714                // to give time for some of the receiver tasks to start
715                for _ in 0..20 {
716                    tor_rtcompat::task::yield_now().await;
717                }
718                tx.send(0_u8);
719            })
720            .unwrap();
721
722            let mut joins = vec![];
723            for _ in 0..100 {
724                let rx_clone = rx.clone();
725                let join = rt
726                    .spawn_with_handle(async move { rx_clone.borrowed().await.cloned() })
727                    .unwrap();
728                joins.push(join);
729                // allows the send task to make progress if single-threaded
730                tor_rtcompat::task::yield_now().await;
731            }
732
733            for join in joins {
734                assert!(matches!(join.await, Ok(0)));
735            }
736        });
737    }
738}