Repository: tokio-rs/tokio
File: tokio/src/macros/join.rs (Rust)

Code before:
///         more_async_work());
///
///     // do something with the values
/// }
/// ```
#[macro_export]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
macro_rules! join {
    (@ {
        // One `_` for each branch in the `join!` macro. This is not used once
        // normalization is complete.
        ( $($count:tt)* )

        // Normalized join! branches
        $( ( $($skip:tt)* ) $e:expr, )*

    }) => {{
        use $crate::macros::support::{maybe_done, poll_fn, Future, Pin};
        use $crate::macros::support::Poll::{Ready, Pending};

        // Safety: nothing must be moved out of `futures`. This is to satisfy
        // the requirement of `Pin::new_unchecked` called below.
        let mut futures = ( $( maybe_done($e), )* );

        // How many futures were passed to join!.
        const FUTURE_COUNT: u32 = $crate::count!( $($count)* );

        // When poll_fn is polled, start polling the future at this index.
        let mut start_index = 0;

        poll_fn(move |cx| {
            let mut is_pending = false;

            for i in 0..FUTURE_COUNT {
                let turn;

                #[allow(clippy::modulo_one)]
                {
                    turn = (start_index + i) % FUTURE_COUNT
                };

                match turn {
                  $(
                      #[allow(unreachable_code)]
                      $crate::count!( $($skip)* ) => {
                          let ( $($skip,)* fut, .. ) = &mut futures;

                          // Safety: future is stored on the stack above
                          // and never moved.
                          let mut fut = unsafe { Pin::new_unchecked(fut) };


Review comment (bug):
If the `join!` macro previously worked with more than 64 branches, then this will break any such code. Please double check whether the old version had a branch limit.

Code after:
///         more_async_work());
///
///     // do something with the values
/// }
/// ```
#[macro_export]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
macro_rules! join {
    (@ {
        // One `_` for each branch in the `join!` macro. This is not used once
        // normalization is complete.
        ( $($count:tt)* )

        // The expression `0+1+1+ ... +1` equal to the number of branches.
        ( $($total:tt)* )

        // Normalized join! branches
        $( ( $($skip:tt)* ) $e:expr, )*

    }) => {{
        use $crate::macros::support::{maybe_done, poll_fn, Future, Pin};
        use $crate::macros::support::Poll::{Ready, Pending};

        // Safety: nothing must be moved out of `futures`. This is to satisfy
        // the requirement of `Pin::new_unchecked` called below.
        let mut futures = ( $( maybe_done($e), )* );

        // Each time the future created by poll_fn is polled, a different future will be polled first
        // to ensure every future passed to join! gets a chance to make progress even if
        // one of the futures consumes the whole budget.
        //
        // This is number of futures that will be skipped in the first loop
        // iteration the next time.
        let mut skip_next_time: u32 = 0;

        poll_fn(move |cx| {
            const COUNT: u32 = $($total)*;

            let mut is_pending = false;

            let mut to_run = COUNT;

            // The number of futures that will be skipped in the first loop iteration.
            let mut skip = skip_next_time;

            skip_next_time = if skip + 1 == COUNT { 0 } else { skip + 1 };

            // This loop runs twice and the first `skip` futures
            // are not polled in the first iteration.
            loop {
            $(