2021/04/18
mtqueue and channel
Thread-safe queues, <mtqueue>
(ref:data.queue), can naturally
be used as a ``channel'', a communication primitive between producer
thread(s) and consumer thread(s). In fact, I totally assumed
the two were equivalent, and didn't bother creating a ``channel''
datatype specifically.
But I realized there was one difference--a channel can be closed.
Suppose I have an in-process ``server''---a thread looping over
requests sent via an mtqueue. Other part of the program inserts
its requests with enqueue/wait!
. The server thread reads
it and process it. All the synchronization is done in the queue,
and that's the beauty.
Now, sometimes, you may want to shut down such server. Once it is shut down, we don't want to allow callers to put a new request into the queue, for it will sit in the queue unprocessed forever. How to implement it?
We may have a separate flag in our <server>
object and
ask the caller to check it before queuing a request. But such check
must be done atomically with queue insertion, for other thread
may set the flag after you checked it but before calling enqueue.
Hence you need a separate lock for the flag and the queue, even
the queue itself is thread-safe. It's kind of waste.
With a channel, attempt to put a request on a closed channel would be rejected, and that check is done atomically inside a channel.
For Gauche, I decided to enhance <mtqueue>
to have ``close''
state. It's a bit of divergence from a queue in the original sense,
but it's simpler than making a separate channel class on top of
<mtqueue>
. The lock operation and flag check is intertwined
so deeply that it's difficult to separate them cleanly.
An mtqueue can only be closed via the synchronized queue operations
such as enqueue/wait!
. Usually, if you want to shut down the service, you need a special message, so it's reasonable that you close
the queue simultaneously when you send such termination message.
(If you think a channel as a pipe, then such termination message is
not necessary; the input end can be simply closed, and the output end
reads something like #<eof>
. Queue, on the other hand, is
expected to read something that's explicitly put.)
The feature is available in the next release, 0.9.11.
Tags: data.queue, mtqeueue, Concurrency
Post a comment