-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: Go2: goroutines execute defer statements when main exits #44973
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
Comments
That defer statement does get executed. It’s just that the process exits before the worker goroutine finishes, which is to be expected. Add a sleep after closing the channel and you’ll see what I mean. As such, this issue seems to be a question about how to use Go, rather than a feature request or defect report about the Go language and/or toolchain. For questions about Go, please visit:
Thanks |
@seankhliao I appreciate the perspective.
My issue isn't with defer statements being called after a blocking channel is closed (causing a Goroutine to exit). As you suggest, I can put a sleep after that point in time. It is with defer in Goroutines not being called when main exits. There is nowhere to put a sleep call at or after the main exiting that would allow a Goroutine to execute defer statements. I think it is sensible for the following to execute the Goroutine defer statement. Note that a sleep call has been added in the very last place it could possibly be and does not allow the Goroutine's defer statement to execute. https://play.golang.org/p/OXU92BBl4Yo
https://blog.golang.org/defer-panic-and-recover
From my inexperienced viewpoint, it seems incongruent to have a keyword "defer" which is "commonly used to simplify clean-up activities" not work with Goroutines when main exits. Go should exit all Goroutines and let defer statements run prior to exiting main. This is how a defer statement within main acts, why would any other defer statement work differently? (Not in a technical sense as they all obviously follow the "same" set of rules, but in a descriptive and philosophical sense "defer executes after the end.............except when it doesn't?" Is there a compelling reason why it isn't this way? |
A goroutine may be in an infinite loop when the process exits. How would it reach a deferred function call? |
I agree that this seems impossible. It would mean that on a call to |
There is a feeling associated with defer that this function will definitely execute.
Consider the following code:
Naively, I would expect a defer in But I find that neither always run. This seems contrary to the feeling Go gives me. If the panic is called from inside But if the panic is called within And, of course, if the panic is called by
I do not have the expertise to suggest how this would work. But I am questioning if it should work. There are always clever people who accomplish things I would have been certain were impossible. To me, "can it" and "should it" are orthogonal questions. Of course, there are situations where something should be, but also cannot be. Thinking out loud: If it becomes certain that it is too difficult to guarantee a Goroutine will execute defer statements when the program is terminated elsewhere, is it worthwhile just to try and report the result? What if, when the program exits, it sends a |
It will always be the case that in exceptional circumstances defer functions will not run. If someone pulls the plug on the computer, they will not run. If the program is killed by a Personally I do not think it would be a good way to notify goroutines on program exit and wait for them to respond. Many server programs have tens of thousands of goroutines. We want programs to exit in microseconds, not seconds. Taking this together, if it is essential for some operation to occur when a program exits, that should be handled outside of the program. |
@ianlancetaylor When I read about defer for Go, it seems like a clear use case is simple, orderly, clean-up almost regardless of how the program exits. Honestly, not having dug deeply into it, I expected a Goroutine to always execute a defer statement (except, of course, the most extreme cases). I do not consider main exiting while a Goroutine is running an "extreme" circumstance. Suppose I have a Goroutine that consumes data generating by workers and writes them to file. The canonical example of defer seems to be closing a file. I cannot open the file in main and defer its close there - if a Goroutine causes a termination, it won't run (does that seem right to you?) I cannot open it in the Goroutine and defer the close there - if main (or anything else) causes an exit, it won't run. Of course I can bake in a bunch of flow controls and error / panic handling to make sure the file is closed. But, isn't defer meant to be the easy way to do this? Isn't it weird that defer doesn't play well with raison d'etre of Go? I am sure, since "this is the way it was designed / has always been" this behavior seems very reasonable to seasoned veterans. But what do you think about it if you pretend to have fresh eyes? Obviously, there are going to be some practical considerations, such as the ones you bring up. But can we overcome those issues in a way that allows defer to be a great way to close open files (etc) except in the most exceptional of circumstances? Wouldn't it be nice if I could open a file in a Goroutine, defer its close and be done? Instead of adding a bunch of boilerplate to be able to signal to the routine that main is going to exit and it should close the file? Or instead of opening the file in main even though the file is used nowhere else? |
All files are closed anyhow when a program exits, so that doesn't seem like a helpful example. I'm not trying to argue that defer should work a certain way because it has always worked that way. I'm arguing that defer was never intended to solve the problem of "this code must always run," because, as discussed above, that problem can't be solved within a single process. We've always been very aware of that fact when developing Go. Another problem we've always been aware of is that many C++ programs take a long time to exit, because they must run all global destructors. This is such a well-known problem that C++ introduced a way to avoid that problem: a I believe that any changes made here should be made considering those potential problems. |
@ianlancetaylor I really appreciate you taking the time to share your expertise here. It means a lot to me.
Well, you got me there. I should have chosen a better example.
Can you share with me what the motivation for defer is? While looking into to this, I read some articles about defer, the origins of Go, and the "philosophy" of Go to try to figure out what I was missing. I was still left with my question afterward.
Can you point me to a conversation that discusses this in more detail (preferably written with a reasonably general audience in mind)? |
Programs today may rely on the fact that the code in a goroutine executes before its func f() (err error) {
errc := make(chan error, 1)
defer func() { err = <-errc }
errc <- nil
} If that (Compare #41891 (comment).) |
Ken's initial motivation for the construct that became So the goal was to provide a way to execute a series of statements upon function return, typically to preserve a set of invariants across calls to the function. (For what it's worth, in other languages a
I don't know of one, sorry. |
Based on this discussion above, and the lack of support based on emoji voting, this is a likely decline. Leaving open for four weeks for final comments. |
No further comments. |
Currently, Goroutines do not execute defer statements when main exits.
https://play.golang.org/p/pBWU5gcnA4C
It seems reasonable for Goroutines to exit and execute defer statements as part of main exiting.
This would be a straightforward way guarantee any clean up is performed before main exits.
Are there compelling reasons not to do this?
The text was updated successfully, but these errors were encountered: