Skip to main content

nuclei/macros/
join.rs

1//! `join!` and `try_join!`: wait on several futures at once, in the task
2//! that awaits them, and give their outputs as one tuple.
3//!
4//! Each future sits pinned in a `MaybeDone` in one tuple; every poll polls
5//! the unfinished ones, starting one later each time so a future that is
6//! always ready cannot starve the ones after it (or in the order written
7//! after `biased;`).
8
9/// Waits on every future given, all at once, and gives their outputs as a
10/// tuple in the order written.
11///
12/// The futures run concurrently in the task that awaits `join!`, not in
13/// tasks of their own: [`spawn`](crate::spawn) them for parallelism. Each
14/// argument is an [`IntoFuture`](core::future::IntoFuture). Each poll polls
15/// the unfinished futures, starting one later each time; after `biased;`
16/// they are polled in the order written. `join!()` is `()`.
17///
18/// # Examples
19///
20/// ```
21/// use nuclei::sync::oneshot;
22///
23/// # nuclei::block_on(async {
24/// let (tx, rx) = oneshot::channel::<u32>();
25/// // Each needs the other: only a concurrent wait finishes.
26/// let (sent, got) = nuclei::join!(async { tx.send(7) }, rx);
27/// assert!(sent.is_ok());
28/// assert_eq!(got.unwrap(), 7);
29/// # });
30/// ```
31#[macro_export]
32macro_rules! join {
33    () => {
34        ()
35    };
36    (biased; $($future:expr),+ $(,)?) => {
37        $crate::__join!(@munch join [biased] [] [] $($future,)+)
38    };
39    ($($future:expr),+ $(,)?) => {
40        $crate::__join!(@munch join [fair] [] [] $($future,)+)
41    };
42}
43
44/// Waits on every future given, all at once, each giving a `Result` with
45/// the same error type, and gives `Ok` with their outputs as a tuple in the
46/// order written, or the first error.
47///
48/// On the first `Err`, `try_join!` gives it at once and drops the other
49/// futures. Otherwise it runs as [`join!`](crate::join) does: concurrently,
50/// in the awaiting task, polled from one later each time or in order after
51/// `biased;`. `try_join!()` is `Ok(())`.
52///
53/// # Examples
54///
55/// ```
56/// # nuclei::block_on(async {
57/// let ok: Result<(u8, u16), &str> = nuclei::try_join!(async { Ok(1) }, async { Ok(2) });
58/// assert_eq!(ok, Ok((1, 2)));
59///
60/// let failed: Result<(u8, ()), &str> =
61///     nuclei::try_join!(async { Err("first") }, std::future::pending::<Result<(), &str>>());
62/// assert_eq!(failed, Err("first"));
63/// # });
64/// ```
65#[macro_export]
66macro_rules! try_join {
67    () => {
68        ::core::result::Result::Ok(())
69    };
70    (biased; $($future:expr),+ $(,)?) => {
71        $crate::__join!(@munch try_join [biased] [] [] $($future,)+)
72    };
73    ($($future:expr),+ $(,)?) => {
74        $crate::__join!(@munch try_join [fair] [] [] $($future,)+)
75    };
76}
77
78/// The muncher of [`join!`] and [`try_join!`]: gives each future its `_`
79/// list (one `_` per future before it), then expands.
80#[doc(hidden)]
81#[macro_export]
82macro_rules! __join {
83    (@munch $kind:ident $mode:tt [$($done:tt)*] [$($all:tt)*] $future:expr, $($rest:tt)*) => {
84        $crate::__join!(@munch $kind $mode [$($done)* ([$($all)*] $future)] [$($all)* _] $($rest)*)
85    };
86    (@munch $kind:ident [$mode:ident] [$( ([$($skip:tt)*] $future:expr) )+] [$($all:tt)*]) => {{
87        let mut __futures = ($(
88            ::core::pin::pin!($crate::macros::support::MaybeDone::new(
89                ::core::future::IntoFuture::into_future($future),
90            )),
91        )+);
92        let mut __next: u32 = 0;
93        $crate::macros::support::poll_fn(|__cx: &mut ::core::task::Context<'_>| {
94            if !$crate::macros::support::proceed(__cx) {
95                return ::core::task::Poll::Pending;
96            }
97            let __count: u32 = $crate::__count!($($all)*);
98            let __start: u32 = $crate::__join!(@start $mode __next);
99            __next = (__start + 1) % __count;
100            let mut __done = true;
101            for __turn in 0..__count {
102                let __index = (__start + __turn) % __count;
103                $(
104                    if __index == $crate::__count!($($skip)*) {
105                        let ($($skip,)* __future, ..) = &mut __futures;
106                        if $crate::macros::support::MaybeDone::poll_done(::core::pin::Pin::as_mut(__future), __cx) {
107                            $crate::__join!(@check $kind __future);
108                        } else {
109                            __done = false;
110                        }
111                        continue;
112                    }
113                )+
114            }
115            if !__done {
116                return ::core::task::Poll::Pending;
117            }
118            ::core::task::Poll::Ready($crate::__join!(@output $kind ($({
119                let ($($skip,)* __future, ..) = &mut __futures;
120                match $crate::macros::support::MaybeDone::take_output(::core::pin::Pin::as_mut(__future)) {
121                    ::core::option::Option::Some(__output) => __output,
122                    ::core::option::Option::None => ::core::unreachable!("a joined output taken twice"),
123                }
124            },)+)))
125        })
126        .await
127    }};
128
129    (@start biased $next:ident) => {
130        0
131    };
132    (@start fair $next:ident) => {
133        $next
134    };
135
136    // `join!` waits for every output; `try_join!` ends at the first error.
137    (@check join $future:ident) => {};
138    (@check try_join $future:ident) => {
139        if let ::core::option::Option::Some(::core::result::Result::Err(_)) =
140            $crate::macros::support::MaybeDone::output_mut(::core::pin::Pin::as_mut($future))
141        {
142            if let ::core::option::Option::Some(::core::result::Result::Err(__error)) =
143                $crate::macros::support::MaybeDone::take_output(::core::pin::Pin::as_mut($future))
144            {
145                return ::core::task::Poll::Ready(::core::result::Result::Err(__error));
146            }
147        }
148    };
149
150    (@output join ($($output:expr,)+)) => {
151        ($($output,)+)
152    };
153    (@output try_join ($($output:expr,)+)) => {
154        ::core::result::Result::Ok(($(
155            match $output {
156                ::core::result::Result::Ok(__value) => __value,
157                ::core::result::Result::Err(_) => ::core::unreachable!("a `try_join!` error not given at once"),
158            },
159        )+))
160    };
161}