2012-05-29 23:10:54 -06:00
|
|
|
// Copyright 2012 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package runtime_test
|
|
|
|
|
|
|
|
import (
|
2015-01-05 14:14:08 -07:00
|
|
|
"fmt"
|
2012-05-29 23:10:54 -06:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2015-02-07 05:31:18 -07:00
|
|
|
"regexp"
|
2014-05-20 10:10:19 -06:00
|
|
|
"runtime"
|
2013-02-20 01:15:02 -07:00
|
|
|
"strings"
|
2015-01-05 14:14:08 -07:00
|
|
|
"sync"
|
2012-05-29 23:10:54 -06:00
|
|
|
"testing"
|
|
|
|
"text/template"
|
|
|
|
)
|
|
|
|
|
2013-04-07 12:37:37 -06:00
|
|
|
func testEnv(cmd *exec.Cmd) *exec.Cmd {
|
|
|
|
if cmd.Env != nil {
|
|
|
|
panic("environment already set")
|
|
|
|
}
|
|
|
|
for _, env := range os.Environ() {
|
2015-02-07 05:31:18 -07:00
|
|
|
// Exclude GODEBUG from the environment to prevent its output
|
|
|
|
// from breaking tests that are trying to parse other command output.
|
2013-06-28 08:37:06 -06:00
|
|
|
if strings.HasPrefix(env, "GODEBUG=") {
|
2013-04-07 12:37:37 -06:00
|
|
|
continue
|
|
|
|
}
|
2015-02-07 05:31:18 -07:00
|
|
|
// Exclude GOTRACEBACK for the same reason.
|
|
|
|
if strings.HasPrefix(env, "GOTRACEBACK=") {
|
|
|
|
continue
|
|
|
|
}
|
2013-04-07 12:37:37 -06:00
|
|
|
cmd.Env = append(cmd.Env, env)
|
|
|
|
}
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2014-10-28 19:53:09 -06:00
|
|
|
func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
|
2014-07-08 12:47:52 -06:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "android", "nacl":
|
|
|
|
t.Skipf("skipping on %s", runtime.GOOS)
|
2014-05-20 10:10:19 -06:00
|
|
|
}
|
|
|
|
|
2013-02-20 01:15:02 -07:00
|
|
|
checkStaleRuntime(t)
|
2012-05-29 23:10:54 -06:00
|
|
|
|
2013-02-20 01:15:02 -07:00
|
|
|
st := template.Must(template.New("crashSource").Parse(templ))
|
2012-05-29 23:10:54 -06:00
|
|
|
|
|
|
|
dir, err := ioutil.TempDir("", "go-build")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to create temp directory: %v", err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
src := filepath.Join(dir, "main.go")
|
|
|
|
f, err := os.Create(src)
|
|
|
|
if err != nil {
|
2013-08-12 12:04:10 -06:00
|
|
|
t.Fatalf("failed to create file: %v", err)
|
2012-05-29 23:10:54 -06:00
|
|
|
}
|
2013-02-20 01:15:02 -07:00
|
|
|
err = st.Execute(f, data)
|
2012-05-29 23:10:54 -06:00
|
|
|
if err != nil {
|
|
|
|
f.Close()
|
|
|
|
t.Fatalf("failed to execute template: %v", err)
|
|
|
|
}
|
2013-08-12 12:04:10 -06:00
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
t.Fatalf("failed to close file: %v", err)
|
|
|
|
}
|
2012-05-29 23:10:54 -06:00
|
|
|
|
2014-10-28 19:53:09 -06:00
|
|
|
for i := 0; i < len(extra); i += 2 {
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command("go", "build", "-o", "a.exe")
|
|
|
|
cmd.Dir = dir
|
|
|
|
out, err := testEnv(cmd).CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("building source: %v\n%s", err, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
|
2013-02-20 01:15:02 -07:00
|
|
|
return string(got)
|
|
|
|
}
|
|
|
|
|
2015-01-05 14:14:08 -07:00
|
|
|
var (
|
|
|
|
staleRuntimeOnce sync.Once // guards init of staleRuntimeErr
|
|
|
|
staleRuntimeErr error
|
|
|
|
)
|
|
|
|
|
2013-02-20 01:15:02 -07:00
|
|
|
func checkStaleRuntime(t *testing.T) {
|
2015-01-05 14:14:08 -07:00
|
|
|
staleRuntimeOnce.Do(func() {
|
|
|
|
// 'go run' uses the installed copy of runtime.a, which may be out of date.
|
|
|
|
out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if string(out) != "false\n" {
|
|
|
|
staleRuntimeErr = fmt.Errorf("Stale runtime.a. Run 'go install runtime'.")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if staleRuntimeErr != nil {
|
|
|
|
t.Fatal(staleRuntimeErr)
|
2012-05-29 23:10:54 -06:00
|
|
|
}
|
2013-02-20 01:15:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func testCrashHandler(t *testing.T, cgo bool) {
|
|
|
|
type crashTest struct {
|
|
|
|
Cgo bool
|
|
|
|
}
|
2013-08-15 20:34:06 -06:00
|
|
|
output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
|
2012-05-29 23:10:54 -06:00
|
|
|
want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
|
2013-08-15 20:34:06 -06:00
|
|
|
if output != want {
|
|
|
|
t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
|
2012-05-29 23:10:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCrashHandler(t *testing.T) {
|
2013-02-20 01:15:02 -07:00
|
|
|
testCrashHandler(t, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testDeadlock(t *testing.T, source string) {
|
2013-08-15 20:34:06 -06:00
|
|
|
output := executeTest(t, source, nil)
|
2013-02-20 01:15:02 -07:00
|
|
|
want := "fatal error: all goroutines are asleep - deadlock!\n"
|
2013-08-15 20:34:06 -06:00
|
|
|
if !strings.HasPrefix(output, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, output)
|
2013-02-20 01:15:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2012-05-29 23:10:54 -06:00
|
|
|
}
|
|
|
|
|
2013-03-05 00:40:17 -07:00
|
|
|
func TestGoexitDeadlock(t *testing.T) {
|
2013-08-15 20:34:06 -06:00
|
|
|
output := executeTest(t, goexitDeadlockSource, nil)
|
runtime: crash when func main calls Goexit and all other goroutines exit
This has typically crashed in the past, although usually with
an 'all goroutines are asleep - deadlock!' message that shows
no goroutines (because there aren't any).
Previous discussion at:
https://groups.google.com/d/msg/golang-nuts/uCT_7WxxopQ/BoSBlLFzUTkJ
https://groups.google.com/d/msg/golang-dev/KUojayEr20I/u4fp_Ej5PdUJ
http://golang.org/issue/7711
There is general agreement that runtime.Goexit terminates the
main goroutine, so that main cannot return, so the program does
not exit.
The interpretation that all other goroutines exiting causes an
exit(0) is relatively new and was not part of those discussions.
That is what this CL changes.
Thankfully, even though the exit(0) has been there for a while,
some other accounting bugs made it very difficult to trigger,
so it is reasonable to replace. In particular, see golang.org/issue/7711#c10
for an examination of the behavior across past releases.
Fixes #7711.
LGTM=iant, r
R=golang-codereviews, iant, dvyukov, r
CC=golang-codereviews
https://golang.org/cl/88210044
2014-04-16 11:12:18 -06:00
|
|
|
want := "no goroutines (main called runtime.Goexit) - deadlock!"
|
|
|
|
if !strings.Contains(output, want) {
|
|
|
|
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
|
2013-08-15 20:34:06 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStackOverflow(t *testing.T) {
|
|
|
|
output := executeTest(t, stackOverflowSource, nil)
|
|
|
|
want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
|
|
|
|
if !strings.HasPrefix(output, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, output)
|
2013-03-05 00:40:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-16 20:25:26 -06:00
|
|
|
func TestThreadExhaustion(t *testing.T) {
|
|
|
|
output := executeTest(t, threadExhaustionSource, nil)
|
|
|
|
want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
|
|
|
|
if !strings.HasPrefix(output, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-07 09:50:30 -07:00
|
|
|
func TestRecursivePanic(t *testing.T) {
|
|
|
|
output := executeTest(t, recursivePanicSource, nil)
|
|
|
|
want := `wrap: bad
|
|
|
|
panic: again
|
|
|
|
|
|
|
|
`
|
|
|
|
if !strings.HasPrefix(output, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, output)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
runtime: crash when func main calls Goexit and all other goroutines exit
This has typically crashed in the past, although usually with
an 'all goroutines are asleep - deadlock!' message that shows
no goroutines (because there aren't any).
Previous discussion at:
https://groups.google.com/d/msg/golang-nuts/uCT_7WxxopQ/BoSBlLFzUTkJ
https://groups.google.com/d/msg/golang-dev/KUojayEr20I/u4fp_Ej5PdUJ
http://golang.org/issue/7711
There is general agreement that runtime.Goexit terminates the
main goroutine, so that main cannot return, so the program does
not exit.
The interpretation that all other goroutines exiting causes an
exit(0) is relatively new and was not part of those discussions.
That is what this CL changes.
Thankfully, even though the exit(0) has been there for a while,
some other accounting bugs made it very difficult to trigger,
so it is reasonable to replace. In particular, see golang.org/issue/7711#c10
for an examination of the behavior across past releases.
Fixes #7711.
LGTM=iant, r
R=golang-codereviews, iant, dvyukov, r
CC=golang-codereviews
https://golang.org/cl/88210044
2014-04-16 11:12:18 -06:00
|
|
|
func TestGoexitCrash(t *testing.T) {
|
2014-04-15 09:48:17 -06:00
|
|
|
output := executeTest(t, goexitExitSource, nil)
|
runtime: crash when func main calls Goexit and all other goroutines exit
This has typically crashed in the past, although usually with
an 'all goroutines are asleep - deadlock!' message that shows
no goroutines (because there aren't any).
Previous discussion at:
https://groups.google.com/d/msg/golang-nuts/uCT_7WxxopQ/BoSBlLFzUTkJ
https://groups.google.com/d/msg/golang-dev/KUojayEr20I/u4fp_Ej5PdUJ
http://golang.org/issue/7711
There is general agreement that runtime.Goexit terminates the
main goroutine, so that main cannot return, so the program does
not exit.
The interpretation that all other goroutines exiting causes an
exit(0) is relatively new and was not part of those discussions.
That is what this CL changes.
Thankfully, even though the exit(0) has been there for a while,
some other accounting bugs made it very difficult to trigger,
so it is reasonable to replace. In particular, see golang.org/issue/7711#c10
for an examination of the behavior across past releases.
Fixes #7711.
LGTM=iant, r
R=golang-codereviews, iant, dvyukov, r
CC=golang-codereviews
https://golang.org/cl/88210044
2014-04-16 11:12:18 -06:00
|
|
|
want := "no goroutines (main called runtime.Goexit) - deadlock!"
|
|
|
|
if !strings.Contains(output, want) {
|
|
|
|
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
|
2014-04-15 09:48:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-15 16:09:17 -06:00
|
|
|
func TestGoexitDefer(t *testing.T) {
|
|
|
|
c := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
if r != nil {
|
|
|
|
t.Errorf("non-nil recover during Goexit")
|
|
|
|
}
|
|
|
|
c <- struct{}{}
|
|
|
|
}()
|
|
|
|
runtime.Goexit()
|
|
|
|
}()
|
|
|
|
// Note: if the defer fails to run, we will get a deadlock here
|
|
|
|
<-c
|
|
|
|
}
|
|
|
|
|
2014-05-27 22:00:01 -06:00
|
|
|
func TestGoNil(t *testing.T) {
|
|
|
|
output := executeTest(t, goNilSource, nil)
|
|
|
|
want := "go of nil func value"
|
|
|
|
if !strings.Contains(output, want) {
|
|
|
|
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-16 02:19:33 -06:00
|
|
|
func TestMainGoroutineId(t *testing.T) {
|
|
|
|
output := executeTest(t, mainGoroutineIdSource, nil)
|
|
|
|
want := "panic: test\n\ngoroutine 1 [running]:\n"
|
|
|
|
if !strings.HasPrefix(output, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-07 05:31:18 -07:00
|
|
|
func TestNoHelperGoroutines(t *testing.T) {
|
|
|
|
output := executeTest(t, noHelperGoroutinesSource, nil)
|
|
|
|
matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
|
|
|
|
if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
|
|
|
|
t.Fatalf("want to see only goroutine 1, see:\n%s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-11 10:08:30 -06:00
|
|
|
func TestBreakpoint(t *testing.T) {
|
|
|
|
output := executeTest(t, breakpointSource, nil)
|
|
|
|
want := "runtime.Breakpoint()"
|
|
|
|
if !strings.Contains(output, want) {
|
|
|
|
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-29 23:10:54 -06:00
|
|
|
const crashSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
{{if .Cgo}}
|
|
|
|
import "C"
|
|
|
|
{{end}}
|
|
|
|
|
|
|
|
func test(name string) {
|
|
|
|
defer func() {
|
|
|
|
if x := recover(); x != nil {
|
|
|
|
fmt.Printf(" recovered")
|
|
|
|
}
|
|
|
|
fmt.Printf(" done\n")
|
|
|
|
}()
|
|
|
|
fmt.Printf("%s:", name)
|
|
|
|
var s *string
|
|
|
|
_ = *s
|
|
|
|
fmt.Print("SHOULD NOT BE HERE")
|
|
|
|
}
|
|
|
|
|
|
|
|
func testInNewThread(name string) {
|
|
|
|
c := make(chan bool)
|
|
|
|
go func() {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
test(name)
|
|
|
|
c <- true
|
|
|
|
}()
|
|
|
|
<-c
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
test("main")
|
|
|
|
testInNewThread("new-thread")
|
|
|
|
testInNewThread("second-new-thread")
|
|
|
|
test("main-again")
|
|
|
|
}
|
|
|
|
`
|
2013-02-20 01:15:02 -07:00
|
|
|
|
|
|
|
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 {}
|
|
|
|
}
|
|
|
|
`
|
2013-03-05 00:40:17 -07:00
|
|
|
|
|
|
|
const goexitDeadlockSource = `
|
|
|
|
package main
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
func F() {
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
go F()
|
|
|
|
go F()
|
|
|
|
runtime.Goexit()
|
|
|
|
}
|
|
|
|
`
|
2013-08-15 20:34:06 -06:00
|
|
|
|
|
|
|
const stackOverflowSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import "runtime/debug"
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
debug.SetMaxStack(4<<20)
|
|
|
|
f(make([]byte, 10))
|
|
|
|
}
|
|
|
|
|
|
|
|
func f(x []byte) byte {
|
|
|
|
var buf [64<<10]byte
|
|
|
|
return x[0] + f(buf[:])
|
|
|
|
}
|
|
|
|
`
|
2013-08-16 20:25:26 -06:00
|
|
|
|
|
|
|
const threadExhaustionSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"runtime/debug"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
debug.SetMaxThreads(10)
|
|
|
|
c := make(chan int)
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
go func() {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
c <- 0
|
|
|
|
select{}
|
|
|
|
}()
|
|
|
|
<-c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`
|
2014-03-07 09:50:30 -07:00
|
|
|
|
|
|
|
const recursivePanicSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
func() {
|
|
|
|
defer func() {
|
|
|
|
fmt.Println(recover())
|
|
|
|
}()
|
|
|
|
var x [8192]byte
|
|
|
|
func(x [8192]byte) {
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
panic("wrap: " + err.(string))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
panic("bad")
|
|
|
|
}(x)
|
|
|
|
}()
|
|
|
|
panic("again")
|
|
|
|
}
|
|
|
|
`
|
2014-04-15 09:48:17 -06:00
|
|
|
|
|
|
|
const goexitExitSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
go func() {
|
|
|
|
time.Sleep(time.Millisecond)
|
|
|
|
}()
|
|
|
|
i := 0
|
|
|
|
runtime.SetFinalizer(&i, func(p *int) {})
|
|
|
|
runtime.GC()
|
|
|
|
runtime.Goexit()
|
|
|
|
}
|
|
|
|
`
|
2014-05-27 22:00:01 -06:00
|
|
|
|
|
|
|
const goNilSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
defer func() {
|
|
|
|
recover()
|
|
|
|
}()
|
|
|
|
var f func()
|
|
|
|
go f()
|
|
|
|
select{}
|
|
|
|
}
|
|
|
|
`
|
2014-07-16 02:19:33 -06:00
|
|
|
|
|
|
|
const mainGoroutineIdSource = `
|
|
|
|
package main
|
|
|
|
func main() {
|
|
|
|
panic("test")
|
|
|
|
}
|
|
|
|
`
|
2014-09-11 10:08:30 -06:00
|
|
|
|
2015-02-07 05:31:18 -07:00
|
|
|
const noHelperGoroutinesSource = `
|
|
|
|
package main
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
func init() {
|
|
|
|
i := 0
|
|
|
|
runtime.SetFinalizer(&i, func(p *int) {})
|
|
|
|
time.AfterFunc(time.Hour, func() {})
|
|
|
|
panic("oops")
|
|
|
|
}
|
|
|
|
func main() {
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
2014-09-11 10:08:30 -06:00
|
|
|
const breakpointSource = `
|
|
|
|
package main
|
|
|
|
import "runtime"
|
|
|
|
func main() {
|
|
|
|
runtime.Breakpoint()
|
|
|
|
}
|
|
|
|
`
|
2014-09-19 17:33:14 -06:00
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
2015-01-13 10:12:50 -07:00
|
|
|
|
|
|
|
func TestNetpollDeadlock(t *testing.T) {
|
|
|
|
output := executeTest(t, netpollDeadlockSource, nil)
|
|
|
|
want := "done\n"
|
|
|
|
if !strings.HasSuffix(output, want) {
|
|
|
|
t.Fatalf("output does not start with %q:\n%s", want, output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const netpollDeadlockSource = `
|
|
|
|
package main
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
)
|
|
|
|
func init() {
|
|
|
|
fmt.Println("dialing")
|
|
|
|
c, err := net.Dial("tcp", "localhost:14356")
|
|
|
|
if err == nil {
|
|
|
|
c.Close()
|
|
|
|
} else {
|
|
|
|
fmt.Println("error: ", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func main() {
|
|
|
|
fmt.Println("done")
|
|
|
|
}
|
|
|
|
`
|