Skip to content

runtime: Goexit on C-created thread report more useful error message #69126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
2 changes: 2 additions & 0 deletions src/runtime/panic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
11 changes: 11 additions & 0 deletions src/runtime/testdata/testprogcgo/callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down