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 } 1202