// 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
                // 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.
 
 import (
        "internal/testenv"
        "io"
+       "math/rand"
        "os"
        "os/exec"
        "os/signal"
+       "runtime"
        "syscall"
        "testing"
+       "time"
        "unsafe"
 )
 
 
        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")
+}