Go OR-Done-Channel Pattern

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

Reading from a channel until the channel is closed can be done by ranging over a channel:

for v := range c {
  // do something with the channel value, we exit the loop automatically when the channel is closed
  ...
}

The for/range syntax is simple and intuitive, but it cannot be used anymore if we want to make the loop preemptable by using a done channel. In that case we need to replace it with a for/select syntax and case for the state of the done channel (see an example here).

The equivalent syntax is:

for v := range orDone(done, c) {
  // do something with the channel value, we exit the loop automatically when the 'c' channel is closed
  // or when the 'done' channel is messaged (written onto or closed)
  ...
}

orDone()

// orDone iterates over the input channel 'in', sending the incoming elements to the output channel returned
// as the result of the function, until the 'done' channel is messaged (written onto or closed).
func orDone(done <-chan interface{}, in <-chan interface{}) <-chan interface{} {
	var out = make(chan interface{})
	go func() {
		defer close(out)
		for {
			select {
			case <-done:
				// the 'done' channel was messaged, exit the closure, which will close the output channel
				return
			case v, inputChanStillOpen := <-in:
				if !inputChanStillOpen {
					// input channel closed, exit the closure, which will close the output channel
					return
				}
				out <- v
			}
		}
	}()
	return out
}