//! 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 },) ) }; // Accept a trailing comma at the end of the list. (@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 ) }; // Allow trailing comma... (@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 ) }; // Allow trailing comma... (@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,) ) }; // Allow trailing comma... (@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)* ) }; }