mirror of
https://github.com/golang/go
synced 2024-11-26 15:46:54 -07:00
runtime: fix deadlock detector false negative
Fixes #4819. R=golang-dev, rsc CC=golang-dev https://golang.org/cl/7322086
This commit is contained in:
parent
a92e11a256
commit
06a488fa97
@ -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 (
|
|||||||
"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
|
st := template.Must(template.New("crashSource").Parse(templ))
|
||||||
// both main (m0) and non-main threads (m).
|
|
||||||
|
|
||||||
func testCrashHandler(t *testing.T, ct *crashTest) {
|
|
||||||
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) {
|
|||||||
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.
|
||||||
if err != nil {
|
cmd := exec.Command("go", "run", src)
|
||||||
t.Fatalf("program exited with error: %v\n%v", err, string(got))
|
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"
|
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() {
|
|||||||
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)
|
|||||||
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user