diff options
Diffstat (limited to '')
-rw-r--r-- | src/runtime/netpoll_epoll.go | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go new file mode 100644 index 0000000..7164a59 --- /dev/null +++ b/src/runtime/netpoll_epoll.go @@ -0,0 +1,167 @@ +// 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 + *(**pollDesc)(unsafe.Pointer(&ev.Data)) = pd + 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 { + pd := *(**pollDesc)(unsafe.Pointer(&ev.Data)) + pd.setEventErr(ev.Events == syscall.EPOLLERR) + netpollready(&toRun, pd, mode) + } + } + return toRun +} |