proposal: better channel semantics #31152
Labels
FrozenDueToAge
LanguageChange
Suggested changes to the Go language
Proposal
v2
An incompatible library change
Milestone
Goroutines and Go channels are phenomenally powerful, and even relatively easy to use for simple programs. The fact that some limited deadlock detection is built-in is a tremendous win for concurrent applications! However, I think there are ways to improve channel semantics for some really common use cases, and I'd like to know what people think about what the channel API could look like for Go v2.0.
First, I observe that channels can act like iterators in Go, providing a relatively convenient syntax for achieving stream-like data and computation flows. The thing is, I often want to insert an element into a channel without actually getting blocked for a recipient to read it. So I end up doing a lot of
go func() { ... }()
wrapping around channel sends just to present the overall program in a somewhat linear fashion. I would like a syntax for sending messages to channels that does not block, as opposed to the<-
operator.I know that I can declare a buffer size for my channels, but that's not particularly helpful for resolving the problem of asynchronous sending. In some cases, I won't know ahead of time exactly how many messages I need to send. A function can pass me a writable channel, where I don't have the option to declare a buffer size. Finally, it is also common for the receiver of the channel to die, garbage collect its end of the channel, or otherwise be in a state where it is not consuming messages / unblocking my program counter. As I build longer and deeper channel combinations, the probability of a deadlock due to over- or underaggressive reaction to error or done states, rises to the level of inescapable deadlock, somewhere lost in the woods.
Perhaps the biggest problem that I'm encountering with Go channels, is nested back propagation, where I need to dedicate specific channels for signaling an early termination error / done signal. Just one level deep can lead to unit tests stalling, because some done signal wasn't sent, or worse, was sent to a goroutine that had already left the party. Could there be a way to detect when a channel has closed, so that the listener no longer tries to send/receive messages on it?
Furthermore, what to do when you are trying to send a done signal, and then the done recipient dies? Would be nice for Go to provide some sort of indication that the message wasn't sent. Though of course we shouldn't use error channels to relay this information, as that just moves the problem! Hmm, maybe I need to stop writing so many asynchronous functions and just write blocking functions with an error return signature :P
Not to mention that a channel for signaling done is a common need, with few good conventions.
chan bool
begs the question, what would receivingfalse
even mean? Andchan struct{}
is quite ugly. At the very least, we could get some decent aliases in the stdlib:type Done = struct{}
,type DoneMessage = struct{}{}
. If not the aforementioned method for querying when a channel has closed, which would be best of all!Done channels, data channels, and error channels are reasonably good to bundle into a reusable structure, instead of having to pass these around individually. Though of course this would only be practical for the stdlib were Generics introduced. Hmm, maybe a convention of using
chan (error, bool, <your-type-here>)
for error, done status, successful result?Just some thoughts as I get my head around some gnarly concurrent apps.
The text was updated successfully, but these errors were encountered: