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] 24 macro_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] 1160 macro_rules! select { 1161 ($($tokens:tt)*) => { 1162 $crate::crossbeam_channel_internal!( 1163 $($tokens)* 1164 ) 1165 }; 1166 } 1167