Sometimes a go programmer will wish for an infinitely buffered channel. Go does not offer any such construct, though by creating two channels and a goroutine to move data between them, it is possible to have infinitely buffered channel semantics.
Sometimes what people actually want is a channel that never blocks and never forgets. This isn’t quite the same as a channel with an infinite buffer. I didn’t mention anything about preserving the original message order, for one.
What you can do in the situation I describe is use something I’ve been calling a “stacked” channel. It’s a buffered channel with a special send operation and whose value type has a meaningful “join” function.
type Thing anything
func JoinThings(thing1, thing2 Thing) (thing3 Thing) { ... }
type ThingChan chan Thing
func NewThingChan() ThingChan {
return make(ThingChan, 1)
}
func (tch ThingChan) Stack(thing1 Thing) {
for {
select {
case tch <- thing1:
return
case thing2 := <- tch:
thing1 = JoinThings(thing1, thing2)
}
}
}
If it’s not immediately clear how stacking works, take a minute to try to figure it out before reading the explanation below.
The stacked channel allows non-blocking sending. That is, whenever a goroutine wants to send to this channel using the .Stack() method, it will complete quickly (provided that the join function completes quickly).
This non-blocking occurs because when the select{} statement is executed, there are two possible states for the channel. Either its buffer is full or its buffer is empty. If the buffer is empty, the new value will be put in immediately. If the buffer is full, the value will be picked off, combing with the new value and put back on.
It is possible that another goroutine stacks something in the meantime, and it will have to pick off another value to join and put back. With a fair scheduler, every goroutine attempting to stack will make progress quickly, relative to the the number of goroutines in contention. This progress comes about because every time a goroutine has to loop back and try again, there must have been another goroutine that succeeded in leaving a value on the channel.
This stacking technique has definite real-project application. I work for a private corporation on a public project called skynet. With skynet, client programs need to be notified about new services as they come available. Sometimes these notifications can come faster than a client can ask about them, and they stack up. We use a stacked channel to collect the notifications into bundles without stalling out the notification system.
Here’s a play example: http://play.golang.org/p/MEp87YesU6