From: Ian Lance Taylor Date: Fri, 8 Nov 2019 05:08:48 +0000 (-0800) Subject: runtime: use pipe rather than note in TestSignalM X-Git-Tag: go1.14beta1~233 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=99957b6930c76b683dbca1ff4bcdd56e59b1e035;p=gostls13.git runtime: use pipe rather than note in TestSignalM At least on Darwin notewakeup is not async-signal-safe. Fixes #35276 Change-Id: I1d7523715e8e77dbd7f21d9b1ed131e52d46cc41 Reviewed-on: https://go-review.googlesource.com/c/go/+/206078 Reviewed-by: Austin Clements --- diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go index 234da6d52c..e494dcb988 100644 --- a/src/runtime/crash_unix_test.go +++ b/src/runtime/crash_unix_test.go @@ -18,6 +18,7 @@ import ( "sync" "syscall" "testing" + "time" "unsafe" ) @@ -308,20 +309,45 @@ func TestSignalDuringExec(t *testing.T) { } func TestSignalM(t *testing.T) { + r, w, errno := runtime.Pipe() + if errno != 0 { + t.Fatal(syscall.Errno(errno)) + } + defer func() { + runtime.Close(r) + runtime.Close(w) + }() + runtime.Closeonexec(r) + runtime.Closeonexec(w) + var want, got int64 var wg sync.WaitGroup ready := make(chan *runtime.M) wg.Add(1) go func() { runtime.LockOSThread() - want, got = runtime.WaitForSigusr1(func(mp *runtime.M) { + var errno int32 + want, got = runtime.WaitForSigusr1(r, w, func(mp *runtime.M) { ready <- mp - }, 1e9) + }) + if errno != 0 { + t.Error(syscall.Errno(errno)) + } runtime.UnlockOSThread() wg.Done() }() waitingM := <-ready runtime.SendSigusr1(waitingM) + + timer := time.AfterFunc(time.Second, func() { + // Write 1 to tell WaitForSigusr1 that we timed out. + bw := byte(1) + if n := runtime.Write(uintptr(w), unsafe.Pointer(&bw), 1); n != 1 { + t.Errorf("pipe write failed: %d", n) + } + }) + defer timer.Stop() + wg.Wait() if got == -1 { t.Fatal("signalM signal not received") diff --git a/src/runtime/export_unix_test.go b/src/runtime/export_unix_test.go index 375513337e..621488eaba 100644 --- a/src/runtime/export_unix_test.go +++ b/src/runtime/export_unix_test.go @@ -6,6 +6,8 @@ package runtime +import "unsafe" + var NonblockingPipe = nonblockingPipe var Pipe = pipe var SetNonblock = setNonblock @@ -26,33 +28,45 @@ func Sigisblocked(i int) bool { type M = m var waitForSigusr1 struct { - park note - mID int64 + rdpipe int32 + wrpipe int32 + mID int64 } // WaitForSigusr1 blocks until a SIGUSR1 is received. It calls ready // when it is set up to receive SIGUSR1. The ready function should -// cause a SIGUSR1 to be sent. +// cause a SIGUSR1 to be sent. The r and w arguments are a pipe that +// the signal handler can use to report when the signal is received. // // Once SIGUSR1 is received, it returns the ID of the current M and -// the ID of the M the SIGUSR1 was received on. If no SIGUSR1 is -// received for timeoutNS nanoseconds, it returns -1. -func WaitForSigusr1(ready func(mp *M), timeoutNS int64) (int64, int64) { +// the ID of the M the SIGUSR1 was received on. If the caller writes +// a non-zero byte to w, WaitForSigusr1 returns immediately with -1, -1. +func WaitForSigusr1(r, w int32, ready func(mp *M)) (int64, int64) { lockOSThread() // Make sure we can receive SIGUSR1. unblocksig(_SIGUSR1) + waitForSigusr1.rdpipe = r + waitForSigusr1.wrpipe = w + mp := getg().m testSigusr1 = waitForSigusr1Callback ready(mp) - ok := notetsleepg(&waitForSigusr1.park, timeoutNS) - noteclear(&waitForSigusr1.park) + + // Wait for the signal. We use a pipe rather than a note + // because write is always async-signal-safe. + entersyscallblock() + var b byte + read(waitForSigusr1.rdpipe, noescape(unsafe.Pointer(&b)), 1) + exitsyscall() + gotM := waitForSigusr1.mID testSigusr1 = nil unlockOSThread() - if !ok { + if b != 0 { + // timeout signal from caller return -1, -1 } return mp.id, gotM @@ -69,7 +83,8 @@ func waitForSigusr1Callback(gp *g) bool { } else { waitForSigusr1.mID = gp.m.id } - notewakeup(&waitForSigusr1.park) + b := byte(0) + write(uintptr(waitForSigusr1.wrpipe), noescape(unsafe.Pointer(&b)), 1) return true }