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