crossbeam_channel/select_macro.rs
1//! The `select!` macro.
2
3/// A helper macro for `select!` to hide the long list of macro patterns from the documentation.
4///
5/// The macro consists of two stages:
6/// 1. Parsing
7/// 2. Code generation
8///
9/// The parsing stage consists of these subparts:
10/// 1. `@list`: Turns a list of tokens into a list of cases.
11/// 2. `@list_errorN`: Diagnoses the syntax error.
12/// 3. `@case`: Parses a single case and verifies its argument list.
13///
14/// The codegen stage consists of these subparts:
15/// 1. `@init`: Attempts to optimize `select!` away and initializes the list of handles.
16/// 1. `@count`: Counts the listed cases.
17/// 3. `@add`: Adds send/receive operations to the list of handles and starts selection.
18/// 4. `@complete`: Completes the selected send/receive operation.
19///
20/// If the parsing stage encounters a syntax error or the codegen stage ends up with too many
21/// cases to process, the macro fails with a compile-time error.
22#[doc(hidden)]
23#[macro_export]
24macro_rules! crossbeam_channel_internal {
25 // The list is empty. Now check the arguments of each processed case.
26 (@list
27 ()
28 ($($head:tt)*)
29 ) => {
30 $crate::crossbeam_channel_internal!(
31 @case
32 ($($head)*)
33 ()
34 ()
35 )
36 };
37 // If necessary, insert an empty argument list after `default`.
38 (@list
39 (default => $($tail:tt)*)
40 ($($head:tt)*)
41 ) => {
42 $crate::crossbeam_channel_internal!(
43 @list
44 (default() => $($tail)*)
45 ($($head)*)
46 )
47 };
48 // But print an error if `default` is followed by a `->`.
49 (@list
50 (default -> $($tail:tt)*)
51 ($($head:tt)*)
52 ) => {
53 compile_error!(
54 "expected `=>` after `default` case, found `->`"
55 )
56 };
57 // Print an error if there's an `->` after the argument list in the default case.
58 (@list
59 (default $args:tt -> $($tail:tt)*)
60 ($($head:tt)*)
61 ) => {
62 compile_error!(
63 "expected `=>` after `default` case, found `->`"
64 )
65 };
66 // Print an error if there is a missing result in a recv case.
67 (@list
68 (recv($($args:tt)*) => $($tail:tt)*)
69 ($($head:tt)*)
70 ) => {
71 compile_error!(
72 "expected `->` after `recv` case, found `=>`"
73 )
74 };
75 // Print an error if there is a missing result in a send case.
76 (@list
77 (send($($args:tt)*) => $($tail:tt)*)
78 ($($head:tt)*)
79 ) => {
80 compile_error!(
81 "expected `->` after `send` operation, found `=>`"
82 )
83 };
84 // Make sure the arrow and the result are not repeated.
85 (@list
86 ($case:ident $args:tt -> $res:tt -> $($tail:tt)*)
87 ($($head:tt)*)
88 ) => {
89 compile_error!("expected `=>`, found `->`")
90 };
91 // Print an error if there is a semicolon after the block.
92 (@list
93 ($case:ident $args:tt $(-> $res:pat)* => { $($body:tt)* }; $($tail:tt)*)
94 ($($head:tt)*)
95 ) => {
96 compile_error!(
97 "did you mean to put a comma instead of the semicolon after `}`?"
98 )
99 };
100 // The first case is separated by a comma.
101 (@list
102 ($case:ident ($($args:tt)*) $(-> $res:pat)* => { $($body:tt)* }, $($tail:tt)*)
103 ($($head:tt)*)
104 ) => {
105 $crate::crossbeam_channel_internal!(
106 @list
107 ($($tail)*)
108 ($($head)* $case ($($args)*) $(-> $res)* => { $($body)* },)
109 )
110 };
111 (@list
112 ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr, $($tail:tt)*)
113 ($($head:tt)*)
114 ) => {
115 $crate::crossbeam_channel_internal!(
116 @list
117 ($($tail)*)
118 ($($head)* $case ($($args)*) $(-> $res)* => { $body },)
119 )
120 };
121 // Don't require a comma after the case if it has a proper block.
122 (@list
123 ($case:ident ($($args:tt)*) $(-> $res:pat)* => { $($body:tt)* } $($tail:tt)*)
124 ($($head:tt)*)
125 ) => {
126 $crate::crossbeam_channel_internal!(
127 @list
128 ($($tail)*)
129 ($($head)* $case ($($args)*) $(-> $res)* => { $($body)* },)
130 )
131 };
132 // Only one case remains.
133 (@list
134 ($case:ident ($($args:tt)*) $(-> $res:pat)* => $body:expr $(,)?)
135 ($($head:tt)*)
136 ) => {
137 $crate::crossbeam_channel_internal!(
138 @list
139 ()
140 ($($head)* $case ($($args)*) $(-> $res)* => { $body },)
141 )
142 };
143 // Diagnose and print an error.
144 (@list
145 ($($tail:tt)*)
146 ($($head:tt)*)
147 ) => {
148 $crate::crossbeam_channel_internal!(@list_error1 $($tail)*)
149 };
150 // Stage 1: check the case type.
151 (@list_error1 recv $($tail:tt)*) => {
152 $crate::crossbeam_channel_internal!(@list_error2 recv $($tail)*)
153 };
154 (@list_error1 send $($tail:tt)*) => {
155 $crate::crossbeam_channel_internal!(@list_error2 send $($tail)*)
156 };
157 (@list_error1 default $($tail:tt)*) => {
158 $crate::crossbeam_channel_internal!(@list_error2 default $($tail)*)
159 };
160 (@list_error1 $t:tt $($tail:tt)*) => {
161 compile_error!(
162 concat!(
163 "expected one of `recv`, `send`, or `default`, found `",
164 stringify!($t),
165 "`",
166 )
167 )
168 };
169 (@list_error1 $($tail:tt)*) => {
170 $crate::crossbeam_channel_internal!(@list_error2 $($tail)*);
171 };
172 // Stage 2: check the argument list.
173 (@list_error2 $case:ident) => {
174 compile_error!(
175 concat!(
176 "missing argument list after `",
177 stringify!($case),
178 "`",
179 )
180 )
181 };
182 (@list_error2 $case:ident => $($tail:tt)*) => {
183 compile_error!(
184 concat!(
185 "missing argument list after `",
186 stringify!($case),
187 "`",
188 )
189 )
190 };
191 (@list_error2 $($tail:tt)*) => {
192 $crate::crossbeam_channel_internal!(@list_error3 $($tail)*)
193 };
194 // Stage 3: check the `=>` and what comes after it.
195 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)*) => {
196 compile_error!(
197 concat!(
198 "missing `=>` after `",
199 stringify!($case),
200 "` case",
201 )
202 )
203 };
204 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* =>) => {
205 compile_error!(
206 "expected expression after `=>`"
207 )
208 };
209 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:expr; $($tail:tt)*) => {
210 compile_error!(
211 concat!(
212 "did you mean to put a comma instead of the semicolon after `",
213 stringify!($body),
214 "`?",
215 )
216 )
217 };
218 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => recv($($a:tt)*) $($tail:tt)*) => {
219 compile_error!(
220 "expected an expression after `=>`"
221 )
222 };
223 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => send($($a:tt)*) $($tail:tt)*) => {
224 compile_error!(
225 "expected an expression after `=>`"
226 )
227 };
228 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => default($($a:tt)*) $($tail:tt)*) => {
229 compile_error!(
230 "expected an expression after `=>`"
231 )
232 };
233 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident($($a:tt)*) $($tail:tt)*) => {
234 compile_error!(
235 concat!(
236 "did you mean to put a comma after `",
237 stringify!($f),
238 "(",
239 stringify!($($a)*),
240 ")`?",
241 )
242 )
243 };
244 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!($($a:tt)*) $($tail:tt)*) => {
245 compile_error!(
246 concat!(
247 "did you mean to put a comma after `",
248 stringify!($f),
249 "!(",
250 stringify!($($a)*),
251 ")`?",
252 )
253 )
254 };
255 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident![$($a:tt)*] $($tail:tt)*) => {
256 compile_error!(
257 concat!(
258 "did you mean to put a comma after `",
259 stringify!($f),
260 "![",
261 stringify!($($a)*),
262 "]`?",
263 )
264 )
265 };
266 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $f:ident!{$($a:tt)*} $($tail:tt)*) => {
267 compile_error!(
268 concat!(
269 "did you mean to put a comma after `",
270 stringify!($f),
271 "!{",
272 stringify!($($a)*),
273 "}`?",
274 )
275 )
276 };
277 (@list_error3 $case:ident($($args:tt)*) $(-> $r:pat)* => $body:tt $($tail:tt)*) => {
278 compile_error!(
279 concat!(
280 "did you mean to put a comma after `",
281 stringify!($body),
282 "`?",
283 )
284 )
285 };
286 (@list_error3 $case:ident($($args:tt)*) -> => $($tail:tt)*) => {
287 compile_error!("missing pattern after `->`")
288 };
289 (@list_error3 $case:ident($($args:tt)*) $t:tt $(-> $r:pat)* => $($tail:tt)*) => {
290 compile_error!(
291 concat!(
292 "expected `->`, found `",
293 stringify!($t),
294 "`",
295 )
296 )
297 };
298 (@list_error3 $case:ident($($args:tt)*) -> $t:tt $($tail:tt)*) => {
299 compile_error!(
300 concat!(
301 "expected a pattern, found `",
302 stringify!($t),
303 "`",
304 )
305 )
306 };
307 (@list_error3 recv($($args:tt)*) $t:tt $($tail:tt)*) => {
308 compile_error!(
309 concat!(
310 "expected `->`, found `",
311 stringify!($t),
312 "`",
313 )
314 )
315 };
316 (@list_error3 send($($args:tt)*) $t:tt $($tail:tt)*) => {
317 compile_error!(
318 concat!(
319 "expected `->`, found `",
320 stringify!($t),
321 "`",
322 )
323 )
324 };
325 (@list_error3 recv $args:tt $($tail:tt)*) => {
326 compile_error!(
327 concat!(
328 "expected an argument list after `recv`, found `",
329 stringify!($args),
330 "`",
331 )
332 )
333 };
334 (@list_error3 send $args:tt $($tail:tt)*) => {
335 compile_error!(
336 concat!(
337 "expected an argument list after `send`, found `",
338 stringify!($args),
339 "`",
340 )
341 )
342 };
343 (@list_error3 default $args:tt $($tail:tt)*) => {
344 compile_error!(
345 concat!(
346 "expected an argument list or `=>` after `default`, found `",
347 stringify!($args),
348 "`",
349 )
350 )
351 };
352 (@list_error3 $($tail:tt)*) => {
353 $crate::crossbeam_channel_internal!(@list_error4 $($tail)*)
354 };
355 // Stage 4: fail with a generic error message.
356 (@list_error4 $($tail:tt)*) => {
357 compile_error!("invalid syntax")
358 };
359
360 // Success! All cases were parsed.
361 (@case
362 ()
363 $cases:tt
364 $default:tt
365 ) => {
366 $crate::crossbeam_channel_internal!(
367 @init
368 $cases
369 $default
370 )
371 };
372
373 // Check the format of a recv case.
374 (@case
375 (recv($r:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*)
376 ($($cases:tt)*)
377 $default:tt
378 ) => {
379 $crate::crossbeam_channel_internal!(
380 @case
381 ($($tail)*)
382 ($($cases)* recv($r) -> $res => $body,)
383 $default
384 )
385 };
386 // Print an error if the argument list is invalid.
387 (@case
388 (recv($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*)
389 ($($cases:tt)*)
390 $default:tt
391 ) => {
392 compile_error!(
393 concat!(
394 "invalid argument list in `recv(",
395 stringify!($($args)*),
396 ")`",
397 )
398 )
399 };
400 // Print an error if there is no argument list.
401 (@case
402 (recv $t:tt $($tail:tt)*)
403 ($($cases:tt)*)
404 $default:tt
405 ) => {
406 compile_error!(
407 concat!(
408 "expected an argument list after `recv`, found `",
409 stringify!($t),
410 "`",
411 )
412 )
413 };
414
415 // Check the format of a send case.
416 (@case
417 (send($s:expr, $m:expr $(,)?) -> $res:pat => $body:tt, $($tail:tt)*)
418 ($($cases:tt)*)
419 $default:tt
420 ) => {
421 $crate::crossbeam_channel_internal!(
422 @case
423 ($($tail)*)
424 ($($cases)* send($s, $m) -> $res => $body,)
425 $default
426 )
427 };
428 // Print an error if the argument list is invalid.
429 (@case
430 (send($($args:tt)*) -> $res:pat => $body:tt, $($tail:tt)*)
431 ($($cases:tt)*)
432 $default:tt
433 ) => {
434 compile_error!(
435 concat!(
436 "invalid argument list in `send(",
437 stringify!($($args)*),
438 ")`",
439 )
440 )
441 };
442 // Print an error if there is no argument list.
443 (@case
444 (send $t:tt $($tail:tt)*)
445 ($($cases:tt)*)
446 $default:tt
447 ) => {
448 compile_error!(
449 concat!(
450 "expected an argument list after `send`, found `",
451 stringify!($t),
452 "`",
453 )
454 )
455 };
456
457 // Check the format of a default case.
458 (@case
459 (default() => $body:tt, $($tail:tt)*)
460 $cases:tt
461 ()
462 ) => {
463 $crate::crossbeam_channel_internal!(
464 @case
465 ($($tail)*)
466 $cases
467 (default() => $body,)
468 )
469 };
470 // Check the format of a default case with timeout.
471 (@case
472 (default($timeout:expr $(,)?) => $body:tt, $($tail:tt)*)
473 $cases:tt
474 ()
475 ) => {
476 $crate::crossbeam_channel_internal!(
477 @case
478 ($($tail)*)
479 $cases
480 (default($timeout) => $body,)
481 )
482 };
483 // Check for duplicate default cases...
484 (@case
485 (default $($tail:tt)*)
486 $cases:tt
487 ($($def:tt)+)
488 ) => {
489 compile_error!(
490 "there can be only one `default` case in a `select!` block"
491 )
492 };
493 // Print an error if the argument list is invalid.
494 (@case
495 (default($($args:tt)*) => $body:tt, $($tail:tt)*)
496 $cases:tt
497 $default:tt
498 ) => {
499 compile_error!(
500 concat!(
501 "invalid argument list in `default(",
502 stringify!($($args)*),
503 ")`",
504 )
505 )
506 };
507 // Print an error if there is an unexpected token after `default`.
508 (@case
509 (default $t:tt $($tail:tt)*)
510 $cases:tt
511 $default:tt
512 ) => {
513 compile_error!(
514 concat!(
515 "expected an argument list or `=>` after `default`, found `",
516 stringify!($t),
517 "`",
518 )
519 )
520 };
521
522 // The case was not consumed, therefore it must be invalid.
523 (@case
524 ($case:ident $($tail:tt)*)
525 $cases:tt
526 $default:tt
527 ) => {
528 compile_error!(
529 concat!(
530 "expected one of `recv`, `send`, or `default`, found `",
531 stringify!($case),
532 "`",
533 )
534 )
535 };
536
537 // Optimize `select!` into `try_recv()`.
538 (@init
539 (recv($r:expr) -> $res:pat => $recv_body:tt,)
540 (default() => $default_body:tt,)
541 ) => {{
542 match $r {
543 ref _r => {
544 let _r: &$crate::Receiver<_> = _r;
545 match _r.try_recv() {
546 ::std::result::Result::Err($crate::TryRecvError::Empty) => {
547 $default_body
548 }
549 _res => {
550 let _res = _res.map_err(|_| $crate::RecvError);
551 let $res = _res;
552 $recv_body
553 }
554 }
555 }
556 }
557 }};
558 // Optimize `select!` into `recv()`.
559 (@init
560 (recv($r:expr) -> $res:pat => $body:tt,)
561 ()
562 ) => {{
563 match $r {
564 ref _r => {
565 let _r: &$crate::Receiver<_> = _r;
566 let _res = _r.recv();
567 let $res = _res;
568 $body
569 }
570 }
571 }};
572 // Optimize `select!` into `recv_timeout()`.
573 (@init
574 (recv($r:expr) -> $res:pat => $recv_body:tt,)
575 (default($timeout:expr) => $default_body:tt,)
576 ) => {{
577 match $r {
578 ref _r => {
579 let _r: &$crate::Receiver<_> = _r;
580 match _r.recv_timeout($timeout) {
581 ::std::result::Result::Err($crate::RecvTimeoutError::Timeout) => {
582 $default_body
583 }
584 _res => {
585 let _res = _res.map_err(|_| $crate::RecvError);
586 let $res = _res;
587 $recv_body
588 }
589 }
590 }
591 }
592 }};
593
594 // // Optimize the non-blocking case with two receive operations.
595 // (@init
596 // (recv($r1:expr) -> $res1:pat => $recv_body1:tt,)
597 // (recv($r2:expr) -> $res2:pat => $recv_body2:tt,)
598 // (default() => $default_body:tt,)
599 // ) => {{
600 // match $r1 {
601 // ref _r1 => {
602 // let _r1: &$crate::Receiver<_> = _r1;
603 //
604 // match $r2 {
605 // ref _r2 => {
606 // let _r2: &$crate::Receiver<_> = _r2;
607 //
608 // // TODO(stjepang): Implement this optimization.
609 // }
610 // }
611 // }
612 // }
613 // }};
614 // // Optimize the blocking case with two receive operations.
615 // (@init
616 // (recv($r1:expr) -> $res1:pat => $body1:tt,)
617 // (recv($r2:expr) -> $res2:pat => $body2:tt,)
618 // ()
619 // ) => {{
620 // match $r1 {
621 // ref _r1 => {
622 // let _r1: &$crate::Receiver<_> = _r1;
623 //
624 // match $r2 {
625 // ref _r2 => {
626 // let _r2: &$crate::Receiver<_> = _r2;
627 //
628 // // TODO(stjepang): Implement this optimization.
629 // }
630 // }
631 // }
632 // }
633 // }};
634 // // Optimize the case with two receive operations and a timeout.
635 // (@init
636 // (recv($r1:expr) -> $res1:pat => $recv_body1:tt,)
637 // (recv($r2:expr) -> $res2:pat => $recv_body2:tt,)
638 // (default($timeout:expr) => $default_body:tt,)
639 // ) => {{
640 // match $r1 {
641 // ref _r1 => {
642 // let _r1: &$crate::Receiver<_> = _r1;
643 //
644 // match $r2 {
645 // ref _r2 => {
646 // let _r2: &$crate::Receiver<_> = _r2;
647 //
648 // // TODO(stjepang): Implement this optimization.
649 // }
650 // }
651 // }
652 // }
653 // }};
654
655 // // Optimize `select!` into `try_send()`.
656 // (@init
657 // (send($s:expr, $m:expr) -> $res:pat => $send_body:tt,)
658 // (default() => $default_body:tt,)
659 // ) => {{
660 // match $s {
661 // ref _s => {
662 // let _s: &$crate::Sender<_> = _s;
663 // // TODO(stjepang): Implement this optimization.
664 // }
665 // }
666 // }};
667 // // Optimize `select!` into `send()`.
668 // (@init
669 // (send($s:expr, $m:expr) -> $res:pat => $body:tt,)
670 // ()
671 // ) => {{
672 // match $s {
673 // ref _s => {
674 // let _s: &$crate::Sender<_> = _s;
675 // // TODO(stjepang): Implement this optimization.
676 // }
677 // }
678 // }};
679 // // Optimize `select!` into `send_timeout()`.
680 // (@init
681 // (send($s:expr, $m:expr) -> $res:pat => $body:tt,)
682 // (default($timeout:expr) => $body:tt,)
683 // ) => {{
684 // match $s {
685 // ref _s => {
686 // let _s: &$crate::Sender<_> = _s;
687 // // TODO(stjepang): Implement this optimization.
688 // }
689 // }
690 // }};
691
692 // Create the list of handles and add operations to it.
693 (@init
694 ($($cases:tt)*)
695 $default:tt
696 ) => {{
697 const _LEN: usize = $crate::crossbeam_channel_internal!(@count ($($cases)*));
698 let _handle: &dyn $crate::internal::SelectHandle = &$crate::never::<()>();
699
700 #[allow(unused_mut, clippy::zero_repeat_side_effects)]
701 let mut _sel = [(_handle, 0, ::std::ptr::null()); _LEN];
702
703 $crate::crossbeam_channel_internal!(
704 @add
705 _sel
706 ($($cases)*)
707 $default
708 (
709 (0usize _oper0)
710 (1usize _oper1)
711 (2usize _oper2)
712 (3usize _oper3)
713 (4usize _oper4)
714 (5usize _oper5)
715 (6usize _oper6)
716 (7usize _oper7)
717 (8usize _oper8)
718 (9usize _oper9)
719 (10usize _oper10)
720 (11usize _oper11)
721 (12usize _oper12)
722 (13usize _oper13)
723 (14usize _oper14)
724 (15usize _oper15)
725 (16usize _oper16)
726 (17usize _oper17)
727 (18usize _oper18)
728 (19usize _oper19)
729 (20usize _oper20)
730 (21usize _oper21)
731 (22usize _oper22)
732 (23usize _oper23)
733 (24usize _oper24)
734 (25usize _oper25)
735 (26usize _oper26)
736 (27usize _oper27)
737 (28usize _oper28)
738 (29usize _oper29)
739 (30usize _oper30)
740 (31usize _oper31)
741 )
742 ()
743 )
744 }};
745
746 // Count the listed cases.
747 (@count ()) => {
748 0
749 };
750 (@count ($oper:ident $args:tt -> $res:pat => $body:tt, $($cases:tt)*)) => {
751 1 + $crate::crossbeam_channel_internal!(@count ($($cases)*))
752 };
753
754 // Run blocking selection.
755 (@add
756 $sel:ident
757 ()
758 ()
759 $labels:tt
760 $cases:tt
761 ) => {{
762 let _oper: $crate::SelectedOperation<'_> = {
763 let _oper = $crate::internal::select(&mut $sel, _IS_BIASED);
764
765 // Erase the lifetime so that `sel` can be dropped early even without NLL.
766 unsafe { ::std::mem::transmute(_oper) }
767 };
768
769 $crate::crossbeam_channel_internal! {
770 @complete
771 $sel
772 _oper
773 $cases
774 }
775 }};
776 // Run non-blocking selection.
777 (@add
778 $sel:ident
779 ()
780 (default() => $body:tt,)
781 $labels:tt
782 $cases:tt
783 ) => {{
784 let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = {
785 let _oper = $crate::internal::try_select(&mut $sel, _IS_BIASED);
786
787 // Erase the lifetime so that `sel` can be dropped early even without NLL.
788 unsafe { ::std::mem::transmute(_oper) }
789 };
790
791 match _oper {
792 None => {
793 { $sel };
794 $body
795 }
796 Some(_oper) => {
797 $crate::crossbeam_channel_internal! {
798 @complete
799 $sel
800 _oper
801 $cases
802 }
803 }
804 }
805 }};
806 // Run selection with a timeout.
807 (@add
808 $sel:ident
809 ()
810 (default($timeout:expr) => $body:tt,)
811 $labels:tt
812 $cases:tt
813 ) => {{
814 let _oper: ::std::option::Option<$crate::SelectedOperation<'_>> = {
815 let _oper = $crate::internal::select_timeout(&mut $sel, $timeout, _IS_BIASED);
816
817 // Erase the lifetime so that `sel` can be dropped early even without NLL.
818 unsafe { ::std::mem::transmute(_oper) }
819 };
820
821 match _oper {
822 ::std::option::Option::None => {
823 { $sel };
824 $body
825 }
826 ::std::option::Option::Some(_oper) => {
827 $crate::crossbeam_channel_internal! {
828 @complete
829 $sel
830 _oper
831 $cases
832 }
833 }
834 }
835 }};
836 // Have we used up all labels?
837 (@add
838 $sel:ident
839 $input:tt
840 $default:tt
841 ()
842 $cases:tt
843 ) => {
844 compile_error!("too many operations in a `select!` block")
845 };
846 // Add a receive operation to `sel`.
847 (@add
848 $sel:ident
849 (recv($r:expr) -> $res:pat => $body:tt, $($tail:tt)*)
850 $default:tt
851 (($i:tt $var:ident) $($labels:tt)*)
852 ($($cases:tt)*)
853 ) => {{
854 match $r {
855 ref _r => {
856 let $var: &$crate::Receiver<_> = unsafe {
857 let _r: &$crate::Receiver<_> = _r;
858
859 // Erase the lifetime so that `sel` can be dropped early even without NLL.
860 unsafe fn unbind<'a, T>(x: &T) -> &'a T {
861 ::std::mem::transmute(x)
862 }
863 unbind(_r)
864 };
865 $sel[$i] = ($var, $i, $var as *const $crate::Receiver<_> as *const u8);
866
867 $crate::crossbeam_channel_internal!(
868 @add
869 $sel
870 ($($tail)*)
871 $default
872 ($($labels)*)
873 ($($cases)* [$i] recv($var) -> $res => $body,)
874 )
875 }
876 }
877 }};
878 // Add a send operation to `sel`.
879 (@add
880 $sel:ident
881 (send($s:expr, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*)
882 $default:tt
883 (($i:tt $var:ident) $($labels:tt)*)
884 ($($cases:tt)*)
885 ) => {{
886 match $s {
887 ref _s => {
888 let $var: &$crate::Sender<_> = unsafe {
889 let _s: &$crate::Sender<_> = _s;
890
891 // Erase the lifetime so that `sel` can be dropped early even without NLL.
892 unsafe fn unbind<'a, T>(x: &T) -> &'a T {
893 ::std::mem::transmute(x)
894 }
895 unbind(_s)
896 };
897 $sel[$i] = ($var, $i, $var as *const $crate::Sender<_> as *const u8);
898
899 $crate::crossbeam_channel_internal!(
900 @add
901 $sel
902 ($($tail)*)
903 $default
904 ($($labels)*)
905 ($($cases)* [$i] send($var, $m) -> $res => $body,)
906 )
907 }
908 }
909 }};
910
911 // Complete a receive operation.
912 (@complete
913 $sel:ident
914 $oper:ident
915 ([$i:tt] recv($r:ident) -> $res:pat => $body:tt, $($tail:tt)*)
916 ) => {{
917 if $oper.index() == $i {
918 let _res = $oper.recv($r);
919 { $sel };
920
921 let $res = _res;
922 $body
923 } else {
924 $crate::crossbeam_channel_internal! {
925 @complete
926 $sel
927 $oper
928 ($($tail)*)
929 }
930 }
931 }};
932 // Complete a send operation.
933 (@complete
934 $sel:ident
935 $oper:ident
936 ([$i:tt] send($s:ident, $m:expr) -> $res:pat => $body:tt, $($tail:tt)*)
937 ) => {{
938 if $oper.index() == $i {
939 let _res = $oper.send($s, $m);
940 { $sel };
941
942 let $res = _res;
943 $body
944 } else {
945 $crate::crossbeam_channel_internal! {
946 @complete
947 $sel
948 $oper
949 ($($tail)*)
950 }
951 }
952 }};
953 // Panic if we don't identify the selected case, but this should never happen.
954 (@complete
955 $sel:ident
956 $oper:ident
957 ()
958 ) => {{
959 unreachable!(
960 "internal error in crossbeam-channel: invalid case"
961 )
962 }};
963
964 // Catches a bug within this macro (should not happen).
965 (@$($tokens:tt)*) => {
966 compile_error!(
967 concat!(
968 "internal error in crossbeam-channel: ",
969 stringify!(@$($tokens)*),
970 )
971 )
972 };
973
974 // The entry points.
975 () => {
976 compile_error!("empty `select!` block")
977 };
978 ($($case:ident $(($($args:tt)*))* => $body:expr $(,)*)*) => {
979 $crate::crossbeam_channel_internal!(
980 @list
981 ($($case $(($($args)*))* => { $body },)*)
982 ()
983 )
984 };
985 ($($tokens:tt)*) => {
986 $crate::crossbeam_channel_internal!(
987 @list
988 ($($tokens)*)
989 ()
990 )
991 };
992}
993
994/// Selects from a set of channel operations.
995///
996/// This macro allows you to define a set of channel operations, wait until any one of them becomes
997/// ready, and finally execute it. If multiple operations are ready at the same time, a random one
998/// among them is selected (i.e. the unbiased selection). Use [`select_biased!`] for the biased
999/// selection.
1000///
1001/// It is also possible to define a `default` case that gets executed if none of the operations are
1002/// ready, either right away or for a certain duration of time.
1003///
1004/// An operation is considered to be ready if it doesn't have to block. Note that it is ready even
1005/// when it will simply return an error because the channel is disconnected.
1006///
1007/// The `select!` macro is a convenience wrapper around [`Select`]. However, it cannot select over a
1008/// dynamically created list of channel operations.
1009///
1010/// [`Select`]: super::Select
1011/// [`select_biased!`]: super::select_biased
1012///
1013/// # Examples
1014///
1015/// Block until a send or a receive operation is selected:
1016///
1017/// ```
1018/// use crossbeam_channel::{select, unbounded};
1019///
1020/// let (s1, r1) = unbounded();
1021/// let (s2, r2) = unbounded();
1022/// s1.send(10).unwrap();
1023///
1024/// // Since both operations are initially ready, a random one will be executed.
1025/// select! {
1026/// recv(r1) -> msg => assert_eq!(msg, Ok(10)),
1027/// send(s2, 20) -> res => {
1028/// assert_eq!(res, Ok(()));
1029/// assert_eq!(r2.recv(), Ok(20));
1030/// }
1031/// }
1032/// ```
1033///
1034/// Select from a set of operations without blocking:
1035///
1036/// ```
1037/// use std::thread;
1038/// use std::time::Duration;
1039/// use crossbeam_channel::{select, unbounded};
1040///
1041/// let (s1, r1) = unbounded();
1042/// let (s2, r2) = unbounded();
1043///
1044/// thread::spawn(move || {
1045/// thread::sleep(Duration::from_secs(1));
1046/// s1.send(10).unwrap();
1047/// });
1048/// thread::spawn(move || {
1049/// thread::sleep(Duration::from_millis(500));
1050/// s2.send(20).unwrap();
1051/// });
1052///
1053/// // None of the operations are initially ready.
1054/// select! {
1055/// recv(r1) -> msg => panic!(),
1056/// recv(r2) -> msg => panic!(),
1057/// default => println!("not ready"),
1058/// }
1059/// ```
1060///
1061/// Select over a set of operations with a timeout:
1062///
1063/// ```
1064/// use std::thread;
1065/// use std::time::Duration;
1066/// use crossbeam_channel::{select, unbounded};
1067///
1068/// let (s1, r1) = unbounded();
1069/// let (s2, r2) = unbounded();
1070///
1071/// thread::spawn(move || {
1072/// thread::sleep(Duration::from_secs(1));
1073/// s1.send(10).unwrap();
1074/// });
1075/// thread::spawn(move || {
1076/// thread::sleep(Duration::from_millis(500));
1077/// s2.send(20).unwrap();
1078/// });
1079///
1080/// // None of the two operations will become ready within 100 milliseconds.
1081/// select! {
1082/// recv(r1) -> msg => panic!(),
1083/// recv(r2) -> msg => panic!(),
1084/// default(Duration::from_millis(100)) => println!("timed out"),
1085/// }
1086/// ```
1087///
1088/// Optionally add a receive operation to `select!` using [`never`]:
1089///
1090/// ```
1091/// use std::thread;
1092/// use std::time::Duration;
1093/// use crossbeam_channel::{select, never, unbounded};
1094///
1095/// let (s1, r1) = unbounded();
1096/// let (s2, r2) = unbounded();
1097///
1098/// thread::spawn(move || {
1099/// thread::sleep(Duration::from_secs(1));
1100/// s1.send(10).unwrap();
1101/// });
1102/// thread::spawn(move || {
1103/// thread::sleep(Duration::from_millis(500));
1104/// s2.send(20).unwrap();
1105/// });
1106///
1107/// // This receiver can be a `Some` or a `None`.
1108/// let r2 = Some(&r2);
1109///
1110/// // None of the two operations will become ready within 100 milliseconds.
1111/// select! {
1112/// recv(r1) -> msg => panic!(),
1113/// recv(r2.unwrap_or(&never())) -> msg => assert_eq!(msg, Ok(20)),
1114/// }
1115/// ```
1116///
1117/// To optionally add a timeout to `select!`, see the [example] for [`never`].
1118///
1119/// [`never`]: super::never
1120/// [example]: super::never#examples
1121#[macro_export]
1122macro_rules! select {
1123 ($($tokens:tt)*) => {
1124 {
1125 const _IS_BIASED: bool = false;
1126
1127 $crate::crossbeam_channel_internal!(
1128 $($tokens)*
1129 )
1130 }
1131 };
1132}
1133
1134/// Selects from a set of channel operations.
1135///
1136/// This macro allows you to define a list of channel operations, wait until any one of them
1137/// becomes ready, and finally execute it. If multiple operations are ready at the same time, the
1138/// operation nearest to the front of the list is always selected (i.e. the biased selection). Use
1139/// [`select!`] for the unbiased selection.
1140///
1141/// Otherwise, this macro's functionality is identical to [`select!`]. Refer to it for the syntax.
1142#[macro_export]
1143macro_rules! select_biased {
1144 ($($tokens:tt)*) => {
1145 {
1146 const _IS_BIASED: bool = true;
1147
1148 $crate::crossbeam_channel_internal!(
1149 $($tokens)*
1150 )
1151 }
1152 };
1153}