Skip to content

Channels

Channels provide message passing between tasks.

Basic Usage

wyn
ch = Task.channel(10)       // buffered channel, capacity 10

Task.send(ch, 42)
Task.send(ch, 100)

first = Task.recv(ch)       // 42
second = Task.recv(ch)      // 100

Producer / Consumer

wyn
fn producer(ch: int, n: int) -> int {
    for i in 0..n {
        Task.send(ch, i * i)
    }
    return 0
}

fn main() -> int {
    ch = Task.channel(100)
    f = spawn producer(ch, 10)

    for i in 0..10 {
        print(Task.recv(ch).to_string())
    }
    await f
    return 0
}

Messages are received in FIFO order.

Select (Multiple Channels)

Wait on multiple channels simultaneously using Task.select_2 or Task.select_3. Returns the 0-based index of the first channel with data.

wyn
fn sender(ch: int, value: int, delay_ms: int) {
    Time.sleep(delay_ms)
    Task.send(ch, value)
}

ch1 = Task.channel(8)
ch2 = Task.channel(8)

// In another coroutine, someone sends to ch2
spawn sender(ch2, 42, 100)

// Wait for either channel
ready = Task.select_2(ch1, ch2)
if ready == 0 {
    val = Task.recv(ch1)
    print("got from ch1: " + val.to_string())
} else if ready == 1 {
    val = Task.recv(ch2)
    print("got from ch2: " + val.to_string())
}
  • Task.select_2(ch1, ch2) - wait on 2 channels
  • Task.select_3(ch1, ch2, ch3) - wait on 3 channels
  • Returns -1 if all channels are closed
  • Inside a coroutine, select yields instead of blocking

Draining Until Closed

When you don't know how many messages will arrive, close the channel from the producer and drain it with select. Task.select_2 returns -1 once every channel it watches is closed and empty, which gives the consumer a clean exit:

wyn
fn producer(ch: int) -> int {
    for i in 0..5 {
        Task.send(ch, i * 10)
    }
    Task.close(ch)
    return 0
}

fn main() -> int {
    ch = Task.channel(8)
    spawn producer(ch)

    while true {
        ready = Task.select_2(ch, ch)   // -1 when ch is closed and drained
        if ready == -1 { break }
        print(Task.recv(ch).to_string())
    }
    return 0
}

Note: a receive on a drained channel returns 0, which is indistinguishable from a sent 0. Prefer reading a known count of messages, or draining with select as above, rather than looping on the received value.

See Also

MIT License - v1.21.0