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