home

Prematurely Blocking Producers in Go

Apr 04, 2015

One of the nice things about Go is the ease with which we can control our work queue and concurrent processing. Specifically, writing to a drained channel blocks until a consumer is available:

for {
  result := doWork()
  notify <- result
}

In the above, writing result to the notify channel blocks once the channel's buffer is full and then until a consumer is freed.

The problem I ran into today was that I didn't want doWork to execute until I was sure that writing to notify would not block. In the past, I've advocated the use of Condition Variables for these types of problem. Generally speaking, I like using synchronization primitives for control flow and channels for data flow. Still, given that I already had a channel, I couldn't help but wonder if I could somehow leverage it.

The solution I came up with is, to me, stupid and clever. I run into these types of things every now and again and it's always an interesting feeling which I feel compelled to share it. For one thing, I half expect someone to either point out that it doesn't work or point out that there's a much better and normal solution (but how would I know without exposing my stupidity?!):

for {
  notify <- nil
  result := doWork()
  notify <- result
}

The above code requires consumers to discard any nil messages, which is definitely unfortunate. It works by moving our block before we do any work. Once we know that we're able to write a nil result, we know that we'll be able to write our real result (again, assuming the consumer quickly discards the nil result and moves on to the next real result).