summaryrefslogtreecommitdiffstats
path: root/third_party/rust/crossbeam-channel/src/select_macro.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/crossbeam-channel/src/select_macro.rs
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/crossbeam-channel/src/select_macro.rs')
-rw-r--r--third_party/rust/crossbeam-channel/src/select_macro.rs1116
1 files changed, 1116 insertions, 0 deletions
diff --git a/third_party/rust/crossbeam-channel/src/select_macro.rs b/third_party/rust/crossbeam-channel/src/select_macro.rs
new file mode 100644
index 0000000000..efe0ae4064
--- /dev/null
+++ b/third_party/rust/crossbeam-channel/src/select_macro.rs
@@ -0,0 +1,1116 @@
+//! The `select!` macro.
+
+/// 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.
+ // }
+ // }
+ // }
+ // }
+ // }};
+
+ // // Optimize `select!` into `try_send()`.
+ // (@init
+ // (send($s:expr, $m:expr) -> $res:pat => $send_body:tt,)
+ // (default() => $default_body:tt,)
+ // ) => {{
+ // match $s {
+ // ref _s => {
+ // let _s: &$crate::Sender<_> = _s;
+ // // TODO(stjepang): Implement this optimization.
+ // }
+ // }
+ // }};
+ // // Optimize `select!` into `send()`.
+ // (@init
+ // (send($s:expr, $m:expr) -> $res:pat => $body:tt,)
+ // ()
+ // ) => {{
+ // match $s {
+ // ref _s => {
+ // let _s: &$crate::Sender<_> = _s;
+ // // TODO(stjepang): Implement this optimization.
+ // }
+ // }
+ // }};
+ // // Optimize `select!` into `send_timeout()`.
+ // (@init
+ // (send($s:expr, $m:expr) -> $res:pat => $body:tt,)
+ // (default($timeout:expr) => $body:tt,)
+ // ) => {{
+ // match $s {
+ // ref _s => {
+ // let _s: &$crate::Sender<_> = _s;
+ // // 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: &$crate::internal::SelectHandle = &$crate::never::<()>();
+
+ #[allow(unused_mut)]
+ let mut _sel = [(_handle, 0, ::std::ptr::null()); _LEN];
+
+ $crate::crossbeam_channel_internal!(
+ @add
+ _sel
+ ($($cases)*)
+ $default
+ (
+ (0usize _oper0)
+ (1usize _oper1)
+ (2usize _oper2)
+ (3usize _oper3)
+ (4usize _oper4)
+ (5usize _oper5)
+ (6usize _oper6)
+ (7usize _oper7)
+ (8usize _oper8)
+ (9usize _oper9)
+ (10usize _oper10)
+ (11usize _oper11)
+ (12usize _oper12)
+ (13usize _oper13)
+ (14usize _oper14)
+ (15usize _oper15)
+ (16usize _oper16)
+ (17usize _oper17)
+ (18usize _oper18)
+ (19usize _oper19)
+ (20usize _oper20)
+ (21usize _oper21)
+ (22usize _oper22)
+ (23usize _oper23)
+ (24usize _oper24)
+ (25usize _oper25)
+ (26usize _oper26)
+ (27usize _oper27)
+ (28usize _oper28)
+ (29usize _oper29)
+ (30usize _oper30)
+ (31usize _oper31)
+ )
+ ()
+ )
+ }};
+
+ // Count the listed cases.
+ (@count ()) => {
+ 0
+ };
+ (@count ($oper:ident $args:tt -> $res:pat => $body:tt, $($cases:tt)*)) => {
+ 1 + $crate::crossbeam_channel_internal!(@count ($($cases)*))
+ };
+
+ // Run blocking selection.
+ (@add
+ $sel:ident
+ ()
+ ()
+ $labels:tt
+ $cases:tt
+ ) => {{
+ let _oper: $crate::SelectedOperation<'_> = {
+ let _oper = $crate::internal::select(&mut $sel);
+
+ // Erase the lifetime so that `sel` can be dropped early even without NLL.
+ unsafe { ::std::mem::transmute(_oper) }
+ };
+
+ $crate::crossbeam_channel_internal! {
+ @complete
+ $sel
+ _oper
+ $cases
+ }
+ }};
+ // Run non-blocking selection.
+ (@add
+ $sel:ident
+ ()
+ (default() => $body:tt,)
+ $labels:tt
+ $cases:tt
+ ) => {{
+ let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = {
+ let _oper = $crate::internal::try_select(&mut $sel);
+
+ // 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);
+
+ // 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.
+ unsafe fn 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);
+
+ $crate::crossbeam_channel_internal!(
+ @add
+ $sel
+ ($($tail)*)
+ $default
+ ($($labels)*)
+ ($($cases)* [$i] recv($var) -> $res => $body,)
+ )
+ }
+ }
+ }};
+ // Add a send operation to `sel`.
+ (@add
+ $sel:ident
+ (send($s:expr, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*)
+ $default:tt
+ (($i:tt $var:ident) $($labels:tt)*)
+ ($($cases:tt)*)
+ ) => {{
+ match $s {
+ ref _s => {
+ let $var: &$crate::Sender<_> = unsafe {
+ let _s: &$crate::Sender<_> = _s;
+
+ // Erase the lifetime so that `sel` can be dropped early even without NLL.
+ unsafe fn 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);
+
+ $crate::crossbeam_channel_internal!(
+ @add
+ $sel
+ ($($tail)*)
+ $default
+ ($($labels)*)
+ ($($cases)* [$i] send($var, $m) -> $res => $body,)
+ )
+ }
+ }
+ }};
+
+ // Complete a receive operation.
+ (@complete
+ $sel:ident
+ $oper:ident
+ ([$i:tt] recv($r:ident) -> $res:pat => $body:tt, $($tail:tt)*)
+ ) => {{
+ if $oper.index() == $i {
+ let _res = $oper.recv($r);
+ { $sel };
+
+ let $res = _res;
+ $body
+ } else {
+ $crate::crossbeam_channel_internal! {
+ @complete
+ $sel
+ $oper
+ ($($tail)*)
+ }
+ }
+ }};
+ // Complete a send operation.
+ (@complete
+ $sel:ident
+ $oper:ident
+ ([$i:tt] send($s:ident, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*)
+ ) => {{
+ if $oper.index() == $i {
+ let _res = $oper.send($s, $m);
+ { $sel };
+
+ 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)*),
+ )
+ )
+ };
+
+ // The entry points.
+ () => {
+ compile_error!("empty `select!` block")
+ };
+ ($($case:ident $(($($args:tt)*))* => $body:expr $(,)*)*) => {
+ $crate::crossbeam_channel_internal!(
+ @list
+ ($($case $(($($args)*))* => { $body },)*)
+ ()
+ )
+ };
+ ($($tokens:tt)*) => {
+ $crate::crossbeam_channel_internal!(
+ @list
+ ($($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.
+///
+/// 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)*) => {
+ $crate::crossbeam_channel_internal!(
+ $($tokens)*
+ )
+ };
+}