Skip to content

Commit c485e8b

Browse files
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>
1 parent 623d653 commit c485e8b

13 files changed

+159
-0
lines changed

src/runtime/defs_darwin.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,11 @@ const (
116116
PTHREAD_CREATE_DETACHED = C.PTHREAD_CREATE_DETACHED
117117

118118
F_SETFD = C.F_SETFD
119+
F_GETFL = C.F_GETFL
120+
F_SETFL = C.F_SETFL
119121
FD_CLOEXEC = C.FD_CLOEXEC
122+
123+
O_NONBLOCK = C.O_NONBLOCK
120124
)
121125

122126
type StackT C.struct_sigaltstack

src/runtime/defs_darwin_386.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ const (
9494
_PTHREAD_CREATE_DETACHED = 0x2
9595

9696
_F_SETFD = 0x2
97+
_F_GETFL = 0x3
98+
_F_SETFL = 0x4
9799
_FD_CLOEXEC = 0x1
100+
101+
_O_NONBLOCK = 4
98102
)
99103

100104
type stackt struct {

src/runtime/defs_darwin_amd64.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ const (
9494
_PTHREAD_CREATE_DETACHED = 0x2
9595

9696
_F_SETFD = 0x2
97+
_F_GETFL = 0x3
98+
_F_SETFL = 0x4
9799
_FD_CLOEXEC = 0x1
100+
101+
_O_NONBLOCK = 4
98102
)
99103

100104
type stackt struct {

src/runtime/defs_darwin_arm.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ const (
9696
_PTHREAD_CREATE_DETACHED = 0x2
9797

9898
_F_SETFD = 0x2
99+
_F_GETFL = 0x3
100+
_F_SETFL = 0x4
99101
_FD_CLOEXEC = 0x1
102+
103+
_O_NONBLOCK = 4
100104
)
101105

102106
type stackt struct {

src/runtime/defs_darwin_arm64.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ const (
9494
_PTHREAD_CREATE_DETACHED = 0x2
9595

9696
_F_SETFD = 0x2
97+
_F_GETFL = 0x3
98+
_F_SETFL = 0x4
9799
_FD_CLOEXEC = 0x1
100+
101+
_O_NONBLOCK = 4
98102
)
99103

100104
type stackt struct {

src/runtime/os_darwin.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,52 @@ func semawakeup(mp *m) {
7575
pthread_mutex_unlock(&mp.mutex)
7676
}
7777

78+
// The read and write file descriptors used by the sigNote functions.
79+
var sigNoteRead, sigNoteWrite int32
80+
81+
// sigNoteSetup initializes an async-signal-safe note.
82+
//
83+
// The current implementation of notes on Darwin is not async-signal-safe,
84+
// because the functions pthread_mutex_lock, pthread_cond_signal, and
85+
// pthread_mutex_unlock, called by semawakeup, are not async-signal-safe.
86+
// There is only one case where we need to wake up a note from a signal
87+
// handler: the sigsend function. The signal handler code does not require
88+
// all the features of notes: it does not need to do a timed wait.
89+
// This is a separate implementation of notes, based on a pipe, that does
90+
// not support timed waits but is async-signal-safe.
91+
func sigNoteSetup(*note) {
92+
if sigNoteRead != 0 || sigNoteWrite != 0 {
93+
throw("duplicate sigNoteSetup")
94+
}
95+
var errno int32
96+
sigNoteRead, sigNoteWrite, errno = pipe()
97+
if errno != 0 {
98+
throw("pipe failed")
99+
}
100+
closeonexec(sigNoteRead)
101+
closeonexec(sigNoteWrite)
102+
103+
// Make the write end of the pipe non-blocking, so that if the pipe
104+
// buffer is somehow full we will not block in the signal handler.
105+
// Leave the read end of the pipe blocking so that we will block
106+
// in sigNoteSleep.
107+
setNonblock(sigNoteWrite)
108+
}
109+
110+
// sigNoteWakeup wakes up a thread sleeping on a note created by sigNoteSetup.
111+
func sigNoteWakeup(*note) {
112+
var b byte
113+
write(uintptr(sigNoteWrite), unsafe.Pointer(&b), 1)
114+
}
115+
116+
// sigNoteSleep waits for a note created by sigNoteSetup to be woken.
117+
func sigNoteSleep(*note) {
118+
entersyscallblock()
119+
var b byte
120+
read(sigNoteRead, unsafe.Pointer(&b), 1)
121+
exitsyscall()
122+
}
123+
78124
// BSD interface for threading.
79125
func osinit() {
80126
// pthread_create delayed until end of goenvs so that we

src/runtime/sigqueue.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ Send:
105105
break Send
106106
case sigReceiving:
107107
if atomic.Cas(&sig.state, sigReceiving, sigIdle) {
108+
if GOOS == "darwin" {
109+
sigNoteWakeup(&sig.note)
110+
break Send
111+
}
108112
notewakeup(&sig.note)
109113
break Send
110114
}
@@ -136,6 +140,10 @@ func signal_recv() uint32 {
136140
throw("signal_recv: inconsistent state")
137141
case sigIdle:
138142
if atomic.Cas(&sig.state, sigIdle, sigReceiving) {
143+
if GOOS == "darwin" {
144+
sigNoteSleep(&sig.note)
145+
break Receive
146+
}
139147
notetsleepg(&sig.note, -1)
140148
noteclear(&sig.note)
141149
break Receive
@@ -188,6 +196,10 @@ func signal_enable(s uint32) {
188196
// to use for initialization. It does not pass
189197
// signal information in m.
190198
sig.inuse = true // enable reception of signals; cannot disable
199+
if GOOS == "darwin" {
200+
sigNoteSetup(&sig.note)
201+
return
202+
}
191203
noteclear(&sig.note)
192204
return
193205
}

src/runtime/sigqueue_note.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// The current implementation of notes on Darwin is not async-signal-safe,
6+
// so on Darwin the sigqueue code uses different functions to wake up the
7+
// signal_recv thread. This file holds the non-Darwin implementations of
8+
// those functions. These functions will never be called.
9+
10+
// +build !darwin
11+
// +build !plan9
12+
13+
package runtime
14+
15+
func sigNoteSetup(*note) {
16+
throw("sigNoteSetup")
17+
}
18+
19+
func sigNoteSleep(*note) {
20+
throw("sigNoteSleep")
21+
}
22+
23+
func sigNoteWakeup(*note) {
24+
throw("sigNoteWakeup")
25+
}

src/runtime/sys_darwin.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ func read(fd int32, p unsafe.Pointer, n int32) int32 {
197197
}
198198
func read_trampoline()
199199

200+
func pipe() (r, w int32, errno int32) {
201+
var p [2]int32
202+
errno = libcCall(unsafe.Pointer(funcPC(pipe_trampoline)), noescape(unsafe.Pointer(&p)))
203+
return p[0], p[1], errno
204+
}
205+
func pipe_trampoline()
206+
200207
//go:nosplit
201208
//go:cgo_unsafe_args
202209
func closefd(fd int32) int32 {
@@ -395,6 +402,12 @@ func closeonexec(fd int32) {
395402
fcntl(fd, _F_SETFD, _FD_CLOEXEC)
396403
}
397404

405+
//go:nosplit
406+
func setNonblock(fd int32) {
407+
flags := fcntl(fd, _F_GETFL, 0)
408+
fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
409+
}
410+
398411
// Tell the linker that the libc_* functions are to be found
399412
// in a system library, with the libc_ prefix missing.
400413

@@ -409,6 +422,7 @@ func closeonexec(fd int32) {
409422
//go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib"
410423
//go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib"
411424
//go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib"
425+
//go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib"
412426

413427
//go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib"
414428
//go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib"

src/runtime/sys_darwin_386.s

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
8484
POPL BP
8585
RET
8686

87+
TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
88+
PUSHL BP
89+
MOVL SP, BP
90+
SUBL $8, SP
91+
MOVL 16(SP), CX // arg 1 pipefd
92+
MOVL AX, 0(SP)
93+
CALL libc_pipe(SB)
94+
TESTL AX, AX
95+
JEQ 3(PC)
96+
CALL libc_error(SB) // return negative errno value
97+
NEGL AX
98+
MOVL BP, SP
99+
POPL BP
100+
RET
101+
87102
TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
88103
PUSHL BP
89104
MOVL SP, BP

src/runtime/sys_darwin_amd64.s

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
5959
POPQ BP
6060
RET
6161

62+
TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
63+
PUSHQ BP
64+
MOVQ SP, BP
65+
CALL libc_pipe(SB) // pointer already in DI
66+
TESTL AX, AX
67+
JEQ 3(PC)
68+
CALL libc_error(SB) // return negative errno value
69+
NEGL AX
70+
POPQ BP
71+
RET
72+
6273
TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0
6374
PUSHQ BP
6475
MOVQ SP, BP

src/runtime/sys_darwin_arm.s

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
4141
BL libc_read(SB)
4242
RET
4343

44+
TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
45+
BL libc_pipe(SB) // pointer already in R0
46+
CMP $0, R0
47+
BEQ 3(PC)
48+
BL libc_error(SB) // return negative errno value
49+
RSB $0, R0, R0
50+
RET
51+
4452
TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
4553
MOVW 0(R0), R0 // arg 0 code
4654
BL libc_exit(SB)

src/runtime/sys_darwin_arm64.s

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
4444
BL libc_read(SB)
4545
RET
4646

47+
TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
48+
BL libc_pipe(SB) // pointer already in R0
49+
CMP $0, R0
50+
BEQ 3(PC)
51+
BL libc_error(SB) // return negative errno value
52+
NEG R0, R0
53+
RET
54+
4755
TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
4856
MOVW 0(R0), R0
4957
BL libc_exit(SB)

0 commit comments

Comments
 (0)