mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
macros: fix the hygiene issue of join! and try_join! (#7638)
This commit is contained in:
@@ -113,7 +113,7 @@ doc! {macro_rules! join {
|
|||||||
(@ {
|
(@ {
|
||||||
// Type of rotator that controls which inner future to start with
|
// Type of rotator that controls which inner future to start with
|
||||||
// when polling our output future.
|
// when polling our output future.
|
||||||
rotator=$rotator:ty;
|
rotator_select=$rotator_select:ty;
|
||||||
|
|
||||||
// One `_` for each branch in the `join!` macro. This is not used once
|
// One `_` for each branch in the `join!` macro. This is not used once
|
||||||
// normalization is complete.
|
// normalization is complete.
|
||||||
@@ -126,7 +126,7 @@ doc! {macro_rules! join {
|
|||||||
$( ( $($skip:tt)* ) $e:expr, )*
|
$( ( $($skip:tt)* ) $e:expr, )*
|
||||||
|
|
||||||
}) => {{
|
}) => {{
|
||||||
use $crate::macros::support::{maybe_done, poll_fn, Future, Pin};
|
use $crate::macros::support::{maybe_done, poll_fn, Future, Pin, RotatorSelect};
|
||||||
use $crate::macros::support::Poll::{Ready, Pending};
|
use $crate::macros::support::Poll::{Ready, Pending};
|
||||||
|
|
||||||
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
||||||
@@ -143,14 +143,14 @@ doc! {macro_rules! join {
|
|||||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||||
let mut futures = &mut futures;
|
let mut futures = &mut futures;
|
||||||
|
|
||||||
const COUNT: u32 = $($total)*;
|
|
||||||
|
|
||||||
// Each time the future created by poll_fn is polled, if not using biased mode,
|
// Each time the future created by poll_fn is polled, if not using biased mode,
|
||||||
// a different future is polled first to ensure every future passed to join!
|
// a different future is polled first to ensure every future passed to join!
|
||||||
// can make progress even if one of the futures consumes the whole budget.
|
// can make progress even if one of the futures consumes the whole budget.
|
||||||
let mut rotator = <$rotator>::default();
|
let mut rotator = <$rotator_select as RotatorSelect>::Rotator::<{$($total)*}>::default();
|
||||||
|
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| {
|
||||||
|
const COUNT: u32 = $($total)*;
|
||||||
|
|
||||||
let mut is_pending = false;
|
let mut is_pending = false;
|
||||||
let mut to_run = COUNT;
|
let mut to_run = COUNT;
|
||||||
|
|
||||||
@@ -205,17 +205,17 @@ doc! {macro_rules! join {
|
|||||||
|
|
||||||
// ===== Normalize =====
|
// ===== Normalize =====
|
||||||
|
|
||||||
(@ { rotator=$rotator:ty; ( $($s:tt)* ) ( $($n:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
(@ { rotator_select=$rotator_select:ty; ( $($s:tt)* ) ( $($n:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
||||||
$crate::join!(@{ rotator=$rotator; ($($s)* _) ($($n)* + 1) $($t)* ($($s)*) $e, } $($r)*)
|
$crate::join!(@{ rotator_select=$rotator_select; ($($s)* _) ($($n)* + 1) $($t)* ($($s)*) $e, } $($r)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== Entry point =====
|
// ===== Entry point =====
|
||||||
( biased; $($e:expr),+ $(,)?) => {
|
( biased; $($e:expr),+ $(,)?) => {
|
||||||
$crate::join!(@{ rotator=$crate::macros::support::BiasedRotator; () (0) } $($e,)*)
|
$crate::join!(@{ rotator_select=$crate::macros::support::SelectBiased; () (0) } $($e,)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
( $($e:expr),+ $(,)?) => {
|
( $($e:expr),+ $(,)?) => {
|
||||||
$crate::join!(@{ rotator=$crate::macros::support::Rotator<COUNT>; () (0) } $($e,)*)
|
$crate::join!(@{ rotator_select=$crate::macros::support::SelectNormal; () (0) } $($e,)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
(biased;) => { async {}.await };
|
(biased;) => { async {}.await };
|
||||||
@@ -223,6 +223,30 @@ doc! {macro_rules! join {
|
|||||||
() => { async {}.await }
|
() => { async {}.await }
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
/// Helper trait to select which type of `Rotator` to use.
|
||||||
|
// We need this to allow specifying a const generic without
|
||||||
|
// colliding with caller const names due to macro hygiene.
|
||||||
|
pub trait RotatorSelect {
|
||||||
|
type Rotator<const COUNT: u32>: Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marker type indicating that the starting branch should
|
||||||
|
/// rotate each poll.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SelectNormal;
|
||||||
|
/// Marker type indicating that the starting branch should
|
||||||
|
/// be the first declared branch each poll.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SelectBiased;
|
||||||
|
|
||||||
|
impl RotatorSelect for SelectNormal {
|
||||||
|
type Rotator<const COUNT: u32> = Rotator<COUNT>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RotatorSelect for SelectBiased {
|
||||||
|
type Rotator<const COUNT: u32> = BiasedRotator;
|
||||||
|
}
|
||||||
|
|
||||||
/// Rotates by one each [`Self::num_skip`] call up to COUNT - 1.
|
/// Rotates by one each [`Self::num_skip`] call up to COUNT - 1.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Rotator<const COUNT: u32> {
|
pub struct Rotator<const COUNT: u32> {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ cfg_macros! {
|
|||||||
|
|
||||||
pub use std::future::poll_fn;
|
pub use std::future::poll_fn;
|
||||||
|
|
||||||
pub use crate::macros::join::{BiasedRotator, Rotator};
|
pub use crate::macros::join::{BiasedRotator, Rotator, RotatorSelect, SelectNormal, SelectBiased};
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn thread_rng_n(n: u32) -> u32 {
|
pub fn thread_rng_n(n: u32) -> u32 {
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ doc! {macro_rules! try_join {
|
|||||||
(@ {
|
(@ {
|
||||||
// Type of rotator that controls which inner future to start with
|
// Type of rotator that controls which inner future to start with
|
||||||
// when polling our output future.
|
// when polling our output future.
|
||||||
rotator=$rotator:ty;
|
rotator_select=$rotator_select:ty;
|
||||||
|
|
||||||
// One `_` for each branch in the `try_join!` macro. This is not used once
|
// One `_` for each branch in the `try_join!` macro. This is not used once
|
||||||
// normalization is complete.
|
// normalization is complete.
|
||||||
@@ -179,7 +179,7 @@ doc! {macro_rules! try_join {
|
|||||||
$( ( $($skip:tt)* ) $e:expr, )*
|
$( ( $($skip:tt)* ) $e:expr, )*
|
||||||
|
|
||||||
}) => {{
|
}) => {{
|
||||||
use $crate::macros::support::{maybe_done, poll_fn, Future, Pin};
|
use $crate::macros::support::{maybe_done, poll_fn, Future, Pin, RotatorSelect};
|
||||||
use $crate::macros::support::Poll::{Ready, Pending};
|
use $crate::macros::support::Poll::{Ready, Pending};
|
||||||
|
|
||||||
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
||||||
@@ -196,14 +196,14 @@ doc! {macro_rules! try_join {
|
|||||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||||
let mut futures = &mut futures;
|
let mut futures = &mut futures;
|
||||||
|
|
||||||
const COUNT: u32 = $($total)*;
|
|
||||||
|
|
||||||
// Each time the future created by poll_fn is polled, if not using biased mode,
|
// Each time the future created by poll_fn is polled, if not using biased mode,
|
||||||
// a different future is polled first to ensure every future passed to try_join!
|
// a different future is polled first to ensure every future passed to try_join!
|
||||||
// can make progress even if one of the futures consumes the whole budget.
|
// can make progress even if one of the futures consumes the whole budget.
|
||||||
let mut rotator = <$rotator>::default();
|
let mut rotator = <$rotator_select as RotatorSelect>::Rotator::<{$($total)*}>::default();
|
||||||
|
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| {
|
||||||
|
const COUNT: u32 = $($total)*;
|
||||||
|
|
||||||
let mut is_pending = false;
|
let mut is_pending = false;
|
||||||
let mut to_run = COUNT;
|
let mut to_run = COUNT;
|
||||||
|
|
||||||
@@ -264,17 +264,17 @@ doc! {macro_rules! try_join {
|
|||||||
|
|
||||||
// ===== Normalize =====
|
// ===== Normalize =====
|
||||||
|
|
||||||
(@ { rotator=$rotator:ty; ( $($s:tt)* ) ( $($n:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
(@ { rotator_select=$rotator_select:ty; ( $($s:tt)* ) ( $($n:tt)* ) $($t:tt)* } $e:expr, $($r:tt)* ) => {
|
||||||
$crate::try_join!(@{ rotator=$rotator; ($($s)* _) ($($n)* + 1) $($t)* ($($s)*) $e, } $($r)*)
|
$crate::try_join!(@{ rotator_select=$rotator_select; ($($s)* _) ($($n)* + 1) $($t)* ($($s)*) $e, } $($r)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== Entry point =====
|
// ===== Entry point =====
|
||||||
( biased; $($e:expr),+ $(,)?) => {
|
( biased; $($e:expr),+ $(,)?) => {
|
||||||
$crate::try_join!(@{ rotator=$crate::macros::support::BiasedRotator; () (0) } $($e,)*)
|
$crate::try_join!(@{ rotator_select=$crate::macros::support::SelectBiased; () (0) } $($e,)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
( $($e:expr),+ $(,)?) => {
|
( $($e:expr),+ $(,)?) => {
|
||||||
$crate::try_join!(@{ rotator=$crate::macros::support::Rotator<COUNT>; () (0) } $($e,)*)
|
$crate::try_join!(@{ rotator_select=$crate::macros::support::SelectNormal; () (0) } $($e,)*)
|
||||||
};
|
};
|
||||||
|
|
||||||
(biased;) => { async { Ok(()) }.await };
|
(biased;) => { async { Ok(()) }.await };
|
||||||
|
|||||||
@@ -234,3 +234,23 @@ async fn join_into_future() {
|
|||||||
|
|
||||||
tokio::join!(NotAFuture);
|
tokio::join!(NotAFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for: https://github.com/tokio-rs/tokio/issues/7637
|
||||||
|
// We want to make sure that the `const COUNT: u32` declaration
|
||||||
|
// inside the macro body doesn't leak to the caller to cause compiler failures
|
||||||
|
// or variable shadowing.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn caller_names_const_count() {
|
||||||
|
let (tx, rx) = oneshot::channel::<u32>();
|
||||||
|
|
||||||
|
const COUNT: u32 = 2;
|
||||||
|
|
||||||
|
let mut join = task::spawn(async { tokio::join!(async { tx.send(COUNT).unwrap() }) });
|
||||||
|
assert_ready!(join.poll());
|
||||||
|
|
||||||
|
let res = rx.await.unwrap();
|
||||||
|
|
||||||
|
// This passing demonstrates that the const in the macro is
|
||||||
|
// not shadowing the caller-specified COUNT value
|
||||||
|
assert_eq!(2, res);
|
||||||
|
}
|
||||||
|
|||||||
@@ -247,3 +247,23 @@ async fn empty_try_join() {
|
|||||||
assert_eq!(tokio::try_join!() as Result<_, ()>, Ok(()));
|
assert_eq!(tokio::try_join!() as Result<_, ()>, Ok(()));
|
||||||
assert_eq!(tokio::try_join!(biased;) as Result<_, ()>, Ok(()));
|
assert_eq!(tokio::try_join!(biased;) as Result<_, ()>, Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for: https://github.com/tokio-rs/tokio/issues/7637
|
||||||
|
// We want to make sure that the `const COUNT: u32` declaration
|
||||||
|
// inside the macro body doesn't leak to the caller to cause compiler failures
|
||||||
|
// or variable shadowing.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn caller_names_const_count() {
|
||||||
|
let (tx, rx) = oneshot::channel::<u32>();
|
||||||
|
|
||||||
|
const COUNT: u32 = 2;
|
||||||
|
|
||||||
|
let mut try_join = task::spawn(async { tokio::try_join!(async { tx.send(COUNT) }) });
|
||||||
|
assert_ready!(try_join.poll()).unwrap();
|
||||||
|
|
||||||
|
let res = rx.await.unwrap();
|
||||||
|
|
||||||
|
// This passing demonstrates that the const in the macro is
|
||||||
|
// not shadowing the caller-specified COUNT value
|
||||||
|
assert_eq!(2, res);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user