Commit c485e8b5 authored by Ian Lance Taylor's avatar Ian Lance Taylor

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: default avatarRuss Cox <rsc@golang.org>
parent 623d653d
......@@ -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
}
......
// 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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment