Skip to main content

go_lib/
go_macro.rs

1// SPDX-License-Identifier: Apache-2.0
2//! `go!` and `select!` macros — public spawn/multiplex syntax.
3//!
4//! ## `go!`
5//!
6//! Spawns a closure as a new goroutine.  Equivalent to Go's `go f()`.
7//!
8//! ```no_run
9//! go_lib::run(|| {
10//!     go_lib::go!(|| println!("hello from goroutine"));
11//! });
12//! ```
13//!
14//! ## `select!`
15//!
16//! Multiplexes channel operations, picking the first ready case at random
17//! (Go's fairness guarantee).  Syntax mirrors Go's `select` statement:
18//!
19//! ```text
20//! select! {
21//!     recv(rx)     -> v => { /* v: Option<T> */ }
22//!     send(tx, val)    => { /* val was sent    */ }
23//!     default          => { /* nothing ready   */ }
24//! }
25//! ```
26//!
27//! - Recv arms bind the variable name given after `->` as `Option<T>`:
28//!   `Some(v)` on a normal receive, `None` if the channel was closed.
29//! - Send arms use `ManuallyDrop<T>` internally; the value is consumed when
30//!   the arm wins and dropped when the arm loses.
31//! - `default` makes the select non-blocking (taken when no other arm fires).
32//! - Without `default`, `select!` blocks until at least one case is ready.
33//!
34//! Arms may appear in any order. At most 4 recv and 2 send arms are supported
35//! per invocation.
36
37/// Spawn a closure as a new goroutine.
38///
39/// # Example
40///
41/// ```no_run
42/// go_lib::run(|| {
43///     go_lib::go!(|| {
44///         println!("running in a goroutine");
45///     });
46/// });
47/// ```
48#[macro_export]
49macro_rules! go {
50    ($body:expr) => {{
51        $crate::__spawn($body)
52    }};
53}
54
55// ---------------------------------------------------------------------------
56// Internal helper — shared dispatch pattern for recv-only selects.
57//
58// The macro_rules below are explicit-rule based (not tt-munching) so the
59// generated code is always in a single hygiene scope, giving each arm its
60// own uniquely-named stack slot without any counting trick.
61// ---------------------------------------------------------------------------
62
63/// Multiplex channel operations.
64///
65/// See [module-level documentation][crate::go_macro] for full syntax and
66/// semantics.
67///
68/// # Example — nonblocking recv with default
69///
70/// ```no_run
71/// use go_lib::chan::chan;
72/// go_lib::run(|| {
73///     let (tx, rx) = chan::<i32>(1);
74///     tx.send(42);
75///     go_lib::select! {
76///         recv(rx) -> v => {
77///             println!("received {:?}", v);
78///         }
79///         default => {
80///             println!("nothing ready");
81///         }
82///     }
83/// });
84/// ```
85#[macro_export]
86macro_rules! select {
87
88    // ─── A1: single recv, blocking ────────────────────────────────────────────
89    ( recv($r:expr) -> $v:ident => $b:block $(,)? ) => {{
90        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
91        let mut __sel = ::std::vec![
92            $crate::select::recv_case_of(&($r), &mut __r0),
93        ];
94        $crate::select::selectgo(&mut __sel, false);
95        let $v = __r0;
96        $b
97    }};
98
99    // ─── B1: single recv + default ────────────────────────────────────────────
100    ( recv($r:expr) -> $v:ident => $b:block $(,)? default => $d:block $(,)? ) => {{
101        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
102        let mut __sel = ::std::vec![
103            $crate::select::recv_case_of(&($r), &mut __r0),
104        ];
105        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
106        if __idx == 0 {
107            let $v = __r0;
108            $b
109        } else { $d }
110    }};
111
112    // ─── A2: two recv, blocking ───────────────────────────────────────────────
113    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
114      recv($r2:expr) -> $v2:ident => $b2:block $(,)? ) => {{
115        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
116        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
117        let mut __sel = ::std::vec![
118            $crate::select::recv_case_of(&($r1), &mut __r0),
119            $crate::select::recv_case_of(&($r2), &mut __r1),
120        ];
121        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
122        if __idx == 0 {
123            let $v1 = __r0;
124            $b1
125        } else {
126            let $v2 = __r1;
127            $b2
128        }
129    }};
130
131    // ─── B2: two recv + default ───────────────────────────────────────────────
132    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
133      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
134      default => $d:block $(,)? ) => {{
135        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
136        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
137        let mut __sel = ::std::vec![
138            $crate::select::recv_case_of(&($r1), &mut __r0),
139            $crate::select::recv_case_of(&($r2), &mut __r1),
140        ];
141        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
142        if __idx == 0 {
143            let $v1 = __r0;
144            $b1
145        } else if __idx == 1 {
146            let $v2 = __r1;
147            $b2
148        } else { $d }
149    }};
150
151    // ─── A3: three recv, blocking ─────────────────────────────────────────────
152    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
153      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
154      recv($r3:expr) -> $v3:ident => $b3:block $(,)? ) => {{
155        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
156        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
157        let mut __r2: ::std::option::Option<_> = ::std::option::Option::None;
158        let mut __sel = ::std::vec![
159            $crate::select::recv_case_of(&($r1), &mut __r0),
160            $crate::select::recv_case_of(&($r2), &mut __r1),
161            $crate::select::recv_case_of(&($r3), &mut __r2),
162        ];
163        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
164        if __idx == 0 {
165            let $v1 = __r0;
166            $b1
167        } else if __idx == 1 {
168            let $v2 = __r1;
169            $b2
170        } else {
171            let $v3 = __r2;
172            $b3
173        }
174    }};
175
176    // ─── B3: three recv + default ─────────────────────────────────────────────
177    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
178      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
179      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
180      default => $d:block $(,)? ) => {{
181        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
182        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
183        let mut __r2: ::std::option::Option<_> = ::std::option::Option::None;
184        let mut __sel = ::std::vec![
185            $crate::select::recv_case_of(&($r1), &mut __r0),
186            $crate::select::recv_case_of(&($r2), &mut __r1),
187            $crate::select::recv_case_of(&($r3), &mut __r2),
188        ];
189        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
190        if __idx == 0 {
191            let $v1 = __r0;
192            $b1
193        } else if __idx == 1 {
194            let $v2 = __r1;
195            $b2
196        } else if __idx == 2 {
197            let $v3 = __r2;
198            $b3
199        } else { $d }
200    }};
201
202    // ─── A4: four recv, blocking ──────────────────────────────────────────────
203    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
204      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
205      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
206      recv($r4:expr) -> $v4:ident => $b4:block $(,)? ) => {{
207        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
208        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
209        let mut __r2: ::std::option::Option<_> = ::std::option::Option::None;
210        let mut __r3: ::std::option::Option<_> = ::std::option::Option::None;
211        let mut __sel = ::std::vec![
212            $crate::select::recv_case_of(&($r1), &mut __r0),
213            $crate::select::recv_case_of(&($r2), &mut __r1),
214            $crate::select::recv_case_of(&($r3), &mut __r2),
215            $crate::select::recv_case_of(&($r4), &mut __r3),
216        ];
217        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
218        if __idx == 0 {
219            let $v1 = __r0;
220            $b1
221        } else if __idx == 1 {
222            let $v2 = __r1;
223            $b2
224        } else if __idx == 2 {
225            let $v3 = __r2;
226            $b3
227        } else {
228            let $v4 = __r3;
229            $b4
230        }
231    }};
232
233    // ─── B4: four recv + default ──────────────────────────────────────────────
234    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
235      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
236      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
237      recv($r4:expr) -> $v4:ident => $b4:block $(,)?
238      default => $d:block $(,)? ) => {{
239        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
240        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
241        let mut __r2: ::std::option::Option<_> = ::std::option::Option::None;
242        let mut __r3: ::std::option::Option<_> = ::std::option::Option::None;
243        let mut __sel = ::std::vec![
244            $crate::select::recv_case_of(&($r1), &mut __r0),
245            $crate::select::recv_case_of(&($r2), &mut __r1),
246            $crate::select::recv_case_of(&($r3), &mut __r2),
247            $crate::select::recv_case_of(&($r4), &mut __r3),
248        ];
249        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
250        if __idx == 0 {
251            let $v1 = __r0;
252            $b1
253        } else if __idx == 1 {
254            let $v2 = __r1;
255            $b2
256        } else if __idx == 2 {
257            let $v3 = __r2;
258            $b3
259        } else if __idx == 3 {
260            let $v4 = __r3;
261            $b4
262        } else { $d }
263    }};
264
265    // ─── C1: single send + default (nonblocking send) ─────────────────────────
266    ( send($tx:expr, $sv:expr) => $sb:block $(,)? default => $d:block $(,)? ) => {{
267        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
268        let mut __sel = ::std::vec![
269            $crate::select::send_case_of(&($tx), &mut __s0),
270        ];
271        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
272        if __idx == 0 {
273            // Send won — value consumed, do NOT drop __s0.
274            $sb
275        } else {
276            // Default — drop the unsent value.
277            // SAFETY: __s0 was not consumed by selectgo (default taken).
278            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
279            $d
280        }
281    }};
282
283    // ─── D1: 1 recv + 1 send, blocking ───────────────────────────────────────
284    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
285      send($tx:expr, $sv:expr)    => $sb:block $(,)? ) => {{
286        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
287        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
288        let mut __sel = ::std::vec![
289            $crate::select::recv_case_of(&($r1), &mut __r0),
290            $crate::select::send_case_of(&($tx), &mut __s0),
291        ];
292        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
293        if __idx == 0 {
294            // SAFETY: recv won, send value was not consumed.
295            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
296            let $v1 = __r0;
297            $rb
298        } else {
299            // Send won — value consumed.
300            $sb
301        }
302    }};
303
304    // ─── D2: 1 recv + 1 send + default ───────────────────────────────────────
305    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
306      send($tx:expr, $sv:expr)    => $sb:block $(,)?
307      default                     => $d:block $(,)? ) => {{
308        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
309        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
310        let mut __sel = ::std::vec![
311            $crate::select::recv_case_of(&($r1), &mut __r0),
312            $crate::select::send_case_of(&($tx), &mut __s0),
313        ];
314        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
315        if __idx == 0 {
316            // SAFETY: recv won, send value was not consumed.
317            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
318            let $v1 = __r0;
319            $rb
320        } else if __idx == 1 {
321            $sb
322        } else {
323            // SAFETY: default taken, send value was not consumed.
324            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
325            $d
326        }
327    }};
328
329    // ─── D3: 2 recv + 1 send, blocking ───────────────────────────────────────
330    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
331      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
332      send($tx:expr, $sv:expr)    => $sb:block $(,)? ) => {{
333        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
334        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
335        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
336        let mut __sel = ::std::vec![
337            $crate::select::recv_case_of(&($r1), &mut __r0),
338            $crate::select::recv_case_of(&($r2), &mut __r1),
339            $crate::select::send_case_of(&($tx), &mut __s0),
340        ];
341        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
342        if __idx == 0 {
343            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
344            let $v1 = __r0;
345            $b1
346        } else if __idx == 1 {
347            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
348            let $v2 = __r1;
349            $b2
350        } else {
351            $sb
352        }
353    }};
354
355    // ─── D4: 2 recv + 1 send + default ───────────────────────────────────────
356    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
357      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
358      send($tx:expr, $sv:expr)    => $sb:block $(,)?
359      default                     => $d:block $(,)? ) => {{
360        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
361        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
362        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
363        let mut __sel = ::std::vec![
364            $crate::select::recv_case_of(&($r1), &mut __r0),
365            $crate::select::recv_case_of(&($r2), &mut __r1),
366            $crate::select::send_case_of(&($tx), &mut __s0),
367        ];
368        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
369        if __idx == 0 {
370            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
371            let $v1 = __r0;
372            $b1
373        } else if __idx == 1 {
374            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
375            let $v2 = __r1;
376            $b2
377        } else if __idx == 2 {
378            $sb
379        } else {
380            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
381            $d
382        }
383    }};
384
385    // ─── D5: 3 recv + 1 send, blocking ───────────────────────────────────────
386    ( recv($r1:expr) -> $v1:ident => $b1:block $(,)?
387      recv($r2:expr) -> $v2:ident => $b2:block $(,)?
388      recv($r3:expr) -> $v3:ident => $b3:block $(,)?
389      send($tx:expr, $sv:expr)    => $sb:block $(,)? ) => {{
390        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
391        let mut __r1: ::std::option::Option<_> = ::std::option::Option::None;
392        let mut __r2: ::std::option::Option<_> = ::std::option::Option::None;
393        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv);
394        let mut __sel = ::std::vec![
395            $crate::select::recv_case_of(&($r1), &mut __r0),
396            $crate::select::recv_case_of(&($r2), &mut __r1),
397            $crate::select::recv_case_of(&($r3), &mut __r2),
398            $crate::select::send_case_of(&($tx), &mut __s0),
399        ];
400        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
401        if __idx == 0 {
402            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
403            let $v1 = __r0;
404            $b1
405        } else if __idx == 1 {
406            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
407            let $v2 = __r1;
408            $b2
409        } else if __idx == 2 {
410            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
411            let $v3 = __r2;
412            $b3
413        } else {
414            $sb
415        }
416    }};
417
418    // ─── D6: 1 recv + 2 send, blocking ───────────────────────────────────────
419    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
420      send($tx1:expr, $sv1:expr)  => $sb1:block $(,)?
421      send($tx2:expr, $sv2:expr)  => $sb2:block $(,)? ) => {{
422        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
423        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv1);
424        let mut __s1: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv2);
425        let mut __sel = ::std::vec![
426            $crate::select::recv_case_of(&($r1), &mut __r0),
427            $crate::select::send_case_of(&($tx1), &mut __s0),
428            $crate::select::send_case_of(&($tx2), &mut __s1),
429        ];
430        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, false);
431        if __idx == 0 {
432            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0); ::std::mem::ManuallyDrop::drop(&mut __s1) };
433            let $v1 = __r0;
434            $rb
435        } else if __idx == 1 {
436            // __s0 consumed; drop __s1
437            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s1) };
438            $sb1
439        } else {
440            // __s1 consumed; drop __s0
441            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
442            $sb2
443        }
444    }};
445
446    // ─── D7: 1 recv + 2 send + default ───────────────────────────────────────
447    ( recv($r1:expr) -> $v1:ident => $rb:block $(,)?
448      send($tx1:expr, $sv1:expr)  => $sb1:block $(,)?
449      send($tx2:expr, $sv2:expr)  => $sb2:block $(,)?
450      default                     => $d:block $(,)? ) => {{
451        let mut __r0: ::std::option::Option<_> = ::std::option::Option::None;
452        let mut __s0: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv1);
453        let mut __s1: ::std::mem::ManuallyDrop<_> = ::std::mem::ManuallyDrop::new($sv2);
454        let mut __sel = ::std::vec![
455            $crate::select::recv_case_of(&($r1), &mut __r0),
456            $crate::select::send_case_of(&($tx1), &mut __s0),
457            $crate::select::send_case_of(&($tx2), &mut __s1),
458        ];
459        let (__idx, _ok) = $crate::select::selectgo(&mut __sel, true);
460        if __idx == 0 {
461            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0); ::std::mem::ManuallyDrop::drop(&mut __s1) };
462            let $v1 = __r0;
463            $rb
464        } else if __idx == 1 {
465            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s1) };
466            $sb1
467        } else if __idx == 2 {
468            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0) };
469            $sb2
470        } else {
471            unsafe { ::std::mem::ManuallyDrop::drop(&mut __s0); ::std::mem::ManuallyDrop::drop(&mut __s1) };
472            $d
473        }
474    }};
475}
476
477// ---------------------------------------------------------------------------
478// Tests
479// ---------------------------------------------------------------------------
480
481#[cfg(all(test, not(loom)))]
482mod tests {
483    use crate::chan::chan;
484    use crate::runtime::sched::run_impl;
485    use std::sync::atomic::{AtomicI32, Ordering};
486    use std::sync::Arc;
487
488    // ── go! ───────────────────────────────────────────────────────────────────
489
490    /// go! spawns a goroutine that runs its closure.
491    #[test]
492    fn go_macro_spawns() {
493        let count = Arc::new(AtomicI32::new(0));
494        let c2    = Arc::clone(&count);
495        run_impl(move || {
496            go!(move || { c2.fetch_add(1, Ordering::Relaxed); });
497            // Yield enough times for the goroutine to run.
498            for _ in 0..50 { crate::gosched(); }
499        });
500        assert_eq!(count.load(Ordering::Acquire), 1);
501    }
502
503    // ── select! fast-path (no park) ───────────────────────────────────────────
504
505    /// B1: recv+default — data ready → recv arm taken.
506    #[test]
507    fn select_recv_default_data_ready() {
508        run_impl(|| {
509            let (tx, rx) = chan::<i32>(1);
510            tx.send(7);
511            select! {
512                recv(rx) -> v => { assert_eq!(v.unwrap(), 7); }
513                default      => { panic!("default should not fire"); }
514            }
515        });
516    }
517
518    /// B1: recv+default — channel empty → default taken.
519    #[test]
520    fn select_default_when_empty() {
521        run_impl(|| {
522            let (_tx, rx) = chan::<i32>(1);
523            select! {
524                recv(rx) -> _v => { panic!("should not recv"); }
525                default        => {} // default arm correctly taken
526            }
527        });
528    }
529
530    /// B2: two recv + default — first channel has data.
531    #[test]
532    fn select_two_recv_first_ready() {
533        run_impl(|| {
534            let (tx1, rx1) = chan::<i32>(1);
535            let (_tx2, rx2) = chan::<i32>(1);
536            tx1.send(42);
537            select! {
538                recv(rx1) -> v  => { assert_eq!(v.unwrap(), 42); }
539                recv(rx2) -> _v => { panic!("rx2 should not fire"); }
540                default         => { panic!("unexpected default — rx1 should have been ready"); }
541            }
542        });
543    }
544
545    /// C1: send+default — buffer has space → send arm taken.
546    #[test]
547    fn select_send_default_space_available() {
548        run_impl(|| {
549            let (tx, rx) = chan::<i32>(1);
550            select! {
551                send(tx, 99_i32) => {} // send arm correctly taken
552                default          => { panic!("default should not fire"); }
553            }
554            assert_eq!(rx.recv(), Some(99));
555        });
556    }
557
558    /// C1: send+default — buffer full → default taken.
559    #[test]
560    fn select_send_default_buffer_full() {
561        run_impl(|| {
562            let (tx, rx) = chan::<i32>(1);
563            tx.send(1);   // fill the buffer
564            select! {
565                send(tx, 2_i32) => { panic!("should not send — buffer is full"); }
566                default         => {} // default arm correctly taken
567            }
568            assert_eq!(rx.recv(), Some(1));
569        });
570    }
571
572    /// D2: recv+send+default — recv channel has data, send buffer has space;
573    /// one of them fires, the other does not panic.
574    #[test]
575    fn select_recv_send_default() {
576        run_impl(|| {
577            let (tx1, rx1) = chan::<i32>(1);
578            let (tx2, rx2) = chan::<i32>(1);
579            tx1.send(10);
580            let mut recv_val = -1_i32;
581            let mut send_ok  = false;
582            // Both cases are ready; at least one fires.
583            select! {
584                recv(rx1) -> v  => { recv_val = v.unwrap(); }
585                send(tx2, 20_i32) => { send_ok = true; }
586                default         => {}
587            }
588            // At least one of recv_val or send_ok should have changed.
589            assert!(recv_val == 10 || send_ok);
590            let _ = rx2.try_recv(); // drain if sent
591        });
592    }
593
594    // ── select! blocking path ─────────────────────────────────────────────────
595
596    /// A1: single recv blocking — goroutine parks until sender fires.
597    #[test]
598    fn select_blocking_recv() {
599        let result = Arc::new(AtomicI32::new(-1));
600        let r2 = Arc::clone(&result);
601        run_impl(move || {
602            let (tx, rx) = chan::<i32>(0);
603            go!(move || { tx.send(55); });
604            select! {
605                recv(rx) -> v => { r2.store(v.unwrap(), Ordering::Relaxed); }
606            }
607        });
608        assert_eq!(result.load(Ordering::Acquire), 55);
609    }
610
611    /// A2: two recv blocking — whichever sender fires first wins.
612    #[test]
613    fn select_blocking_two_recv() {
614        let winner = Arc::new(AtomicI32::new(-1));
615        let w2 = Arc::clone(&winner);
616        run_impl(move || {
617            let (tx1, rx1) = chan::<i32>(0);
618            let (tx2, rx2) = chan::<i32>(0);
619            go!(move || { tx1.send(1); });
620            go!(move || { tx2.send(2); });
621            select! {
622                recv(rx1) -> v => { w2.store(v.unwrap(), Ordering::Relaxed); }
623                recv(rx2) -> v => { w2.store(v.unwrap(), Ordering::Relaxed); }
624            }
625        });
626        let w = winner.load(Ordering::Acquire);
627        assert!(w == 1 || w == 2, "winner should be 1 or 2, got {w}");
628    }
629
630    /// D1: recv+send blocking — one goroutine sends, one receives; select picks.
631    #[test]
632    fn select_blocking_recv_send() {
633        let recv_val = Arc::new(AtomicI32::new(-1));
634        let rv2 = Arc::clone(&recv_val);
635        run_impl(move || {
636            let (tx1, rx1) = chan::<i32>(0); // recv from this
637            let (tx2, rx2) = chan::<i32>(0); // send to this
638            // Goroutine that will satisfy the recv arm.
639            go!(move || { tx1.send(77); });
640            // Goroutine that drains if the send arm fires instead.
641            go!(move || {
642                // Give the main goroutine time to block.
643                crate::gosched();
644                let _ = rx2.recv();
645            });
646            select! {
647                recv(rx1) -> v      => { rv2.store(v.unwrap(), Ordering::Relaxed); }
648                send(tx2, 99_i32)   => {}
649            }
650        });
651        // Either recv gave us 77 or send fired (recv_val stays -1 → we got -1).
652        let v = recv_val.load(Ordering::Acquire);
653        assert!(v == 77 || v == -1, "unexpected value {v}");
654    }
655
656    /// recv from closed channel yields None via select.
657    #[test]
658    fn select_recv_closed_yields_none() {
659        run_impl(|| {
660            let (tx, rx) = chan::<i32>(0);
661            tx.close();
662            select! {
663                recv(rx) -> v => { assert!(v.is_none(), "should be None for closed channel"); }
664            }
665        });
666    }
667}