Commit 06a488fa authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

runtime: fix deadlock detector false negative

Fixes #4819.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7322086
parent a92e11a2
...@@ -11,5 +11,5 @@ import ( ...@@ -11,5 +11,5 @@ import (
) )
func TestCgoCrashHandler(t *testing.T) { func TestCgoCrashHandler(t *testing.T) {
testCrashHandler(t, &crashTest{Cgo: true}) testCrashHandler(t, true)
} }
...@@ -9,19 +9,15 @@ import ( ...@@ -9,19 +9,15 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"text/template" "text/template"
) )
type crashTest struct { func executeTest(t *testing.T, templ string, data interface{}) string {
Cgo bool 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(templ))
st := template.Must(template.New("crashSource").Parse(crashSource))
dir, err := ioutil.TempDir("", "go-build") dir, err := ioutil.TempDir("", "go-build")
if err != nil { if err != nil {
...@@ -34,25 +30,73 @@ func testCrashHandler(t *testing.T, ct *crashTest) { ...@@ -34,25 +30,73 @@ func testCrashHandler(t *testing.T, ct *crashTest) {
if err != nil { if err != nil {
t.Fatalf("failed to create %v: %v", src, err) t.Fatalf("failed to create %v: %v", src, err)
} }
err = st.Execute(f, ct) err = st.Execute(f, data)
if err != nil { if err != nil {
f.Close() f.Close()
t.Fatalf("failed to execute template: %v", err) t.Fatalf("failed to execute template: %v", err)
} }
f.Close() 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 { 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" want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
if string(got) != string(want) { if got != want {
t.Fatalf("expected %q, but got %q", string(want), string(got)) t.Fatalf("expected %q, but got %q", want, got)
} }
} }
func TestCrashHandler(t *testing.T) { 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 = ` const crashSource = `
...@@ -98,3 +142,44 @@ func main() { ...@@ -98,3 +142,44 @@ func main() {
test("main-again") 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 {}
}
`
...@@ -241,14 +241,13 @@ runtime·main(void) ...@@ -241,14 +241,13 @@ runtime·main(void)
runtime·sched.init = true; runtime·sched.init = true;
scvg = runtime·newproc1((byte*)runtime·MHeap_Scavenger, nil, 0, 0, runtime·main); scvg = runtime·newproc1((byte*)runtime·MHeap_Scavenger, nil, 0, 0, runtime·main);
scvg->issystem = true; scvg->issystem = true;
main·init();
runtime·sched.init = false;
runtime·unlockOSThread();
// The deadlock detection has false negatives. // The deadlock detection has false negatives.
// Let scvg start up, to eliminate the false negative // Let scvg start up, to eliminate the false negative
// for the trivial program func main() { select{} }. // for the trivial program func main() { select{} }.
runtime·gosched(); runtime·gosched();
main·init();
runtime·sched.init = false;
runtime·unlockOSThread();
main·main(); main·main();
if(raceenabled) if(raceenabled)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment