channel
Pass messages between tasks, in order: a queue that one task puts values on and another takes them off.
introduce channel| Function | Signature | Returns |
|---|---|---|
new | new() | a channel |
send | send(ch, value) | None |
recv | recv(ch, timeout_ms?) | the next value, or None |
try_recv | try_recv(ch) | the next value, or None |
close | close(ch) | None |
len | len(ch) | int |
is_closed | is_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:
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) // 3Channels 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:
introduce channel
ch := channel.new()
println(channel.recv(ch, 100)) // None, after 100 msNone 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:
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:
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)