]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix deadlock detector false negative
authorDmitriy Vyukov <dvyukov@google.com>
Wed, 20 Feb 2013 08:15:02 +0000 (12:15 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Wed, 20 Feb 2013 08:15:02 +0000 (12:15 +0400)
Fixes #4819.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7322086

src/pkg/runtime/crash_cgo_test.go
src/pkg/runtime/crash_test.go
src/pkg/runtime/proc.c

index 91c4bdb0358b6d154adea593097b227fb37adc24..12b75dc1b2c97460a730196da48e9f325dfd86ea 100644 (file)
@@ -11,5 +11,5 @@ import (
 )
 
 func TestCgoCrashHandler(t *testing.T) {
-       testCrashHandler(t, &crashTest{Cgo: true})
+       testCrashHandler(t, true)
 }
index bc6b89feed0f9d9cea595fa4e2741e091680358b..b2db1d7b95c93dccf07a485e07da440497c150f4 100644 (file)
@@ -9,19 +9,15 @@ import (
        "os"
        "os/exec"
        "path/filepath"
+       "strings"
        "testing"
        "text/template"
 )
 
-type crashTest struct {
-       Cgo bool
-}
-
-// This test is a separate program, because it is testing
-// both main (m0) and non-main threads (m).
+func executeTest(t *testing.T, templ string, data interface{}) string {
+       checkStaleRuntime(t)
 
-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()
+       // 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("program exited with error: %v\n%v", err, string(got))
+               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 {}
+}
+`
index 5c60cddf9babb0fbe1e54e3f16b0766f5c842c97..111d90b07912267bb09a91e5c302258b377def65 100644 (file)
@@ -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)