runtime: utilize EVFILT_USER to wake up kevent for kqueue

Fixes #66760

Change-Id: I6ba5bc5b00506b66cb8dc3984a61f32a6358d9bc
Reviewed-on: https://go-review.googlesource.com/c/go/+/577895
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Run-TryBot: Andy Pan <panjf2000@gmail.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
Andy Pan 2024-04-10 16:48:09 +08:00 committed by Gopher Robot
parent 1962672620
commit 519f6a00e4
19 changed files with 240 additions and 47 deletions

View File

@ -84,12 +84,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = 0x0
_EVFILT_WRITE = 0x1
_EVFILT_USER = 0x8
_NOTE_TRIGGER = 0x1000000
)
type sigset struct {

View File

@ -84,12 +84,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = 0x0
_EVFILT_WRITE = 0x1
_EVFILT_USER = 0x8
_NOTE_TRIGGER = 0x1000000
)
type sigset struct {

View File

@ -84,12 +84,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = 0x0
_EVFILT_WRITE = 0x1
_EVFILT_USER = 0x8
_NOTE_TRIGGER = 0x1000000
)
type sigset struct {

View File

@ -84,12 +84,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = 0x0
_EVFILT_WRITE = 0x1
_EVFILT_USER = 0x8
_NOTE_TRIGGER = 0x1000000
)
type sigset struct {

View File

@ -106,12 +106,17 @@ const (
EV_ADD = C.EV_ADD
EV_DELETE = C.EV_DELETE
EV_ENABLE = C.EV_ENABLE
EV_DISABLE = C.EV_DISABLE
EV_CLEAR = C.EV_CLEAR
EV_RECEIPT = C.EV_RECEIPT
EV_ERROR = C.EV_ERROR
EV_EOF = C.EV_EOF
EVFILT_READ = C.EVFILT_READ
EVFILT_WRITE = C.EVFILT_WRITE
EVFILT_USER = C.EVFILT_USER
NOTE_TRIGGER = C.NOTE_TRIGGER
PTHREAD_CREATE_DETACHED = C.PTHREAD_CREATE_DETACHED

View File

@ -85,12 +85,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xa
_NOTE_TRIGGER = 0x1000000
_PTHREAD_CREATE_DETACHED = 0x2

View File

@ -85,12 +85,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xa
_NOTE_TRIGGER = 0x1000000
_PTHREAD_CREATE_DETACHED = 0x2

View File

@ -109,11 +109,16 @@ const (
EV_ADD = C.EV_ADD
EV_DELETE = C.EV_DELETE
EV_ENABLE = C.EV_ENABLE
EV_DISABLE = C.EV_DISABLE
EV_CLEAR = C.EV_CLEAR
EV_ERROR = C.EV_ERROR
EV_EOF = C.EV_EOF
EVFILT_READ = C.EVFILT_READ
EVFILT_WRITE = C.EVFILT_WRITE
EVFILT_USER = C.EVFILT_USER
NOTE_TRIGGER = C.NOTE_TRIGGER
)
type Rtprio C.struct_rtprio

View File

@ -88,11 +88,16 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0x9
_NOTE_TRIGGER = 0x1000000
)
type rtprio struct {

View File

@ -136,12 +136,17 @@ const (
EV_ADD = C.EV_ADD
EV_DELETE = C.EV_DELETE
EV_ENABLE = C.EV_ENABLE
EV_DISABLE = C.EV_DISABLE
EV_CLEAR = C.EV_CLEAR
EV_RECEIPT = C.EV_RECEIPT
EV_ERROR = C.EV_ERROR
EV_EOF = C.EV_EOF
EVFILT_READ = C.EVFILT_READ
EVFILT_WRITE = C.EVFILT_WRITE
EVFILT_USER = C.EVFILT_USER
NOTE_TRIGGER = C.NOTE_TRIGGER
)
type Rtprio C.struct_rtprio

View File

@ -104,12 +104,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xb
_NOTE_TRIGGER = 0x1000000
)
type rtprio struct {

View File

@ -104,12 +104,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xb
_NOTE_TRIGGER = 0x1000000
)
type rtprio struct {

View File

@ -104,12 +104,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xb
_NOTE_TRIGGER = 0x1000000
)
type rtprio struct {

View File

@ -104,12 +104,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xb
_NOTE_TRIGGER = 0x1000000
)
type rtprio struct {

View File

@ -103,12 +103,17 @@ const (
_EV_ADD = 0x1
_EV_DELETE = 0x2
_EV_ENABLE = 0x4
_EV_DISABLE = 0x8
_EV_CLEAR = 0x20
_EV_RECEIPT = 0x40
_EV_ERROR = 0x4000
_EV_EOF = 0x8000
_EVFILT_READ = -0x1
_EVFILT_WRITE = -0x2
_EVFILT_USER = -0xb
_NOTE_TRIGGER = 0x1000000
)
type rtprio struct {

View File

@ -110,12 +110,17 @@ const (
EV_ADD = C.EV_ADD
EV_DELETE = C.EV_DELETE
EV_ENABLE = C.EV_ENABLE
EV_DISABLE = C.EV_DISABLE
EV_CLEAR = C.EV_CLEAR
EV_RECEIPT = 0
EV_ERROR = C.EV_ERROR
EV_EOF = C.EV_EOF
EVFILT_READ = C.EVFILT_READ
EVFILT_WRITE = C.EVFILT_WRITE
EVFILT_USER = C.EVFILT_USER
NOTE_TRIGGER = C.NOTE_TRIGGER
)
type Sigset C.sigset_t

View File

@ -15,10 +15,7 @@ import (
)
var (
kq int32 = -1
netpollBreakRd, netpollBreakWr uintptr // for netpollBreak
kq int32 = -1
netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak
)
@ -29,27 +26,7 @@ func netpollinit() {
throw("runtime: netpollinit failed")
}
closeonexec(kq)
r, w, errno := nonblockingPipe()
if errno != 0 {
println("runtime: pipe failed with", -errno)
throw("runtime: pipe failed")
}
ev := keventt{
filter: _EVFILT_READ,
flags: _EV_ADD,
}
*(*uintptr)(unsafe.Pointer(&ev.ident)) = uintptr(r)
n := kevent(kq, &ev, 1, nil, 0, nil)
if n < 0 {
println("runtime: kevent failed with", -n)
throw("runtime: kevent failed")
}
netpollBreakRd = uintptr(r)
netpollBreakWr = uintptr(w)
}
func netpollIsPollDescriptor(fd uintptr) bool {
return fd == uintptr(kq) || fd == netpollBreakRd || fd == netpollBreakWr
addWakeupEvent(kq)
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
@ -99,18 +76,7 @@ func netpollBreak() {
return
}
for {
var b byte
n := write(netpollBreakWr, unsafe.Pointer(&b), 1)
if n == 1 || n == -_EAGAIN {
break
}
if n == -_EINTR {
continue
}
println("runtime: netpollBreak write failed with", -n)
throw("runtime: netpollBreak write failed")
}
wakeNetpoll(kq)
}
// netpoll checks for ready network connections.
@ -159,17 +125,11 @@ retry:
for i := 0; i < int(n); i++ {
ev := &events[i]
if uintptr(ev.ident) == netpollBreakRd {
if ev.filter != _EVFILT_READ {
println("runtime: netpoll: break fd ready for", ev.filter)
throw("runtime: netpoll: break fd ready for something unexpected")
}
if isWakeup(ev) {
if delay != 0 {
// netpollBreak could be picked up by a
// nonblocking poll. Only read the byte
// if blocking.
var tmp [16]byte
read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp)))
// netpollBreak could be picked up by a nonblocking poll.
// Only call drainWakeupEvent and reset the netpollWakeSig if blocking.
drainWakeupEvent(kq)
netpollWakeSig.Store(0)
}
continue

View File

@ -0,0 +1,80 @@
// Copyright 2024 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.
//go:build darwin || dragonfly || freebsd
package runtime
// Magic number of identifier used for EVFILT_USER.
// This number had zero Google results when it's created.
// That way, people will be directed here when this number
// get printed somehow and they search for it.
const kqIdent = 0xee1eb9f4
func addWakeupEvent(_ int32) {
ev := keventt{
ident: kqIdent,
filter: _EVFILT_USER,
flags: _EV_ADD,
}
for {
n := kevent(kq, &ev, 1, nil, 0, nil)
if n == 0 {
break
}
if n == -_EINTR {
// All changes contained in the changelist should have been applied
// before returning EINTR. But let's be skeptical and retry it anyway,
// to make a 100% commitment.
continue
}
println("runtime: kevent for EVFILT_USER failed with", -n)
throw("runtime: kevent failed")
}
}
func wakeNetpoll(kq int32) {
ev := keventt{
ident: kqIdent,
filter: _EVFILT_USER,
flags: _EV_ENABLE,
fflags: _NOTE_TRIGGER,
}
for {
n := kevent(kq, &ev, 1, nil, 0, nil)
if n == 0 {
break
}
if n == -_EINTR {
// Check out the comment in addWakeupEvent.
continue
}
println("runtime: netpollBreak write failed with", -n)
throw("runtime: netpollBreak write failed")
}
}
func isWakeup(ev *keventt) bool {
if ev.filter == _EVFILT_USER {
if ev.ident == kqIdent {
return true
}
println("runtime: netpoll: break fd ready for", ev.ident)
throw("runtime: netpoll: break fd ready for something unexpected")
}
return false
}
func drainWakeupEvent(kq int32) {
ev := keventt{
ident: kqIdent,
filter: _EVFILT_USER,
flags: _EV_DISABLE,
}
kevent(kq, &ev, 1, nil, 0, nil)
}
func netpollIsPollDescriptor(fd uintptr) bool {
return fd == uintptr(kq)
}

View File

@ -0,0 +1,73 @@
// Copyright 2024 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.
//go:build netbsd || openbsd
package runtime
import "unsafe"
// TODO(panjf2000): NetBSD didn't implement EVFILT_USER for user-established events
// until NetBSD 10.0, check out https://www.netbsd.org/releases/formal-10/NetBSD-10.0.html
// Therefore we use the pipe to wake up the kevent on NetBSD at this point. Get back here
// and switch to EVFILT_USER when we bump up the minimal requirement of NetBSD to 10.0.
// Alternatively, maybe we can use EVFILT_USER on the NetBSD by checking the kernel version
// via uname(3) and fall back to the pipe if the kernel version is older than 10.0.
var netpollBreakRd, netpollBreakWr uintptr // for netpollBreak
func addWakeupEvent(kq int32) {
r, w, errno := nonblockingPipe()
if errno != 0 {
println("runtime: pipe failed with", -errno)
throw("runtime: pipe failed")
}
ev := keventt{
filter: _EVFILT_READ,
flags: _EV_ADD,
}
*(*uintptr)(unsafe.Pointer(&ev.ident)) = uintptr(r)
n := kevent(kq, &ev, 1, nil, 0, nil)
if n < 0 {
println("runtime: kevent failed with", -n)
throw("runtime: kevent failed")
}
netpollBreakRd = uintptr(r)
netpollBreakWr = uintptr(w)
}
func wakeNetpoll(_ int32) {
for {
var b byte
n := write(netpollBreakWr, unsafe.Pointer(&b), 1)
if n == 1 || n == -_EAGAIN {
break
}
if n == -_EINTR {
continue
}
println("runtime: netpollBreak write failed with", -n)
throw("runtime: netpollBreak write failed")
}
}
func isWakeup(ev *keventt) bool {
if uintptr(ev.ident) == netpollBreakRd {
if ev.filter == _EVFILT_READ {
return true
}
println("runtime: netpoll: break fd ready for", ev.filter)
throw("runtime: netpoll: break fd ready for something unexpected")
}
return false
}
func drainWakeupEvent(_ int32) {
var buf [16]byte
read(int32(netpollBreakRd), noescape(unsafe.Pointer(&buf[0])), int32(len(buf)))
}
func netpollIsPollDescriptor(fd uintptr) bool {
return fd == uintptr(kq) || fd == netpollBreakRd || fd == netpollBreakWr
}