Gauche Devlog

< A curious case of rational to flonum conversion | Two concurrency utilities >

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

Name: