Skip to content

Commit

Permalink
runtime: crash when func main calls Goexit and all other goroutines exit
Browse files Browse the repository at this point in the history
This has typically crashed in the past, although usually with
an 'all goroutines are asleep - deadlock!' message that shows
no goroutines (because there aren't any).

Previous discussion at:
https://groups.google.com/d/msg/golang-nuts/uCT_7WxxopQ/BoSBlLFzUTkJ
https://groups.google.com/d/msg/golang-dev/KUojayEr20I/u4fp_Ej5PdUJ
http://golang.org/issue/7711

There is general agreement that runtime.Goexit terminates the
main goroutine, so that main cannot return, so the program does
not exit.

The interpretation that all other goroutines exiting causes an
exit(0) is relatively new and was not part of those discussions.
That is what this CL changes.

Thankfully, even though the exit(0) has been there for a while,
some other accounting bugs made it very difficult to trigger,
so it is reasonable to replace. In particular, see golang.org/issue/7711#c10
for an examination of the behavior across past releases.

Fixes golang#7711.

LGTM=iant, r
R=golang-codereviews, iant, dvyukov, r
CC=golang-codereviews
https://golang.org/cl/88210044
  • Loading branch information
rsc committed Apr 16, 2014
1 parent 468cf82 commit ade6bc6
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 8 deletions.
9 changes: 9 additions & 0 deletions doc/go1.3.html
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,15 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
when the binary's file name contains no path separators.
</li>

<li>
If the main goroutine calls
<a href="/pkg/runtime/#Goexit"><code>runtime.Goexit</code>
and all other goroutines finish execution, the program now always crashes,
reporting a detected deadlock.
Earlier versions of Go handled this situation inconsistently: most instances
were reported as deadlocks, but some trivial cases exited cleanly instead.
</li>

<li>
The <a href="/pkg/strconv/#CanBackquote"><code>CanBackquote</code></a>
function in the <a href="/pkg/strconv/"><code>strconv</code></a> package
Expand Down
14 changes: 7 additions & 7 deletions src/pkg/runtime/crash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ func TestLockedDeadlock2(t *testing.T) {

func TestGoexitDeadlock(t *testing.T) {
output := executeTest(t, goexitDeadlockSource, nil)
if output != "" {
t.Fatalf("expected no output, got:\n%s", output)
want := "no goroutines (main called runtime.Goexit) - deadlock!"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
}
}

Expand Down Expand Up @@ -144,13 +145,12 @@ panic: again

}

func TestGoexitExit(t *testing.T) {
func TestGoexitCrash(t *testing.T) {
output := executeTest(t, goexitExitSource, nil)
want := ""
if output != want {
t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
want := "no goroutines (main called runtime.Goexit) - deadlock!"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
}

}

const crashSource = `
Expand Down
5 changes: 5 additions & 0 deletions src/pkg/runtime/extern.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ func Gosched()

// Goexit terminates the goroutine that calls it. No other goroutine is affected.
// Goexit runs all deferred calls before terminating the goroutine.
//
// Calling Goexit from the main goroutine terminates that goroutine
// without func main returning. Since func main has not returned,
// the program continues execution of other goroutines.
// If all other goroutines exit, the program crashes.
func Goexit()

// Caller reports file and line number information about function invocations on
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/runtime/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2501,7 +2501,7 @@ checkdead(void)
}
runtime·unlock(&allglock);
if(grunning == 0) // possible if main goroutine calls runtime·Goexit()
runtime·exit(0);
runtime·throw("no goroutines (main called runtime.Goexit) - deadlock!");
m->throwing = -1; // do not dump full stacks
runtime·throw("all goroutines are asleep - deadlock!");
}
Expand Down

0 comments on commit ade6bc6

Please sign in to comment.