Skip to content
Kavachकवच

Channels

Also known as: queue message passing producer consumer pipe stream mpsc buffer

● Advanced5 minBefore this: TasksSpec 9.4

A channel passes values from one task to another, in order. put adds a value, take takes the next, and close says no more will come.

const (send, receive) = Channel<T>(capacity: n)
syntax: Kavach code
fn main() {
    const (send, receive) = Channel<Int>(capacity: 4)
    const _producer = spawn {
        send.put(1)
        send.put(2)
        send.close()
    }
    for n in receive {
        print(n)
    }
}

The program ran and printed:

kavach run pipe.kav

12
pipe.kav: Kavach code, then what running it prints
  • put waits when the channel already holds capacity values: a fast producer can't run ahead and fill memory.
  • for n in receive takes values until the channel is closed and empty.
  • Each value is copied into the channel, so the two tasks never share it.

Channels are how tasks talk without sharing anything: one task produces, another consumes, and neither waits for a lock.

Putting without waiting

send.try_put(v) adds v if the channel has room and gives true. When it is full or closed, it gives false at once, and v is let go:

fn main() {
    const (send, receive) = Channel<Int>(capacity: 2)
    for n in 1..=3 {
        print("try_put({n}): {send.try_put(n)}")
    }
    send.close()
    print("try_put(4) after close: {send.try_put(4)}")
    for n in receive {
        print("took {n}")
    }
}

The program ran and printed:

kavach run try-put.kav

try_put(1): truetry_put(2): truetry_put(3): falsetry_put(4) after close: falsetook 1took 2
try-put.kav: Kavach code, then what running it prints

It never waits, so one slow taker can't hold up a task that sends to many, such as a server sending news to every listener.

tested

When the receivers are gone

A receiver is gone once nothing holds it: every copy of the Receiver was let go, as when the task that held it ended. Then try_put gives false, so a task that sends to many, such as a chat room, sees at once that a member left:

/// Joins the room, takes `wants` messages, then leaves.
fn member(joins: Sender<Sender<String>>, wants: Int) -> [String] {
    const (send, receive) = Channel<String>(capacity: 4)
    joins.put(send)
    let got: [String] = []
    for _ in 1..=wants {
        match receive.take() {
            Taken.Item(m) => got.push(m),
            Taken.Closed => return got,
        }
    }
    got
}

fn main() ! Error {
    const (joins, joined) = Channel<Sender<String>>(capacity: 2)
    const ann = spawn member(joins, 2)
    const bob = spawn member(joins, 1)
    let members: [Sender<String>] = []
    for _ in 1..=2 {
        match joined.take() {
            Taken.Item(s) => members.push(s),
            Taken.Closed => panic("nobody joined"),
        }
    }
    members = members.filter(m => m.try_put("news 1"))
    print("bob got {try bob.wait()}")
    members = members.filter(m => m.try_put("news 2"))   // bob left: his channel takes nothing
    print("{members.len()} member left")
    print("ann got {try ann.wait()}")
}

The program ran and printed:

kavach run receivers-gone.kav

bob got ["news 1"]1 member leftann got ["news 1", "news 2"]
receivers-gone.kav: Kavach code, then what running it prints

members.filter(m => m.try_put(…)) keeps the members that took the message: it drops those who left, and those too slow to keep up. A put nobody could ever take panics, and so does a put waiting for room when the last receiver goes:

fn main() {
    const send = {
        const (send, _receive) = Channel<Int>(capacity: 4)
        send
    }
    print(send.try_put(1))
    send.put(2)
    print("not reached")
}

The program ran, then stopped with a panic:

kavach run put-nobody-takes.kav

falsepanic: `put` into a channel whose receivers are all gone
put-nobody-takes.kav: Kavach code, then what running it prints

A channel's ends live to the end of their block, so the receiver above is gone once its block ends.

Limits and sizes

WhatLimit or sizeSource
Channel capacity1 or more; put waits while the channel is full09-concurrency.md
A task's stack512 MB of address space each, used only as calls need itlib.rs

Every limit in Kavach, on one page: Limits and Sizes.

One page for each form