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