New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: Go 2: return error from close #30916
Comments
Your example doesn't seem very illustrative, as there is no reason to call The only reason to ever call |
I think this is more a consequence of unfortunate naming. Maybe something like
That's for sure. I've had quite a few times, especially when doing 1:M stuff with channels, where I've at least thought that I should wrap some of the channel accesses in mutexes or I don't think making |
@ianlancetaylor Thank you for the kind suggestion, I will try to provide a more elaborate example. I was trying to extract as little as possible from my past experience of dealing with it. Main points I am trying to convey are that
Yes, I agree - that train have left the station long time ago. Naming it something like
Hehe, that's what I was thinking doing as well, but went a bit different direction due to unfamiliarity with Context back then. Used context for cancelation for a different part of the code later, when I finally wrapped my head about contexts a bit, but did not get to re-write of 1-N (fanout) messaging using this idea.
I think some seeds for that are there already - |
I agree that in retrospect I don't agree that |
We are not going to make this change. |
Thank you Ian for the quick turnaround. |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?All of them, but I tested on darwin (macOS)
go env
OutputWhat did you do?
Trying to close() a previously closed channel (closed from another goroutine), or closing nil channel. Yes, I know currently it is illegal under penalty of panic() or total lockout on writing into bad channel.
https://play.golang.org/p/Iyy31orWyca
What did you WANT to see?
This program to successfully finish always.
What did you see instead?
Sometimes it fails, sometimes it just normally finishes
Proposal
Add a compile-time override for
close(channel)
with signatureclose(channel) error
that returns an error instead of calling panic on previously closed ornil
channel. Define 2 errors - "channel closed" and "channel is nil" that this function can return instead of panic. Alternatively, it could beclose(channel) bool
, emulating the same behaviour that reading from the channel "value, stillOpen := <- ch"example:
_ := close(ch) // will not panic if
ch
isnil
, will not panic if ch was closed alreadyclose(ch) // just like before, will behave as currently expected on closed or nil channel
Rationale
Using
close()
on channels is suggested as a method to notify reader side that channel is no longer usable, and/or job queue is exhausted. Problem is that even in examples (https://gobyexample.com/closing-channels) it is suggested to use a second channel to indicate "completion". Even if programmer have checked if channel is ready/not ready previously (by usingselect
), it is still requires synchronization because it could be done only once if more than one writer exists.Using fan-in or fan-out patterns of message aggregation/distribution, second channel is recommended as a way to identify "done", where if there was a way to safely notify "done" twice over the same channel, second "confirmation" channel would not be needed. After all, if reader went away, there is no need for the writer to continue - channel is stale anyway - but transmitting that information (channel no longer needed) requires a second channel or atomic bool.
I feel like proposal does not break Go1 Compatibility Guarantee - there is no code that checks return value of close() currently, so that code will continue to behave as it was originally designed.
Implementation
Compiler modifications are beyond my level of understanding of how Go compiler works.
Runtime modifications seems relatively not comlex by creating a second function similar to (or reusing)
closechan()
, (safe_closechan()
?) in src/runtime/chan.go (https://golang.org/src/runtime/chan.go#L334) that does not panic in the first 2 checks.Alternatives
isClosed()
Introduction of this function neither solves task completion notification problem, nor it will solve synchronization issues with calling close() even after
isClose()
returnstrue
Do not call
close()
unless you are absolutely needAs much as memleaks might not be a biggest problem, surprising lack of symmetry for "If something is opened, it needs to be closed" is a continuous stream of questions and confusion from developers.
Having additional backflow mechanism to synchronize "I confirm that it is done"
Not intuitive, and makes usage of channels harder.
Wrap
sync.Once
around close call, and use only that function to close the channel.Workable solution, but encourages a code duplication for each type of the channel used, needs enforcing usage pattern "Use special Close() instead of direct close()", and still does not protect the cases when someone will call
close
on a channel directly. Also... adding an additional synchronization around what supposed to be a synchronization primitive feels (aesthetically?) wrong.Conclusion
I think adding this override does not break Go1 Compatibility promise. It would make operations on channels be more consistent with "nil forgiveness" that makes working with maps and slices so enjoyable, and also would help with channels adoption. As I see right now by scouring libraries and Go own runtime and tools - channels are suggested to be used as an synchronization mechanism in Go, but adoption of it is extremely low.
The text was updated successfully, but these errors were encountered: