github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/netpoll_epoll.go (about) 1 // Copyright 2013 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 //go:build linux 6 7 package runtime 8 9 import ( 10 "runtime/internal/atomic" 11 "runtime/internal/syscall" 12 "unsafe" 13 ) 14 15 var ( 16 epfd int32 = -1 // epoll descriptor 17 18 netpollBreakRd, netpollBreakWr uintptr // for netpollBreak 19 20 netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak 21 ) 22 23 func netpollinit() { 24 var errno uintptr 25 epfd, errno = syscall.EpollCreate1(syscall.EPOLL_CLOEXEC) 26 if errno != 0 { 27 println("runtime: epollcreate failed with", errno) 28 throw("runtime: netpollinit failed") 29 } 30 r, w, errpipe := nonblockingPipe() 31 if errpipe != 0 { 32 println("runtime: pipe failed with", -errpipe) 33 throw("runtime: pipe failed") 34 } 35 ev := syscall.EpollEvent{ 36 Events: syscall.EPOLLIN, 37 } 38 *(**uintptr)(unsafe.Pointer(&ev.Data)) = &netpollBreakRd 39 errno = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, r, &ev) 40 if errno != 0 { 41 println("runtime: epollctl failed with", errno) 42 throw("runtime: epollctl failed") 43 } 44 netpollBreakRd = uintptr(r) 45 netpollBreakWr = uintptr(w) 46 } 47 48 func netpollIsPollDescriptor(fd uintptr) bool { 49 return fd == uintptr(epfd) || fd == netpollBreakRd || fd == netpollBreakWr 50 } 51 52 func netpollopen(fd uintptr, pd *pollDesc) uintptr { 53 var ev syscall.EpollEvent 54 ev.Events = syscall.EPOLLIN | syscall.EPOLLOUT | syscall.EPOLLRDHUP | syscall.EPOLLET 55 tp := taggedPointerPack(unsafe.Pointer(pd), pd.fdseq.Load()) 56 *(*taggedPointer)(unsafe.Pointer(&ev.Data)) = tp 57 return syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, int32(fd), &ev) 58 } 59 60 func netpollclose(fd uintptr) uintptr { 61 var ev syscall.EpollEvent 62 return syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, int32(fd), &ev) 63 } 64 65 func netpollarm(pd *pollDesc, mode int) { 66 throw("runtime: unused") 67 } 68 69 // netpollBreak interrupts an epollwait. 70 func netpollBreak() { 71 // Failing to cas indicates there is an in-flight wakeup, so we're done here. 72 if !netpollWakeSig.CompareAndSwap(0, 1) { 73 return 74 } 75 76 for { 77 var b byte 78 n := write(netpollBreakWr, unsafe.Pointer(&b), 1) 79 if n == 1 { 80 break 81 } 82 if n == -_EINTR { 83 continue 84 } 85 if n == -_EAGAIN { 86 return 87 } 88 println("runtime: netpollBreak write failed with", -n) 89 throw("runtime: netpollBreak write failed") 90 } 91 } 92 93 // netpoll checks for ready network connections. 94 // Returns list of goroutines that become runnable. 95 // delay < 0: blocks indefinitely 96 // delay == 0: does not block, just polls 97 // delay > 0: block for up to that many nanoseconds 98 func netpoll(delay int64) gList { 99 if epfd == -1 { 100 return gList{} 101 } 102 var waitms int32 103 if delay < 0 { 104 waitms = -1 105 } else if delay == 0 { 106 waitms = 0 107 } else if delay < 1e6 { 108 waitms = 1 109 } else if delay < 1e15 { 110 waitms = int32(delay / 1e6) 111 } else { 112 // An arbitrary cap on how long to wait for a timer. 113 // 1e9 ms == ~11.5 days. 114 waitms = 1e9 115 } 116 var events [128]syscall.EpollEvent 117 retry: 118 n, errno := syscall.EpollWait(epfd, events[:], int32(len(events)), waitms) 119 if errno != 0 { 120 if errno != _EINTR { 121 println("runtime: epollwait on fd", epfd, "failed with", errno) 122 throw("runtime: netpoll failed") 123 } 124 // If a timed sleep was interrupted, just return to 125 // recalculate how long we should sleep now. 126 if waitms > 0 { 127 return gList{} 128 } 129 goto retry 130 } 131 var toRun gList 132 for i := int32(0); i < n; i++ { 133 ev := events[i] 134 if ev.Events == 0 { 135 continue 136 } 137 138 if *(**uintptr)(unsafe.Pointer(&ev.Data)) == &netpollBreakRd { 139 if ev.Events != syscall.EPOLLIN { 140 println("runtime: netpoll: break fd ready for", ev.Events) 141 throw("runtime: netpoll: break fd ready for something unexpected") 142 } 143 if delay != 0 { 144 // netpollBreak could be picked up by a 145 // nonblocking poll. Only read the byte 146 // if blocking. 147 var tmp [16]byte 148 read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp))) 149 netpollWakeSig.Store(0) 150 } 151 continue 152 } 153 154 var mode int32 155 if ev.Events&(syscall.EPOLLIN|syscall.EPOLLRDHUP|syscall.EPOLLHUP|syscall.EPOLLERR) != 0 { 156 mode += 'r' 157 } 158 if ev.Events&(syscall.EPOLLOUT|syscall.EPOLLHUP|syscall.EPOLLERR) != 0 { 159 mode += 'w' 160 } 161 if mode != 0 { 162 tp := *(*taggedPointer)(unsafe.Pointer(&ev.Data)) 163 pd := (*pollDesc)(tp.pointer()) 164 tag := tp.tag() 165 if pd.fdseq.Load() == tag { 166 pd.setEventErr(ev.Events == syscall.EPOLLERR, tag) 167 netpollready(&toRun, pd, mode) 168 } 169 } 170 } 171 return toRun 172 }