/// A helper macro for `select!` to hide the long list of macro patterns from the documentation. /// /// The macro consists of two stages: /// 1. Parsing /// 2. Code generation /// /// The parsing stage consists of these subparts: /// 1. `@list`: Turns a list of tokens into a list of cases. /// 2. `@list_errorN`: Diagnoses the syntax error. /// 3. `@case`: Parses a single case and verifies its argument list. /// /// The codegen stage consists of these subparts: /// 1. `@init`: Attempts to optimize `select!` away and initializes the list of handles. /// 1. `@count`: Counts the listed cases. /// 3. `@add`: Adds send/receive operations to the list of handles and starts selection. /// 4. `@complete`: Completes the selected send/receive operation. /// /// If the parsing stage encounters a syntax error or the codegen stage ends up with too many /// cases to process, the macro fails with a compile-time error. #[doc(hidden)] #[macro_export]
macro_rules! crossbeam_channel_internal { // The list is empty. Now check the arguments of each processed case.
(@list
()
($($head:tt)*)
) => {
$crate::crossbeam_channel_internal!(
@case
($($head)*)
()
()
)
}; // If necessary, insert an empty argument list after `default`.
(@list
(default => $($tail:tt)*)
($($head:tt)*)
) => {
$crate::crossbeam_channel_internal!(
@list
(default() => $($tail)*)
($($head)*)
)
}; // But print an error if `default` is followed by a `->`.
(@list
(default -> $($tail:tt)*)
($($head:tt)*)
) => {
compile_error!( "expected `=>` after `default` case, found `->`"
)
}; // Print an error if there's an `->` after the argument list in the default case.
(@list
(default $args:tt -> $($tail:tt)*)
($($head:tt)*)
) => {
compile_error!( "expected `=>` after `default` case, found `->`"
)
}; // Print an error if there is a missing result in a recv case.
(@list
(recv($($args:tt)*) => $($tail:tt)*)
($($head:tt)*)
) => {
compile_error!( "expected `->` after `recv` case, found `=>`"
)
}; // Print an error if there is a missing result in a send case.
(@list
(send($($args:tt)*) => $($tail:tt)*)
($($head:tt)*)
) => {
compile_error!( "expected `->` after `send` operation, found `=>`"
)
}; // Make sure the arrow and the result are not repeated.
(@list
($case:ident $args:tt -> $res:tt -> $($tail:tt)*)
($($head:tt)*)
) => {
compile_error!("expected `=>`, found `->`")
}; // Print an error if there is a semicolon after the block.
(@list
($case:ident $args:tt $(-> $res:pat)* => $body:block; $($tail:tt)*)
($($head:tt)*)
) => {
compile_error!( "did you mean to put a comma instead of the semicolon after `}`?"
)
}; // The first case is separated by a comma.
(@list
($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr, $($tail:tt)*)
($($head:tt)*)
) => {
$crate::crossbeam_channel_internal!(
@list
($($tail)*)
($($head)* $case ($($args)*) $(-> $res)* => { $body },)
)
}; // Don't require a comma after the case if it has a proper block.
(@list
($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:block $($tail:tt)*)
($($head:tt)*)
) => {
$crate::crossbeam_channel_internal!(
@list
($($tail)*)
($($head)* $case ($($args)*) $(-> $res)* => { $body },)
)
}; // Only one case remains.
(@list
($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr $(,)?)
($($head:tt)*)
) => {
$crate::crossbeam_channel_internal!(
@list
()
($($head)* $case ($($args)*) $(-> $res)* => { $body },)
)
}; // Diagnose and print an error.
(@list
($($tail:tt)*)
($($head:tt)*)
) => {
$crate::crossbeam_channel_internal!(@list_error1 $($tail)*)
}; // Stage 1: check the case type.
(@list_error1 recv $($tail:tt)*) => {
$crate::crossbeam_channel_internal!(@list_error2 recv $($tail)*)
};
(@list_error1 send $($tail:tt)*) => {
$crate::crossbeam_channel_internal!(@list_error2 send $($tail)*)
};
(@list_error1 default $($tail:tt)*) => {
$crate::crossbeam_channel_internal!(@list_error2 default $($tail)*)
};
(@list_error1 $t:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected one of `recv`, `send`, or `default`, found `",
stringify!($t), "`",
)
)
};
(@list_error1 $($tail:tt)*) => {
$crate::crossbeam_channel_internal!(@list_error2 $($tail)*);
}; // Stage 2: check the argument list.
(@list_error2 $case:ident) => {
compile_error!(
concat!( "missing argument list after `",
stringify!($case), "`",
)
)
};
(@list_error2 $case:ident => $($tail:tt)*) => {
compile_error!(
concat!( "missing argument list after `",
stringify!($case), "`",
)
)
};
(@list_error2 $($tail:tt)*) => {
$crate::crossbeam_channel_internal!(@list_error3 $($tail)*)
}; // Stage 3: check the `=>` and what comes after it.
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)*) => {
compile_error!(
concat!( "missing `=>` after `",
stringify!($case), "` case",
)
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* =>) => {
compile_error!( "expected expression after `=>`"
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:expr; $($tail:tt)*) => {
compile_error!(
concat!( "did you mean to put a comma instead of the semicolon after `",
stringify!($body), "`?",
)
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => recv($($a:tt)*) $($tail:tt)*) => {
compile_error!( "expected an expression after `=>`"
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => send($($a:tt)*) $($tail:tt)*) => {
compile_error!( "expected an expression after `=>`"
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => default($($a:tt)*) $($tail:tt)*) => {
compile_error!( "expected an expression after `=>`"
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident($($a:tt)*) $($tail:tt)*) => {
compile_error!(
concat!( "did you mean to put a comma after `",
stringify!($f), "(",
stringify!($($a)*), ")`?",
)
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!($($a:tt)*) $($tail:tt)*) => {
compile_error!(
concat!( "did you mean to put a comma after `",
stringify!($f), "!(",
stringify!($($a)*), ")`?",
)
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident![$($a:tt)*] $($tail:tt)*) => {
compile_error!(
concat!( "did you mean to put a comma after `",
stringify!($f), "![",
stringify!($($a)*), "]`?",
)
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!{$($a:tt)*} $($tail:tt)*) => {
compile_error!(
concat!( "did you mean to put a comma after `",
stringify!($f), "!{",
stringify!($($a)*), "}`?",
)
)
};
(@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:tt $($tail:tt)*) => {
compile_error!(
concat!( "did you mean to put a comma after `",
stringify!($body), "`?",
)
)
};
(@list_error3 $case:ident($($args:tt)*) -> => $($tail:tt)*) => {
compile_error!("missing pattern after `->`")
};
(@list_error3 $case:ident($($args:tt)*) $t:tt $(-> $r:pat)* => $($tail:tt)*) => {
compile_error!(
concat!( "expected `->`, found `",
stringify!($t), "`",
)
)
};
(@list_error3 $case:ident($($args:tt)*) -> $t:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected a pattern, found `",
stringify!($t), "`",
)
)
};
(@list_error3 recv($($args:tt)*) $t:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected `->`, found `",
stringify!($t), "`",
)
)
};
(@list_error3 send($($args:tt)*) $t:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected `->`, found `",
stringify!($t), "`",
)
)
};
(@list_error3 recv $args:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected an argument list after `recv`, found `",
stringify!($args), "`",
)
)
};
(@list_error3 send $args:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected an argument list after `send`, found `",
stringify!($args), "`",
)
)
};
(@list_error3 default $args:tt $($tail:tt)*) => {
compile_error!(
concat!( "expected an argument list or `=>` after `default`, found `",
stringify!($args), "`",
)
)
};
(@list_error3 $($tail:tt)*) => {
$crate::crossbeam_channel_internal!(@list_error4 $($tail)*)
}; // Stage 4: fail with a generic error message.
(@list_error4 $($tail:tt)*) => {
compile_error!("invalid syntax")
};
// Success! All cases were parsed.
(@case
()
$cases:tt
$default:tt
) => {
$crate::crossbeam_channel_internal!(
@init
$cases
$default
)
};
// Check the format of a recv case.
(@case
(recv($r:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*)
($($cases:tt)*)
$default:tt
) => {
$crate::crossbeam_channel_internal!(
@case
($($tail)*)
($($cases)* recv($r) -> $res => $body,)
$default
)
}; // Print an error if the argument list is invalid.
(@case
(recv($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*)
($($cases:tt)*)
$default:tt
) => {
compile_error!(
concat!( "invalid argument list in `recv(",
stringify!($($args)*), ")`",
)
)
}; // Print an error if there is no argument list.
(@case
(recv $t:tt $($tail:tt)*)
($($cases:tt)*)
$default:tt
) => {
compile_error!(
concat!( "expected an argument list after `recv`, found `",
stringify!($t), "`",
)
)
};
// Check the format of a send case.
(@case
(send($s:expr, $m:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*)
($($cases:tt)*)
$default:tt
) => {
$crate::crossbeam_channel_internal!(
@case
($($tail)*)
($($cases)* send($s, $m) -> $res => $body,)
$default
)
}; // Print an error if the argument list is invalid.
(@case
(send($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*)
($($cases:tt)*)
$default:tt
) => {
compile_error!(
concat!( "invalid argument list in `send(",
stringify!($($args)*), ")`",
)
)
}; // Print an error if there is no argument list.
(@case
(send $t:tt $($tail:tt)*)
($($cases:tt)*)
$default:tt
) => {
compile_error!(
concat!( "expected an argument list after `send`, found `",
stringify!($t), "`",
)
)
};
// Check the format of a default case.
(@case
(default() => $body:tt, $($tail:tt)*)
$cases:tt
()
) => {
$crate::crossbeam_channel_internal!(
@case
($($tail)*)
$cases
(default() => $body,)
)
}; // Check the format of a default case with timeout.
(@case
(default($timeout:expr $(,)?) => $body:tt, $($tail:tt)*)
$cases:tt
()
) => {
$crate::crossbeam_channel_internal!(
@case
($($tail)*)
$cases
(default($timeout) => $body,)
)
}; // Check for duplicate default cases...
(@case
(default $($tail:tt)*)
$cases:tt
($($def:tt)+)
) => {
compile_error!( "there can be only one `default` case in a `select!` block"
)
}; // Print an error if the argument list is invalid.
(@case
(default($($args:tt)*) => $body:tt, $($tail:tt)*)
$cases:tt
$default:tt
) => {
compile_error!(
concat!( "invalid argument list in `default(",
stringify!($($args)*), ")`",
)
)
}; // Print an error if there is an unexpected token after `default`.
(@case
(default $t:tt $($tail:tt)*)
$cases:tt
$default:tt
) => {
compile_error!(
concat!( "expected an argument list or `=>` after `default`, found `",
stringify!($t), "`",
)
)
};
// The case was not consumed, therefore it must be invalid.
(@case
($case:ident $($tail:tt)*)
$cases:tt
$default:tt
) => {
compile_error!(
concat!( "expected one of `recv`, `send`, or `default`, found `",
stringify!($case), "`",
)
)
};
// Optimize `select!` into `try_recv()`.
(@init
(recv($r:expr) -> $res:pat => $recv_body:tt,)
(default() => $default_body:tt,)
) => {{ match $r { ref _r => { let _r: &$crate::Receiver<_> = _r; match _r.try_recv() {
::std::result::Result::Err($crate::TryRecvError::Empty) => {
$default_body
}
_res => { let _res = _res.map_err(|_| $crate::RecvError); let $res = _res;
$recv_body
}
}
}
}
}}; // Optimize `select!` into `recv()`.
(@init
(recv($r:expr) -> $res:pat => $body:tt,)
()
) => {{ match $r { ref _r => { let _r: &$crate::Receiver<_> = _r; let _res = _r.recv(); let $res = _res;
$body
}
}
}}; // Optimize `select!` into `recv_timeout()`.
(@init
(recv($r:expr) -> $res:pat => $recv_body:tt,)
(default($timeout:expr) => $default_body:tt,)
) => {{ match $r { ref _r => { let _r: &$crate::Receiver<_> = _r; match _r.recv_timeout($timeout) {
::std::result::Result::Err($crate::RecvTimeoutError::Timeout) => {
$default_body
}
_res => { let _res = _res.map_err(|_| $crate::RecvError); let $res = _res;
$recv_body
}
}
}
}
}};
// // Optimize the non-blocking case with two receive operations. // (@init // (recv($r1:expr) -> $res1:pat => $recv_body1:tt,) // (recv($r2:expr) -> $res2:pat => $recv_body2:tt,) // (default() => $default_body:tt,) // ) => {{ // match $r1 { // ref _r1 => { // let _r1: &$crate::Receiver<_> = _r1; // // match $r2 { // ref _r2 => { // let _r2: &$crate::Receiver<_> = _r2; // // // TODO(stjepang): Implement this optimization. // } // } // } // } // }}; // // Optimize the blocking case with two receive operations. // (@init // (recv($r1:expr) -> $res1:pat => $body1:tt,) // (recv($r2:expr) -> $res2:pat => $body2:tt,) // () // ) => {{ // match $r1 { // ref _r1 => { // let _r1: &$crate::Receiver<_> = _r1; // // match $r2 { // ref _r2 => { // let _r2: &$crate::Receiver<_> = _r2; // // // TODO(stjepang): Implement this optimization. // } // } // } // } // }}; // // Optimize the case with two receive operations and a timeout. // (@init // (recv($r1:expr) -> $res1:pat => $recv_body1:tt,) // (recv($r2:expr) -> $res2:pat => $recv_body2:tt,) // (default($timeout:expr) => $default_body:tt,) // ) => {{ // match $r1 { // ref _r1 => { // let _r1: &$crate::Receiver<_> = _r1; // // match $r2 { // ref _r2 => { // let _r2: &$crate::Receiver<_> = _r2; // // // TODO(stjepang): Implement this optimization. // } // } // } // } // }};
// Create the list of handles and add operations to it.
(@init
($($cases:tt)*)
$default:tt
) => {{ const _LEN: usize = $crate::crossbeam_channel_internal!(@count ($($cases)*)); let _handle: &dyn $crate::internal::SelectHandle = &$crate::never::<()>();
// Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) }
};
match _oper {
None => {
{ $sel };
$body
}
Some(_oper) => {
$crate::crossbeam_channel_internal! {
@complete
$sel
_oper
$cases
}
}
}
}}; // Run selection with a timeout.
(@add
$sel:ident
()
(default($timeout:expr) => $body:tt,)
$labels:tt
$cases:tt
) => {{ let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = { let _oper = $crate::internal::select_timeout(&mut $sel, $timeout, _IS_BIASED);
// Erase the lifetime so that `sel` can be dropped early even without NLL. unsafe { ::std::mem::transmute(_oper) }
};
match _oper {
::std::option::Option::None => {
{ $sel };
$body
}
::std::option::Option::Some(_oper) => {
$crate::crossbeam_channel_internal! {
@complete
$sel
_oper
$cases
}
}
}
}}; // Have we used up all labels?
(@add
$sel:ident
$input:tt
$default:tt
()
$cases:tt
) => {
compile_error!("too many operations in a `select!` block")
}; // Add a receive operation to `sel`.
(@add
$sel:ident
(recv($r:expr) -> $res:pat => $body:tt, $($tail:tt)*)
$default:tt
(($i:tt $var:ident) $($labels:tt)*)
($($cases:tt)*)
) => {{ match $r { ref _r => { let $var: &$crate::Receiver<_> = unsafe { let _r: &$crate::Receiver<_> = _r;
// Erase the lifetime so that `sel` can be dropped early even without NLL. unsafefn unbind<'a, T>(x: &T) -> &'a T {
::std::mem::transmute(x)
}
unbind(_r)
};
$sel[$i] = ($var, $i, $var as *const $crate::Receiver<_> as *const u8);
// Erase the lifetime so that `sel` can be dropped early even without NLL. unsafefn unbind<'a, T>(x: &T) -> &'a T {
::std::mem::transmute(x)
}
unbind(_s)
};
$sel[$i] = ($var, $i, $var as *const $crate::Sender<_> as *const u8);
let $res = _res;
$body
} else {
$crate::crossbeam_channel_internal! {
@complete
$sel
$oper
($($tail)*)
}
}
}}; // Panic if we don't identify the selected case, but this should never happen.
(@complete
$sel:ident
$oper:ident
()
) => {{
unreachable!( "internal error in crossbeam-channel: invalid case"
)
}};
// Catches a bug within this macro (should not happen).
(@$($tokens:tt)*) => {
compile_error!(
concat!( "internal error in crossbeam-channel: ",
stringify!(@$($tokens)*),
)
)
};
/// Selects from a set of channel operations. /// /// This macro allows you to define a set of channel operations, wait until any one of them becomes /// ready, and finally execute it. If multiple operations are ready at the same time, a random one /// among them is selected (i.e. the unbiased selection). Use `select_biased!` for the biased /// selection. /// /// It is also possible to define a `default` case that gets executed if none of the operations are /// ready, either right away or for a certain duration of time. /// /// An operation is considered to be ready if it doesn't have to block. Note that it is ready even /// when it will simply return an error because the channel is disconnected. /// /// The `select!` macro is a convenience wrapper around [`Select`]. However, it cannot select over a /// dynamically created list of channel operations. /// /// [`Select`]: super::Select /// /// # Examples /// /// Block until a send or a receive operation is selected: /// /// ``` /// use crossbeam_channel::{select, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// s1.send(10).unwrap(); /// /// // Since both operations are initially ready, a random one will be executed. /// select! { /// recv(r1) -> msg => assert_eq!(msg, Ok(10)), /// send(s2, 20) -> res => { /// assert_eq!(res, Ok(())); /// assert_eq!(r2.recv(), Ok(20)); /// } /// } /// ``` /// /// Select from a set of operations without blocking: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{select, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || { /// thread::sleep(Duration::from_millis(500)); /// s2.send(20).unwrap(); /// }); /// /// // None of the operations are initially ready. /// select! { /// recv(r1) -> msg => panic!(), /// recv(r2) -> msg => panic!(), /// default => println!("not ready"), /// } /// ``` /// /// Select over a set of operations with a timeout: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{select, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || { /// thread::sleep(Duration::from_millis(500)); /// s2.send(20).unwrap(); /// }); /// /// // None of the two operations will become ready within 100 milliseconds. /// select! { /// recv(r1) -> msg => panic!(), /// recv(r2) -> msg => panic!(), /// default(Duration::from_millis(100)) => println!("timed out"), /// } /// ``` /// /// Optionally add a receive operation to `select!` using [`never`]: /// /// ``` /// use std::thread; /// use std::time::Duration; /// use crossbeam_channel::{select, never, unbounded}; /// /// let (s1, r1) = unbounded(); /// let (s2, r2) = unbounded(); /// /// thread::spawn(move || { /// thread::sleep(Duration::from_secs(1)); /// s1.send(10).unwrap(); /// }); /// thread::spawn(move || { /// thread::sleep(Duration::from_millis(500)); /// s2.send(20).unwrap(); /// }); /// /// // This receiver can be a `Some` or a `None`. /// let r2 = Some(&r2); /// /// // None of the two operations will become ready within 100 milliseconds. /// select! { /// recv(r1) -> msg => panic!(), /// recv(r2.unwrap_or(&never())) -> msg => assert_eq!(msg, Ok(20)), /// } /// ``` /// /// To optionally add a timeout to `select!`, see the [example] for [`never`]. /// /// [`never`]: super::never /// [example]: super::never#examples #[macro_export]
macro_rules! select {
($($tokens:tt)*) => {
{ const _IS_BIASED: bool = false;
/// Selects from a set of channel operations. /// /// This macro allows you to define a list of channel operations, wait until any one of them /// becomes ready, and finally execute it. If multiple operations are ready at the same time, the /// operation nearest to the front of the list is always selected (i.e. the biased selection). Use /// [`select!`] for the unbiased selection. /// /// Otherwise, this macro's functionality is identical to [`select!`]. Refer to it for the syntax. #[macro_export]
macro_rules! select_biased {
($($tokens:tt)*) => {
{ const _IS_BIASED: bool = true;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.