I am working on my first real Go project, a messaging API. I use channels to pass messages and other data between user goroutines and library goroutines that use a thread-unsafe, event-based C protocol library. For details https://github.com/apache/qpid-proton/blob/master/proton-c/bindings/go/README.md
My question is in 2 related parts:
1. What are common idioms for handling errors across channels?
The goroutine at one end blows up, how do I ensure the other end unblocks, gets an error
value and doesn't get blocked again later?
For readers:
- I can close the channel, but no error info.
- I could pass a
struct { data, error }
- or use a second channel.
Pros & cons? Other ideas?
For writers: I can't close without a panic so I guess I need a second channel. Is this idiomatic?
select {
case sendChan <- data: sentOk()
case err := <- errChan: oops(err)
}
I also can't write after close so I need to store the error somewhere and check before trying to write. Any other approaches?
2. Exposing channels in APIs.
I need channels to pass error info: should I make those channels public fields or hide them in methods?
There is a tradeoff, and I don't have the experience to evaluate it:
Exposing channels lets users select directly, but it requires them to correctly impement the error handling patterns (check for errors before write, select for error as well as write). This seems complex and error-prone but maybe that because I'm not seasoned in go.
Hiding channels in a method simplifies and enforces correct use of the library. But now an async user must create their own goroutine and channel(s). They may just duplicate what the library does already, which is silly. Also there is an extra goroutine and channel on the path. Maybe that's not a big deal, but the data channel is the critical path for my library and I think it has to be hidden along with the error channel.
I could do both: expose the channels for power users and provide a simple method wrapper for people with simple needs. That's more to support but worth it if neither alone can fit all cases.
The standard net.Conn uses blocking methods, not channels, and I wrote goroutines to pump data to my C event-loop channel so I know it can be done, but I did not find it trivial. net.Conn is wrapping sytem calls not channels underneath so "exposing the channels" is not an option. Do any of the standard libraries export channels with error handling? (time.After doesn't count, there are no errors)
Thanks a lot! Alan