diff --git a/src/pkg/runtime/crash_cgo_test.go b/src/pkg/runtime/crash_cgo_test.go index 91c4bdb0358..12b75dc1b2c 100644 --- a/src/pkg/runtime/crash_cgo_test.go +++ b/src/pkg/runtime/crash_cgo_test.go @@ -11,5 +11,5 @@ import ( ) func TestCgoCrashHandler(t *testing.T) { - testCrashHandler(t, &crashTest{Cgo: true}) + testCrashHandler(t, true) } diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go index bc6b89feed0..b2db1d7b95c 100644 --- a/src/pkg/runtime/crash_test.go +++ b/src/pkg/runtime/crash_test.go @@ -9,19 +9,15 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" "text/template" ) -type crashTest struct { - Cgo bool -} +func executeTest(t *testing.T, templ string, data interface{}) string { + checkStaleRuntime(t) -// This test is a separate program, because it is testing -// both main (m0) and non-main threads (m). - -func testCrashHandler(t *testing.T, ct *crashTest) { - st := template.Must(template.New("crashSource").Parse(crashSource)) + st := template.Must(template.New("crashSource").Parse(templ)) dir, err := ioutil.TempDir("", "go-build") if err != nil { @@ -34,25 +30,73 @@ func testCrashHandler(t *testing.T, ct *crashTest) { if err != nil { t.Fatalf("failed to create %v: %v", src, err) } - err = st.Execute(f, ct) + err = st.Execute(f, data) if err != nil { f.Close() t.Fatalf("failed to execute template: %v", err) } f.Close() - got, err := exec.Command("go", "run", src).CombinedOutput() - if err != nil { - t.Fatalf("program exited with error: %v\n%v", err, string(got)) + // Deadlock tests hang with GOMAXPROCS>1. Issue 4826. + cmd := exec.Command("go", "run", src) + for _, s := range os.Environ() { + if strings.HasPrefix(s, "GOMAXPROCS") { + continue + } + cmd.Env = append(cmd.Env, s) } + got, _ := cmd.CombinedOutput() + return string(got) +} + +func checkStaleRuntime(t *testing.T) { + // 'go run' uses the installed copy of runtime.a, which may be out of date. + out, err := exec.Command("go", "list", "-f", "{{.Stale}}", "runtime").CombinedOutput() + if err != nil { + t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out)) + } + if string(out) != "false\n" { + t.Fatalf("Stale runtime.a. Run 'go install runtime'.") + } +} + +func testCrashHandler(t *testing.T, cgo bool) { + type crashTest struct { + Cgo bool + } + got := executeTest(t, crashSource, &crashTest{Cgo: cgo}) want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" - if string(got) != string(want) { - t.Fatalf("expected %q, but got %q", string(want), string(got)) + if got != want { + t.Fatalf("expected %q, but got %q", want, got) } } func TestCrashHandler(t *testing.T) { - testCrashHandler(t, &crashTest{Cgo: false}) + testCrashHandler(t, false) +} + +func testDeadlock(t *testing.T, source string) { + got := executeTest(t, source, nil) + want := "fatal error: all goroutines are asleep - deadlock!\n" + if !strings.HasPrefix(got, want) { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +func TestSimpleDeadlock(t *testing.T) { + testDeadlock(t, simpleDeadlockSource) +} + +func TestInitDeadlock(t *testing.T) { + testDeadlock(t, initDeadlockSource) +} + +func TestLockedDeadlock(t *testing.T) { + testDeadlock(t, lockedDeadlockSource) +} + +func TestLockedDeadlock2(t *testing.T) { + testDeadlock(t, lockedDeadlockSource2) } const crashSource = ` @@ -98,3 +142,44 @@ func main() { test("main-again") } ` + +const simpleDeadlockSource = ` +package main +func main() { + select {} +} +` + +const initDeadlockSource = ` +package main +func init() { + select {} +} +func main() { +} +` + +const lockedDeadlockSource = ` +package main +import "runtime" +func main() { + runtime.LockOSThread() + select {} +} +` + +const lockedDeadlockSource2 = ` +package main +import ( + "runtime" + "time" +) +func main() { + go func() { + runtime.LockOSThread() + select {} + }() + time.Sleep(time.Millisecond) + select {} +} +` diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index 5c60cddf9ba..111d90b0791 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -241,14 +241,13 @@ runtime·main(void) runtime·sched.init = true; scvg = runtime·newproc1((byte*)runtime·MHeap_Scavenger, nil, 0, 0, runtime·main); scvg->issystem = true; - main·init(); - runtime·sched.init = false; - runtime·unlockOSThread(); - // The deadlock detection has false negatives. // Let scvg start up, to eliminate the false negative // for the trivial program func main() { select{} }. runtime·gosched(); + main·init(); + runtime·sched.init = false; + runtime·unlockOSThread(); main·main(); if(raceenabled)