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
|
PTHREAD_CREATE_DETACHED = C.PTHREAD_CREATE_DETACHED
|
||||||
|
|
||||||
F_SETFD = C.F_SETFD
|
F_SETFD = C.F_SETFD
|
||||||
|
F_GETFL = C.F_GETFL
|
||||||
|
F_SETFL = C.F_SETFL
|
||||||
FD_CLOEXEC = C.FD_CLOEXEC
|
FD_CLOEXEC = C.FD_CLOEXEC
|
||||||
|
|
||||||
|
O_NONBLOCK = C.O_NONBLOCK
|
||||||
)
|
)
|
||||||
|
|
||||||
type StackT C.struct_sigaltstack
|
type StackT C.struct_sigaltstack
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,11 @@ const (
|
||||||
_PTHREAD_CREATE_DETACHED = 0x2
|
_PTHREAD_CREATE_DETACHED = 0x2
|
||||||
|
|
||||||
_F_SETFD = 0x2
|
_F_SETFD = 0x2
|
||||||
|
_F_GETFL = 0x3
|
||||||
|
_F_SETFL = 0x4
|
||||||
_FD_CLOEXEC = 0x1
|
_FD_CLOEXEC = 0x1
|
||||||
|
|
||||||
|
_O_NONBLOCK = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
type stackt struct {
|
type stackt struct {
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,11 @@ const (
|
||||||
_PTHREAD_CREATE_DETACHED = 0x2
|
_PTHREAD_CREATE_DETACHED = 0x2
|
||||||
|
|
||||||
_F_SETFD = 0x2
|
_F_SETFD = 0x2
|
||||||
|
_F_GETFL = 0x3
|
||||||
|
_F_SETFL = 0x4
|
||||||
_FD_CLOEXEC = 0x1
|
_FD_CLOEXEC = 0x1
|
||||||
|
|
||||||
|
_O_NONBLOCK = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
type stackt struct {
|
type stackt struct {
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,11 @@ const (
|
||||||
_PTHREAD_CREATE_DETACHED = 0x2
|
_PTHREAD_CREATE_DETACHED = 0x2
|
||||||
|
|
||||||
_F_SETFD = 0x2
|
_F_SETFD = 0x2
|
||||||
|
_F_GETFL = 0x3
|
||||||
|
_F_SETFL = 0x4
|
||||||
_FD_CLOEXEC = 0x1
|
_FD_CLOEXEC = 0x1
|
||||||
|
|
||||||
|
_O_NONBLOCK = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
type stackt struct {
|
type stackt struct {
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,11 @@ const (
|
||||||
_PTHREAD_CREATE_DETACHED = 0x2
|
_PTHREAD_CREATE_DETACHED = 0x2
|
||||||
|
|
||||||
_F_SETFD = 0x2
|
_F_SETFD = 0x2
|
||||||
|
_F_GETFL = 0x3
|
||||||
|
_F_SETFL = 0x4
|
||||||
_FD_CLOEXEC = 0x1
|
_FD_CLOEXEC = 0x1
|
||||||
|
|
||||||
|
_O_NONBLOCK = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
type stackt struct {
|
type stackt struct {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,52 @@ func semawakeup(mp *m) {
|
||||||
pthread_mutex_unlock(&mp.mutex)
|
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.
|
// BSD interface for threading.
|
||||||
func osinit() {
|
func osinit() {
|
||||||
// pthread_create delayed until end of goenvs so that we
|
// pthread_create delayed until end of goenvs so that we
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,10 @@ Send:
|
||||||
break Send
|
break Send
|
||||||
case sigReceiving:
|
case sigReceiving:
|
||||||
if atomic.Cas(&sig.state, sigReceiving, sigIdle) {
|
if atomic.Cas(&sig.state, sigReceiving, sigIdle) {
|
||||||
|
if GOOS == "darwin" {
|
||||||
|
sigNoteWakeup(&sig.note)
|
||||||
|
break Send
|
||||||
|
}
|
||||||
notewakeup(&sig.note)
|
notewakeup(&sig.note)
|
||||||
break Send
|
break Send
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +140,10 @@ func signal_recv() uint32 {
|
||||||
throw("signal_recv: inconsistent state")
|
throw("signal_recv: inconsistent state")
|
||||||
case sigIdle:
|
case sigIdle:
|
||||||
if atomic.Cas(&sig.state, sigIdle, sigReceiving) {
|
if atomic.Cas(&sig.state, sigIdle, sigReceiving) {
|
||||||
|
if GOOS == "darwin" {
|
||||||
|
sigNoteSleep(&sig.note)
|
||||||
|
break Receive
|
||||||
|
}
|
||||||
notetsleepg(&sig.note, -1)
|
notetsleepg(&sig.note, -1)
|
||||||
noteclear(&sig.note)
|
noteclear(&sig.note)
|
||||||
break Receive
|
break Receive
|
||||||
|
|
@ -188,6 +196,10 @@ func signal_enable(s uint32) {
|
||||||
// to use for initialization. It does not pass
|
// to use for initialization. It does not pass
|
||||||
// signal information in m.
|
// signal information in m.
|
||||||
sig.inuse = true // enable reception of signals; cannot disable
|
sig.inuse = true // enable reception of signals; cannot disable
|
||||||
|
if GOOS == "darwin" {
|
||||||
|
sigNoteSetup(&sig.note)
|
||||||
|
return
|
||||||
|
}
|
||||||
noteclear(&sig.note)
|
noteclear(&sig.note)
|
||||||
return
|
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 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:nosplit
|
||||||
//go:cgo_unsafe_args
|
//go:cgo_unsafe_args
|
||||||
func closefd(fd int32) int32 {
|
func closefd(fd int32) int32 {
|
||||||
|
|
@ -395,6 +402,12 @@ func closeonexec(fd int32) {
|
||||||
fcntl(fd, _F_SETFD, _FD_CLOEXEC)
|
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
|
// Tell the linker that the libc_* functions are to be found
|
||||||
// in a system library, with the libc_ prefix missing.
|
// 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_close close "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic libc_read read "/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_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_mmap mmap "/usr/lib/libSystem.B.dylib"
|
||||||
//go:cgo_import_dynamic libc_munmap munmap "/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
|
POPL BP
|
||||||
RET
|
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
|
TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
|
||||||
PUSHL BP
|
PUSHL BP
|
||||||
MOVL SP, BP
|
MOVL SP, BP
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,17 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
|
||||||
POPQ BP
|
POPQ BP
|
||||||
RET
|
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
|
TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0
|
||||||
PUSHQ BP
|
PUSHQ BP
|
||||||
MOVQ SP, BP
|
MOVQ SP, BP
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,14 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
|
||||||
BL libc_read(SB)
|
BL libc_read(SB)
|
||||||
RET
|
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
|
TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
|
||||||
MOVW 0(R0), R0 // arg 0 code
|
MOVW 0(R0), R0 // arg 0 code
|
||||||
BL libc_exit(SB)
|
BL libc_exit(SB)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,14 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
|
||||||
BL libc_read(SB)
|
BL libc_read(SB)
|
||||||
RET
|
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
|
TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
|
||||||
MOVW 0(R0), R0
|
MOVW 0(R0), R0
|
||||||
BL libc_exit(SB)
|
BL libc_exit(SB)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue