diff --git a/src/runtime/panic.go b/src/runtime/panic.go index bd1ea096aa2a01..ed08bf4f300897 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -614,6 +614,8 @@ func deferreturn() { // without func main returning. Since func main has not returned, // the program continues execution of other goroutines. // If all other goroutines exit, the program crashes. +// +// It crashes if called from a thread not created by the Go runtime. func Goexit() { // Create a panic object for Goexit, so we can recognize when it might be // bypassed by a recover(). diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 70e432929e6a06..97f6cc3305ad83 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -4321,6 +4321,9 @@ func gdestroy(gp *g) { if locked && mp.lockedInt != 0 { print("runtime: mp.lockedInt = ", mp.lockedInt, "\n") + if mp.isextra { + throw("runtime.Goexit called in a thread that was not created by the Go runtime") + } throw("exited a goroutine internally locked to the OS thread") } gfput(pp, gp) diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index a930ea707f12cc..0a542409842cba 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -1158,3 +1158,13 @@ func TestBigGOMAXPROCS(t *testing.T) { t.Errorf("output:\n%s\nwanted:\nunknown function: NonexistentTest", output) } } + +func TestCgoToGoCallGoexit(t *testing.T) { + if runtime.GOOS == "plan9" || runtime.GOOS == "windows" { + t.Skipf("no pthreads on %s", runtime.GOOS) + } + output := runTestProg(t, "testprogcgo", "CgoToGoCallGoexit") + if !strings.Contains(output, "runtime.Goexit called in a thread that was not created by the Go runtime") { + t.Fatalf("output should contain %s, got %s", "runtime.Goexit called in a thread that was not created by the Go runtime", output) + } +} diff --git a/src/runtime/testdata/testprogcgo/callback.go b/src/runtime/testdata/testprogcgo/callback.go index 319572fe109034..39993f13a678a5 100644 --- a/src/runtime/testdata/testprogcgo/callback.go +++ b/src/runtime/testdata/testprogcgo/callback.go @@ -38,10 +38,21 @@ import ( func init() { register("CgoCallbackGC", CgoCallbackGC) + register("CgoToGoCallGoexit", CgoToGoCallGoexit) } +func CgoToGoCallGoexit() { + goexit = true + C.foo() +} + +var goexit = false + //export go_callback func go_callback() { + if goexit { + runtime.Goexit() + } if e := extraMInUse.Load(); e == 0 { fmt.Printf("in callback extraMInUse got %d want >0\n", e) os.Exit(1)