My first real Go project (a messaging library) uses channels to pass data between arbitrary user goroutines and internal goroutines that use a single-threaded C library (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 unblock the other end, give it an `error` and make sure it doesn't block again later?
For receivers I can close the data channel. For the `error` I could pass `struct { data, error }`, or set a struct `error` field, or use a second `chan error`. Pros & cons? Other ideas?
For senders I can't close without a panic. Is this the right way to handle it?
select {
case sendChan <- data: sentOk()
case err := <- errChan: oops(err)
}
Send on a closed channel also panics so I need an `error` field as well, so code can check for error before attempting to write.
Is there a common pattern for how to organize all this? Other approaches I've missed?
2. Exposing channels in APIs.
Should the channels be exported or hidden in methods? There is a tradeoff, and I don't have the experience to evaluate it:
Exposing channels lets users select directly, but they must implement the error handling patterns of 1. correctly which seems complex and error-prone.
I can hide the channels in blocking methods that return `error`. This is simpler for simple use, but an async user has to wrap it in their own goroutine loops and error channels, which just duplicates what is already in the library! That seems a bit silly and adds overhead. The overhead may be small but this is my critical path.
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 can fit all cases well.
The standard net.Conn uses blocking methods, and I wrote goroutines with data error channels to pump data to my internal goroutines. It can be done, but I did not find it trivial. I would have liked net.Conn to provide a `chan []buf` interface for me. On the other hand I'd hate if net.Conn *only* had a channel interface and no simple Read/Write interface. So maybe both is the answer.
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