summaryrefslogtreecommitdiffstats
path: root/src/runtime/netpoll_epoll.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/runtime/netpoll_epoll.go
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/runtime/netpoll_epoll.go')
-rw-r--r--src/runtime/netpoll_epoll.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go
new file mode 100644
index 0000000..e29b64d
--- /dev/null
+++ b/src/runtime/netpoll_epoll.go
@@ -0,0 +1,172 @@
+// Copyright 2013 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 linux
+
+package runtime
+
+import (
+ "runtime/internal/atomic"
+ "runtime/internal/syscall"
+ "unsafe"
+)
+
+var (
+ epfd int32 = -1 // epoll descriptor
+
+ netpollBreakRd, netpollBreakWr uintptr // for netpollBreak
+
+ netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak
+)
+
+func netpollinit() {
+ var errno uintptr
+ epfd, errno = syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
+ if errno != 0 {
+ println("runtime: epollcreate failed with", errno)
+ throw("runtime: netpollinit failed")
+ }
+ r, w, errpipe := nonblockingPipe()
+ if errpipe != 0 {
+ println("runtime: pipe failed with", -errpipe)
+ throw("runtime: pipe failed")
+ }
+ ev := syscall.EpollEvent{
+ Events: syscall.EPOLLIN,
+ }
+ *(**uintptr)(unsafe.Pointer(&ev.Data)) = &netpollBreakRd
+ errno = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, r, &ev)
+ if errno != 0 {
+ println("runtime: epollctl failed with", errno)
+ throw("runtime: epollctl failed")
+ }
+ netpollBreakRd = uintptr(r)
+ netpollBreakWr = uintptr(w)
+}
+
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return fd == uintptr(epfd) || fd == netpollBreakRd || fd == netpollBreakWr
+}
+
+func netpollopen(fd uintptr, pd *pollDesc) uintptr {
+ var ev syscall.EpollEvent
+ ev.Events = syscall.EPOLLIN | syscall.EPOLLOUT | syscall.EPOLLRDHUP | syscall.EPOLLET
+ tp := taggedPointerPack(unsafe.Pointer(pd), pd.fdseq.Load())
+ *(*taggedPointer)(unsafe.Pointer(&ev.Data)) = tp
+ return syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, int32(fd), &ev)
+}
+
+func netpollclose(fd uintptr) uintptr {
+ var ev syscall.EpollEvent
+ return syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, int32(fd), &ev)
+}
+
+func netpollarm(pd *pollDesc, mode int) {
+ throw("runtime: unused")
+}
+
+// netpollBreak interrupts an epollwait.
+func netpollBreak() {
+ // Failing to cas indicates there is an in-flight wakeup, so we're done here.
+ if !netpollWakeSig.CompareAndSwap(0, 1) {
+ return
+ }
+
+ for {
+ var b byte
+ n := write(netpollBreakWr, unsafe.Pointer(&b), 1)
+ if n == 1 {
+ break
+ }
+ if n == -_EINTR {
+ continue
+ }
+ if n == -_EAGAIN {
+ return
+ }
+ println("runtime: netpollBreak write failed with", -n)
+ throw("runtime: netpollBreak write failed")
+ }
+}
+
+// netpoll checks for ready network connections.
+// Returns list of goroutines that become runnable.
+// delay < 0: blocks indefinitely
+// delay == 0: does not block, just polls
+// delay > 0: block for up to that many nanoseconds
+func netpoll(delay int64) gList {
+ if epfd == -1 {
+ return gList{}
+ }
+ var waitms int32
+ if delay < 0 {
+ waitms = -1
+ } else if delay == 0 {
+ waitms = 0
+ } else if delay < 1e6 {
+ waitms = 1
+ } else if delay < 1e15 {
+ waitms = int32(delay / 1e6)
+ } else {
+ // An arbitrary cap on how long to wait for a timer.
+ // 1e9 ms == ~11.5 days.
+ waitms = 1e9
+ }
+ var events [128]syscall.EpollEvent
+retry:
+ n, errno := syscall.EpollWait(epfd, events[:], int32(len(events)), waitms)
+ if errno != 0 {
+ if errno != _EINTR {
+ println("runtime: epollwait on fd", epfd, "failed with", errno)
+ throw("runtime: netpoll failed")
+ }
+ // If a timed sleep was interrupted, just return to
+ // recalculate how long we should sleep now.
+ if waitms > 0 {
+ return gList{}
+ }
+ goto retry
+ }
+ var toRun gList
+ for i := int32(0); i < n; i++ {
+ ev := events[i]
+ if ev.Events == 0 {
+ continue
+ }
+
+ if *(**uintptr)(unsafe.Pointer(&ev.Data)) == &netpollBreakRd {
+ if ev.Events != syscall.EPOLLIN {
+ println("runtime: netpoll: break fd ready for", ev.Events)
+ throw("runtime: netpoll: break fd ready for something unexpected")
+ }
+ 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)))
+ netpollWakeSig.Store(0)
+ }
+ continue
+ }
+
+ var mode int32
+ if ev.Events&(syscall.EPOLLIN|syscall.EPOLLRDHUP|syscall.EPOLLHUP|syscall.EPOLLERR) != 0 {
+ mode += 'r'
+ }
+ if ev.Events&(syscall.EPOLLOUT|syscall.EPOLLHUP|syscall.EPOLLERR) != 0 {
+ mode += 'w'
+ }
+ if mode != 0 {
+ tp := *(*taggedPointer)(unsafe.Pointer(&ev.Data))
+ pd := (*pollDesc)(tp.pointer())
+ tag := tp.tag()
+ if pd.fdseq.Load() == tag {
+ pd.setEventErr(ev.Events == syscall.EPOLLERR, tag)
+ netpollready(&toRun, pd, mode)
+ }
+ }
+ }
+ return toRun
+}