diff options
author | Keith Randall <khr@golang.org> | 2014-09-19 16:33:14 -0700 |
---|---|---|
committer | Keith Randall <khr@golang.org> | 2014-09-19 16:33:14 -0700 |
commit | ad546c6d47e249174a5b339c71a39d4459161302 (patch) | |
tree | 9cd548cdb1d3d9d61d6dbc7da0afd84972479eff /src/runtime/crash_test.go | |
parent | 803001e2e76308e3c829e1a63c1f04fb042723dd (diff) | |
download | go-ad546c6d47e249174a5b339c71a39d4459161302.tar.gz |
runtime: Fix interaction between Goexit and defers
When running defers, we must check whether the defer
has already been marked as started so we don't run it twice.
Fixes issue 8774.
LGTM=rsc
R=rsc
CC=golang-codereviews
https://codereview.appspot.com/142280044
Diffstat (limited to 'src/runtime/crash_test.go')
-rw-r--r-- | src/runtime/crash_test.go | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index d1577fb5f..783b4c48f 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -412,3 +412,91 @@ func main() { runtime.Breakpoint() } ` + +func TestGoexitInPanic(t *testing.T) { + // see issue 8774: this code used to trigger an infinite recursion + output := executeTest(t, goexitInPanicSource, nil) + want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +const goexitInPanicSource = ` +package main +import "runtime" +func main() { + go func() { + defer func() { + runtime.Goexit() + }() + panic("hello") + }() + runtime.Goexit() +} +` + +func TestPanicAfterGoexit(t *testing.T) { + // an uncaught panic should still work after goexit + output := executeTest(t, panicAfterGoexitSource, nil) + want := "panic: hello" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +const panicAfterGoexitSource = ` +package main +import "runtime" +func main() { + defer func() { + panic("hello") + }() + runtime.Goexit() +} +` + +func TestRecoveredPanicAfterGoexit(t *testing.T) { + output := executeTest(t, recoveredPanicAfterGoexitSource, nil) + want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +const recoveredPanicAfterGoexitSource = ` +package main +import "runtime" +func main() { + defer func() { + defer func() { + r := recover() + if r == nil { + panic("bad recover") + } + }() + panic("hello") + }() + runtime.Goexit() +} +` + +func TestRecoverBeforePanicAfterGoexit(t *testing.T) { + // 1. defer a function that recovers + // 2. defer a function that panics + // 3. call goexit + // Goexit should run the #2 defer. Its panic + // should be caught by the #1 defer, and execution + // should resume in the caller. Like the Goexit + // never happened! + defer func() { + r := recover() + if r == nil { + panic("bad recover") + } + }() + defer func() { + panic("hello") + }() + runtime.Goexit() +} |