aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/runtime/signal_unix.go11
-rw-r--r--src/syscall/exec_unix_test.go45
2 files changed, 56 insertions, 0 deletions
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index dbac55c3d1..c7a6c2197c 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -360,6 +360,13 @@ func preemptM(mp *m) {
// required).
return
}
+
+ // On Darwin, don't try to preempt threads during exec.
+ // Issue #41702.
+ if GOOS == "darwin" {
+ execLock.rlock()
+ }
+
if atomic.Cas(&mp.signalPending, 0, 1) {
// If multiple threads are preempting the same M, it may send many
// signals to the same M such that it hardly make progress, causing
@@ -368,6 +375,10 @@ func preemptM(mp *m) {
// Only send a signal if there isn't already one pending.
signalM(mp, sigPreempt)
}
+
+ if GOOS == "darwin" {
+ execLock.runlock()
+ }
}
// sigFetchG fetches the value of G safely when running in a signal handler.
diff --git a/src/syscall/exec_unix_test.go b/src/syscall/exec_unix_test.go
index 33614f5221..722534af78 100644
--- a/src/syscall/exec_unix_test.go
+++ b/src/syscall/exec_unix_test.go
@@ -9,11 +9,14 @@ package syscall_test
import (
"internal/testenv"
"io"
+ "math/rand"
"os"
"os/exec"
"os/signal"
+ "runtime"
"syscall"
"testing"
+ "time"
"unsafe"
)
@@ -213,3 +216,45 @@ func TestForeground(t *testing.T) {
signal.Reset()
}
+
+// TestExec is for issue #41702.
+func TestExec(t *testing.T) {
+ cmd := exec.Command(os.Args[0], "-test.run=TestExecHelper")
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
+ o, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("%s\n%v", o, err)
+ }
+}
+
+// TestExecHelper is used by TestExec. It does nothing by itself.
+// In testing on macOS 10.14, this used to fail with
+// "signal: illegal instruction" more than half the time.
+func TestExecHelper(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
+ return
+ }
+
+ // We don't have to worry about restoring these values.
+ // We are in a child process that only runs this test,
+ // and we are going to call syscall.Exec anyhow.
+ runtime.GOMAXPROCS(50)
+ os.Setenv("GO_WANT_HELPER_PROCESS", "3")
+
+ stop := time.Now().Add(time.Second)
+ for i := 0; i < 100; i++ {
+ go func(i int) {
+ r := rand.New(rand.NewSource(int64(i)))
+ for time.Now().Before(stop) {
+ r.Uint64()
+ }
+ }(i)
+ }
+
+ time.Sleep(10 * time.Millisecond)
+
+ argv := []string{os.Args[0], "-test.run=TestExecHelper"}
+ syscall.Exec(os.Args[0], argv, os.Environ())
+
+ t.Error("syscall.Exec returned")
+}