Channel Select - fthoms/jiminy GitHub Wiki
Often you will need to receive messages from multiple channels at once. Since Receive()
is a blocking operation, Jiminy
provides a means to Select
between channels. Based on which channel has a message waiting, the associated message handler is invoked.
In the case of non-blocking selects, there is also a default handler.
This starts by creating a channel selector, which we provide with cases to select from:
var err = Channel.Select()
.Case(chan1, m => Console.WriteLine("channel 1"))
.Case(chan2, m => Console.WriteLine("channel 2"))
.Receive();
Calling Receive()
will block until
- there is a message on one of the channels, in which case the associated handler is invoked and
null
is returned, or - the channel is closed with no unconsumed messages, in which case an error is returned, or
- either of the handlers throws an exception, in which case the exception message is returned in the
Error
The following code illustrates this more thoroughly:
sealed class Select : IExample {
public void Run() {
var done = Channel.Make<bool>();
Task.Run(() => Receive(MakeChan("first"), MakeChan("second"), MakeChan("third"), done));
done.Receive();
Console.WriteLine("done");
}
IChannel<string> MakeChan(string name) {
var chan = Channel.Make<string>();
Task.Run(() =>
{
for(var i = 0; i < 5; i++) {
chan.Send($"{name} {i}");
}
chan.Close();
});
return chan;
}
void Receive(IChannel<string> ch1, IChannel<string> ch2, IChannel<string> ch3, IChannel<bool> done) {
var error = Error.Nil;
while (error == null) {
error = Channel.Select()
.Case(ch1, m => Console.WriteLine(m))
.Case(ch2, m => Console.WriteLine(m))
.Case(ch3, m => Console.WriteLine(m))
.Receive();
}
done.Send(true);
}
}
Non-blocking select is an operation that will execute the default action if there are no messages available on any of the channels, either because one, some or all channels are closed, or because there are no waiting messages at the time of execution.
The following code will print "no messages"
to the console:
sealed class ChannelDefaultSelect : IExample {
public void Run() {
var chan1 = Channel.Make<int>();
var chan2 = Channel.Make<int>();
var err = Channel.Select()
.Case(chan1, _ => Console.WriteLine("channel 1"))
.Case(chan2, _ => Console.WriteLine("channel 1"))
.Otherwise(() => Console.WriteLine("no messages"));
}
}
Again, err
is null
if any of the handlers succeed. One key difference with blocking select is that err
is null
even if all channels are closed (in which case the default handler is invoked).