channel

Pass messages between tasks, in order: a queue that one task puts values on and another takes them off.

Oxigen
introduce channel
FunctionSignatureReturns
newnew()a channel
sendsend(ch, value)None
recvrecv(ch, timeout_ms?)the next value, or None
try_recvtry_recv(ch)the next value, or None
closeclose(ch)None
lenlen(ch)int
is_closedis_closed(ch)bool

A channel is a handle, an int, so it crosses into a diverge task like any other value, and every task that has it uses the same queue. A value sent is copied, the same way task arguments and results are, so anything that can cross threads can be sent: numbers, strings, collections, structs and enums. Functions cannot.

A worker that processes jobs in the order they arrive:

Oxigen
introduce channel

jobs := channel.new()

worker := diverge {
    done := 0
    repeat when True {
        job := channel.recv(jobs)
        stop when job == None          // closed and empty
        println("speaking: " + job)
        done = done + 1
    }
    done
}

each line in ["hello", "how are you", "goodbye"] {
    channel.send(jobs, line)
}
channel.close(jobs)
println(converge worker)   // 3

Channels need the threads capability, like diverge.


#new()

A new, empty channel. It has no size limit: send never waits.

#send(ch, value)

Put value at the end of the queue. Sending on a closed channel is an error.

#recv(ch, timeout_ms?)

Take the value at the front of the queue, waiting for one if the queue is empty. Returns None once the channel is closed and empty, so a loop ends when the sender closes it. With timeout_ms, it gives up after that many milliseconds and returns None:

Oxigen
introduce channel

ch := channel.new()
println(channel.recv(ch, 100))   // None, after 100 ms

None can also be sent as a value. A channel that carries None needs is_closed to tell the two apart.

#try_recv(ch)

recv without waiting: the next value if one is queued, or None at once. For checking a channel from inside a loop that has other work:

Oxigen
introduce channel

ch := channel.new()
channel.send(ch, "stop")
println(channel.try_recv(ch))   // stop
println(channel.try_recv(ch))   // None

#close(ch)

Stop the channel taking new values. Receivers waiting on it wake up. Values already queued can still be received; after them, recv returns None.

#len(ch)

How many values are queued.

#is_closed(ch)

Whether close has been called.


#Signals

os.signals returns a channel that receives Ctrl-C and other signals as strings, so a program can clean up before it exits:

Oxigen
introduce os
introduce channel

stop := os.signals(["INT", "TERM"])
speaker := os.spawn("say", [])
repeat when channel.try_recv(stop) == None {
    // ... feed the speaker ...
}
os.kill(speaker)