Skip to content

std/select ​

Wait on the first ready of many sources, like Go's select. Arm channel receives and sends, fd reads and writes, or a timeout, then wait parks until one fires and returns the winning arm's index. The value of a receive arm is fetched afterwards with ch.recv() or tryRecv(), which keeps Select non-generic; that is also why only single-consumer channels are supported.

On a program that never spawned a green task there is no scheduler, so a timeout arm never fires. The helpers in std/timer call ensureTimersLive() first.

milo
from "std/select" import { Select }

API reference ​

Select ​

milo
pub struct Select

Owns its runtime state, so the pointer fields make it move-tracked (no @copy), and frees it in Drop, so no Select value outlives its state (an explicit destroy(&self) could be called twice, or followed by another wait()).

Select.new ​

milo
fn Select.new(): Select

Select.onRead ​

milo
fn Select.onRead<T: AsFd>(self: &mut Select, h: &T): void

Arm on h becoming readable. The arm holds only the descriptor NUMBER until wait() returns (a borrow cannot keep h alive past this call), so the caller must keep h open across the wait: closing it first arms whatever descriptor reuses the number. That is a wrong wakeup, not a memory error; channel arms keep an owner.

Select.onRecv ​

milo
fn Select.onRecv<T>(self: &mut Select, ch: &Channel<T>): void

Arm on a value becoming receivable from ch. The arm keeps its own reference to the channel, so dropping ch before wait() is safe.

Select.onSend ​

milo
fn Select.onSend<T>(self: &mut Select, ch: &Channel<T>): void

Arm on ch having room for a send.

Select.onTimeout ​

milo
fn Select.onTimeout(self: &mut Select, ms: i64): void

Select.onWrite ​

milo
fn Select.onWrite<T: AsFd>(self: &mut Select, h: &T): void

Arm on h accepting a write without blocking.

Select.wait ​

milo
fn Select.wait(self: &mut Select): i64

Arm every source, park until one fires, tear down the rest, return the winning arm index.