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