When is a channel consumer calling close! an anti-pattern?


#1

So in the core-async videos, the example shows the consumer calling close! to signal to the collection of assembly workers that they should stop producing cars.

In this respect, close! seems to work like a kind of super-fast back-pressure. Whereas backpressure says “woah that’s too fast”, close! says “that’s enough!”. It has the advantage of being final and acting instantly, in that you don’t need to wait for the buffer to fill before the effect is visible to the producer.

But then yesterday I got into a conversation on the Clojurians Slack that seemed to suggest this was the wrong way to think about. This Google Groups post by Brandon Bloom goes into more detail, and I followed up with him later. But the essential argument is that a channel should be an abstraction for one-way communication, from producer to consumer. As such, only the producer should ever close the channel. I believe that the Go language may enforce this restriction with the type system. On this view, closing a channel should semantically correspond to “nothing more to send, ever”, rather than a way for the consumer to say “no more, please”.

On this view, if you have a process that’s filling a channel with a potentially infinite stream of values (say by polling an external source), and you want to close the channel, then you should create a second, separate means of communication to tell the producer to close the channel. What Brandon recommended was creating a second channel, stop-chan, whose only purpose is for the consumer of the first channel to be able to signal to the producer of the first channel to close it. You send that message by closing stop-chan; you don’t even need to put a value in it.

This was a new pattern to me. I’m wondering if it’s typical of how people use core.async? Or of how they should use it? Or is it a golang-ism which is inconsistent with other aspects of core.async’s and clojure’s approach?


#2

Hi Alexis,

I think I disagree with Brandon Bloom. The design of Go was an inspiration, but not an authority on core.aync. I think closing a channel on the consumer side is fine.

But, it does bring up another issue, which is that you definitely need to figure out, in each particular case, what it means to have a closed channel. Does it mean stop producing? Does it mean stop consuming? Who’s able to close? Those are all questions you should ask and find answers to.

And sometimes, when you answer those questions, you realize that it wouldn’t work for a consumer to close the channel, but you still need to signal the producer, like in your case, you can create an explicit stop channel that the consumer closes. But in many cases, closing from the consumer side is fine.

Eric