mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: use a pipe to wake up signal_recv on Darwin
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>
This commit is contained in:
parent
623d653db7
commit
c485e8b559
13 changed files with 159 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
25
src/runtime/sigqueue_note.go
Normal file
25
src/runtime/sigqueue_note.go
Normal file
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue