Skip to content

Commit 3088639

Browse files
ianlancetaylorgopherbot
authored andcommitted
runtime: in __tsan_fini tell scheduler we are entering non-Go code
__tsan_fini will call exit which will call destructors which may in principle call back into Go functions. Prepare the scheduler by calling entersyscall before __tsan_fini. Fixes #59711 Change-Id: Ic4df8fba3014bafa516739408ccfc30aba4f22ad Reviewed-on: https://go-review.googlesource.com/c/go/+/486615 Reviewed-by: Michael Pratt <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]>
1 parent cedf500 commit 3088639

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

src/runtime/crash_cgo_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package runtime_test
99
import (
1010
"fmt"
1111
"internal/goos"
12+
"internal/platform"
1213
"internal/testenv"
1314
"os"
1415
"os/exec"
@@ -777,3 +778,39 @@ func TestCgoSigfwd(t *testing.T) {
777778
t.Fatalf("expected %q, but got:\n%s", want, got)
778779
}
779780
}
781+
782+
func TestDestructorCallback(t *testing.T) {
783+
t.Parallel()
784+
got := runTestProg(t, "testprogcgo", "DestructorCallback")
785+
if want := "OK\n"; got != want {
786+
t.Errorf("expected %q, but got:\n%s", want, got)
787+
}
788+
}
789+
790+
func TestDestructorCallbackRace(t *testing.T) {
791+
// This test requires building with -race,
792+
// so it's somewhat slow.
793+
if testing.Short() {
794+
t.Skip("skipping test in -short mode")
795+
}
796+
797+
if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
798+
t.Skipf("skipping on %s/%s because race detector not supported", runtime.GOOS, runtime.GOARCH)
799+
}
800+
801+
t.Parallel()
802+
803+
exe, err := buildTestProg(t, "testprogcgo", "-race")
804+
if err != nil {
805+
t.Fatal(err)
806+
}
807+
808+
got, err := testenv.CleanCmdEnv(exec.Command(exe, "DestructorCallback")).CombinedOutput()
809+
if err != nil {
810+
t.Fatal(err)
811+
}
812+
813+
if want := "OK\n"; string(got) != want {
814+
t.Errorf("expected %q, but got:\n%s", want, got)
815+
}
816+
}

src/runtime/race.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,16 @@ func racefini() {
407407
// already held it's assumed that the first caller exits the program
408408
// so other calls can hang forever without an issue.
409409
lock(&raceFiniLock)
410+
411+
// __tsan_fini will run C atexit functions and C++ destructors,
412+
// which can theoretically call back into Go.
413+
// Tell the scheduler we entering external code.
414+
entersyscall()
415+
410416
// We're entering external code that may call ExitProcess on
411417
// Windows.
412418
osPreemptExtEnter(getg().m)
419+
413420
racecall(&__tsan_fini, 0, 0, 0, 0)
414421
}
415422

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
#include "_cgo_export.h"
6+
7+
static void callDestructorCallback() {
8+
GoDestructorCallback();
9+
}
10+
11+
static void (*destructorFn)(void);
12+
13+
void registerDestructor() {
14+
destructorFn = callDestructorCallback;
15+
}
16+
17+
__attribute__((destructor))
18+
static void destructor() {
19+
if (destructorFn) {
20+
destructorFn();
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// extern void registerDestructor();
8+
import "C"
9+
10+
import "fmt"
11+
12+
func init() {
13+
register("DestructorCallback", DestructorCallback)
14+
}
15+
16+
//export GoDestructorCallback
17+
func GoDestructorCallback() {
18+
}
19+
20+
func DestructorCallback() {
21+
C.registerDestructor()
22+
fmt.Println("OK")
23+
}

0 commit comments

Comments
 (0)