]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use a pipe to wake up signal_recv on Darwin
authorIan Lance Taylor <iant@golang.org>
Fri, 28 Jun 2019 18:20:15 +0000 (11:20 -0700)
committerIan Lance Taylor <iant@golang.org>
Sun, 30 Jun 2019 05:48:31 +0000 (05:48 +0000)
The implementation of semaphores, and therefore notes, used on Darwin
is not async-signal-safe. The runtime has one case where a note needs
to be woken up from a signal handler: the call to notewakeup in sigsend.
That notewakeup call is only called on a single note, and it doesn't
need the full functionality of notes: nothing ever does a timed wait on it.
So change that one note to use a different implementation on Darwin,
based on a pipe. This lets the wakeup code use the write call, which is
async-signal-safe.

Fixes #31264

Change-Id: If705072d7a961dd908ea9d639c8d12b222c64806
Reviewed-on: https://go-review.googlesource.com/c/go/+/184169
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
13 files changed:
src/runtime/defs_darwin.go
src/runtime/defs_darwin_386.go
src/runtime/defs_darwin_amd64.go
src/runtime/defs_darwin_arm.go
src/runtime/defs_darwin_arm64.go
src/runtime/os_darwin.go
src/runtime/sigqueue.go
src/runtime/sigqueue_note.go [new file with mode: 0644]
src/runtime/sys_darwin.go
src/runtime/sys_darwin_386.s
src/runtime/sys_darwin_amd64.s
src/runtime/sys_darwin_arm.s
src/runtime/sys_darwin_arm64.s

index 61ae7a4186fc2060c3e776c97c0bd1837ccc1589..0cd133f6e02755c1fa74bda5367aa72fd3fb0f3c 100644 (file)
@@ -116,7 +116,11 @@ const (
        PTHREAD_CREATE_DETACHED = C.PTHREAD_CREATE_DETACHED
 
        F_SETFD    = C.F_SETFD
+       F_GETFL    = C.F_GETFL
+       F_SETFL    = C.F_SETFL
        FD_CLOEXEC = C.FD_CLOEXEC
+
+       O_NONBLOCK = C.O_NONBLOCK
 )
 
 type StackT C.struct_sigaltstack
index 43dc08a078e381eb87a1929b6166dd5e27a77c69..83928e784151558a57f5e5a215e807e656f4e14b 100644 (file)
@@ -94,7 +94,11 @@ const (
        _PTHREAD_CREATE_DETACHED = 0x2
 
        _F_SETFD    = 0x2
+       _F_GETFL    = 0x3
+       _F_SETFL    = 0x4
        _FD_CLOEXEC = 0x1
+
+       _O_NONBLOCK = 4
 )
 
 type stackt struct {
index 59b0effa135e2f076d43d4d87366f047094d30b4..45c34a8fc04fbd5f0d7cc72a92a3b43566d75a3a 100644 (file)
@@ -94,7 +94,11 @@ const (
        _PTHREAD_CREATE_DETACHED = 0x2
 
        _F_SETFD    = 0x2
+       _F_GETFL    = 0x3
+       _F_SETFL    = 0x4
        _FD_CLOEXEC = 0x1
+
+       _O_NONBLOCK = 4
 )
 
 type stackt struct {
index 243f52a5dfd8c3e0ecfe6074b09b393761010834..5e2af978a7b4cccce7fdbf86edda3d4fa6eed64c 100644 (file)
@@ -96,7 +96,11 @@ const (
        _PTHREAD_CREATE_DETACHED = 0x2
 
        _F_SETFD    = 0x2
+       _F_GETFL    = 0x3
+       _F_SETFL    = 0x4
        _FD_CLOEXEC = 0x1
+
+       _O_NONBLOCK = 4
 )
 
 type stackt struct {
index 7056074f468f411bf86c511b13e6438a6d8b2a63..f673eb7b24b031897196145398aea6b307645a09 100644 (file)
@@ -94,7 +94,11 @@ const (
        _PTHREAD_CREATE_DETACHED = 0x2
 
        _F_SETFD    = 0x2
+       _F_GETFL    = 0x3
+       _F_SETFL    = 0x4
        _FD_CLOEXEC = 0x1
+
+       _O_NONBLOCK = 4
 )
 
 type stackt struct {
index 819aaaca70f845d9d8d7e44434485f1526f108ef..1614b66c8a76d36222dd2189084258aa0474f051 100644 (file)
@@ -75,6 +75,52 @@ func semawakeup(mp *m) {
        pthread_mutex_unlock(&mp.mutex)
 }
 
+// The read and write file descriptors used by the sigNote functions.
+var sigNoteRead, sigNoteWrite int32
+
+// sigNoteSetup initializes an async-signal-safe note.
+//
+// The current implementation of notes on Darwin is not async-signal-safe,
+// because the functions pthread_mutex_lock, pthread_cond_signal, and
+// pthread_mutex_unlock, called by semawakeup, are not async-signal-safe.
+// There is only one case where we need to wake up a note from a signal
+// handler: the sigsend function. The signal handler code does not require
+// all the features of notes: it does not need to do a timed wait.
+// This is a separate implementation of notes, based on a pipe, that does
+// not support timed waits but is async-signal-safe.
+func sigNoteSetup(*note) {
+       if sigNoteRead != 0 || sigNoteWrite != 0 {
+               throw("duplicate sigNoteSetup")
+       }
+       var errno int32
+       sigNoteRead, sigNoteWrite, errno = pipe()
+       if errno != 0 {
+               throw("pipe failed")
+       }
+       closeonexec(sigNoteRead)
+       closeonexec(sigNoteWrite)
+
+       // Make the write end of the pipe non-blocking, so that if the pipe
+       // buffer is somehow full we will not block in the signal handler.
+       // Leave the read end of the pipe blocking so that we will block
+       // in sigNoteSleep.
+       setNonblock(sigNoteWrite)
+}
+
+// sigNoteWakeup wakes up a thread sleeping on a note created by sigNoteSetup.
+func sigNoteWakeup(*note) {
+       var b byte
+       write(uintptr(sigNoteWrite), unsafe.Pointer(&b), 1)
+}
+
+// sigNoteSleep waits for a note created by sigNoteSetup to be woken.
+func sigNoteSleep(*note) {
+       entersyscallblock()
+       var b byte
+       read(sigNoteRead, unsafe.Pointer(&b), 1)
+       exitsyscall()
+}
+
 // BSD interface for threading.
 func osinit() {
        // pthread_create delayed until end of goenvs so that we
index a425433b20a8d55b9fb5f8a1425c414c14203bdd..b2ebb2b4574f17030ac0ca60297d3f290d304c61 100644 (file)
@@ -105,6 +105,10 @@ Send:
                        break Send
                case sigReceiving:
                        if atomic.Cas(&sig.state, sigReceiving, sigIdle) {
+                               if GOOS == "darwin" {
+                                       sigNoteWakeup(&sig.note)
+                                       break Send
+                               }
                                notewakeup(&sig.note)
                                break Send
                        }
@@ -136,6 +140,10 @@ func signal_recv() uint32 {
                                throw("signal_recv: inconsistent state")
                        case sigIdle:
                                if atomic.Cas(&sig.state, sigIdle, sigReceiving) {
+                                       if GOOS == "darwin" {
+                                               sigNoteSleep(&sig.note)
+                                               break Receive
+                                       }
                                        notetsleepg(&sig.note, -1)
                                        noteclear(&sig.note)
                                        break Receive
@@ -188,6 +196,10 @@ func signal_enable(s uint32) {
                // to use for initialization. It does not pass
                // signal information in m.
                sig.inuse = true // enable reception of signals; cannot disable
+               if GOOS == "darwin" {
+                       sigNoteSetup(&sig.note)
+                       return
+               }
                noteclear(&sig.note)
                return
        }
diff --git a/src/runtime/sigqueue_note.go b/src/runtime/sigqueue_note.go
new file mode 100644 (file)
index 0000000..16aeeb2
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The current implementation of notes on Darwin is not async-signal-safe,
+// so on Darwin the sigqueue code uses different functions to wake up the
+// signal_recv thread. This file holds the non-Darwin implementations of
+// those functions. These functions will never be called.
+
+// +build !darwin
+// +build !plan9
+
+package runtime
+
+func sigNoteSetup(*note) {
+       throw("sigNoteSetup")
+}
+
+func sigNoteSleep(*note) {
+       throw("sigNoteSleep")
+}
+
+func sigNoteWakeup(*note) {
+       throw("sigNoteWakeup")
+}
index b50d441d92a7af1131e6bcba653410b54756e717..376f76dbc52fb55523dc999f08e3d4050f13ba12 100644 (file)
@@ -197,6 +197,13 @@ func read(fd int32, p unsafe.Pointer, n int32) int32 {
 }
 func read_trampoline()
 
+func pipe() (r, w int32, errno int32) {
+       var p [2]int32
+       errno = libcCall(unsafe.Pointer(funcPC(pipe_trampoline)), noescape(unsafe.Pointer(&p)))
+       return p[0], p[1], errno
+}
+func pipe_trampoline()
+
 //go:nosplit
 //go:cgo_unsafe_args
 func closefd(fd int32) int32 {
@@ -395,6 +402,12 @@ func closeonexec(fd int32) {
        fcntl(fd, _F_SETFD, _FD_CLOEXEC)
 }
 
+//go:nosplit
+func setNonblock(fd int32) {
+       flags := fcntl(fd, _F_GETFL, 0)
+       fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
+}
+
 // Tell the linker that the libc_* functions are to be found
 // in a system library, with the libc_ prefix missing.
 
@@ -409,6 +422,7 @@ func closeonexec(fd int32) {
 //go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib"
 //go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib"
 //go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib"
+//go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib"
 
 //go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib"
 //go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib"
index 9a0b3607c2d1e70678d232d0f4fb8b00f4c18284..ac5f4e4d63c8d7945dc5e513e01f20ab71486753 100644 (file)
@@ -84,6 +84,21 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
        POPL    BP
        RET
 
+TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
+       PUSHL   BP
+       MOVL    SP, BP
+       SUBL    $8, SP
+       MOVL    16(SP), CX              // arg 1 pipefd
+       MOVL    AX, 0(SP)
+       CALL    libc_pipe(SB)
+       TESTL   AX, AX
+       JEQ     3(PC)
+       CALL    libc_error(SB)          // return negative errno value
+       NEGL    AX
+       MOVL    BP, SP
+       POPL    BP
+       RET
+
 TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
        PUSHL   BP
        MOVL    SP, BP
index bbe6bc14bff8c00397bac5cf78bb98c277a80d3a..87c8db8c82dc49e48c8d0895b55011781759b8be 100644 (file)
@@ -59,6 +59,17 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
        POPQ    BP
        RET
 
+TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
+       PUSHQ   BP
+       MOVQ    SP, BP
+       CALL    libc_pipe(SB)           // pointer already in DI
+       TESTL   AX, AX
+       JEQ     3(PC)
+       CALL    libc_error(SB)          // return negative errno value
+       NEGL    AX
+       POPQ    BP
+       RET
+
 TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0
        PUSHQ   BP
        MOVQ    SP, BP
index b1c22b32423840084f19d9af0a6cd43db4f0452e..996f8028a36b389ac4de6de9f814c9df0e139abc 100644 (file)
@@ -41,6 +41,14 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
        BL      libc_read(SB)
        RET
 
+TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
+       BL      libc_pipe(SB)   // pointer already in R0
+       CMP     $0, R0
+       BEQ     3(PC)
+       BL      libc_error(SB)  // return negative errno value
+       RSB     $0, R0, R0
+       RET
+
 TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
        MOVW    0(R0), R0       // arg 0 code
        BL libc_exit(SB)
index af03af37bba26486ed32387125dbb58a87c27e14..ac3ca74f6379773ab1a01710ea48d7b4741d94fb 100644 (file)
@@ -44,6 +44,14 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
        BL      libc_read(SB)
        RET
 
+TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
+       BL      libc_pipe(SB)   // pointer already in R0
+       CMP     $0, R0
+       BEQ     3(PC)
+       BL      libc_error(SB)  // return negative errno value
+       NEG     R0, R0
+       RET
+
 TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
        MOVW    0(R0), R0
        BL      libc_exit(SB)