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