github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/runtime/netpoll_kqueue.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 darwin || dragonfly || freebsd || netbsd || openbsd 6 7 package runtime 8 9 // Integrated network poller (kqueue-based implementation). 10 11 import ( 12 "runtime/internal/atomic" 13 "unsafe" 14 ) 15 16 var ( 17 kq int32 = -1 18 19 netpollBreakRd, netpollBreakWr uintptr // for netpollBreak 20 21 netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak 22 ) 23 24 func netpollinit() { 25 kq = kqueue() 26 if kq < 0 { 27 println("runtime: kqueue failed with", -kq) 28 throw("runtime: netpollinit failed") 29 } 30 closeonexec(kq) 31 r, w, errno := nonblockingPipe() 32 if errno != 0 { 33 println("runtime: pipe failed with", -errno) 34 throw("runtime: pipe failed") 35 } 36 ev := keventt{ 37 filter: _EVFILT_READ, 38 flags: _EV_ADD, 39 } 40 *(*uintptr)(unsafe.Pointer(&ev.ident)) = uintptr(r) 41 n := kevent(kq, &ev, 1, nil, 0, nil) 42 if n < 0 { 43 println("runtime: kevent failed with", -n) 44 throw("runtime: kevent failed") 45 } 46 netpollBreakRd = uintptr(r) 47 netpollBreakWr = uintptr(w) 48 } 49 50 func netpollIsPollDescriptor(fd uintptr) bool { 51 return fd == uintptr(kq) || fd == netpollBreakRd || fd == netpollBreakWr 52 } 53 54 func netpollopen(fd uintptr, pd *pollDesc) int32 { 55 // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR) 56 // for the whole fd lifetime. The notifications are automatically unregistered 57 // when fd is closed. 58 var ev [2]keventt 59 *(*uintptr)(unsafe.Pointer(&ev[0].ident)) = fd 60 ev[0].filter = _EVFILT_READ 61 ev[0].flags = _EV_ADD | _EV_CLEAR 62 ev[0].fflags = 0 63 ev[0].data = 0 64 ev[0].udata = (*byte)(unsafe.Pointer(pd)) 65 ev[1] = ev[0] 66 ev[1].filter = _EVFILT_WRITE 67 n := kevent(kq, &ev[0], 2, nil, 0, nil) 68 if n < 0 { 69 return -n 70 } 71 return 0 72 } 73 74 func netpollclose(fd uintptr) int32 { 75 // Don't need to unregister because calling close() 76 // on fd will remove any kevents that reference the descriptor. 77 return 0 78 } 79 80 func netpollarm(pd *pollDesc, mode int) { 81 throw("runtime: unused") 82 } 83 84 // netpollBreak interrupts a kevent. 85 func netpollBreak() { 86 // Failing to cas indicates there is an in-flight wakeup, so we're done here. 87 if !netpollWakeSig.CompareAndSwap(0, 1) { 88 return 89 } 90 91 for { 92 var b byte 93 n := write(netpollBreakWr, unsafe.Pointer(&b), 1) 94 if n == 1 || n == -_EAGAIN { 95 break 96 } 97 if n == -_EINTR { 98 continue 99 } 100 println("runtime: netpollBreak write failed with", -n) 101 throw("runtime: netpollBreak write failed") 102 } 103 } 104 105 // netpoll checks for ready network connections. 106 // Returns list of goroutines that become runnable. 107 // delay < 0: blocks indefinitely 108 // delay == 0: does not block, just polls 109 // delay > 0: block for up to that many nanoseconds 110 func netpoll(delay int64) gList { 111 if kq == -1 { 112 return gList{} 113 } 114 var tp *timespec 115 var ts timespec 116 if delay < 0 { 117 tp = nil 118 } else if delay == 0 { 119 tp = &ts 120 } else { 121 ts.setNsec(delay) 122 if ts.tv_sec > 1e6 { 123 // Darwin returns EINVAL if the sleep time is too long. 124 ts.tv_sec = 1e6 125 } 126 tp = &ts 127 } 128 var events [64]keventt 129 retry: 130 n := kevent(kq, nil, 0, &events[0], int32(len(events)), tp) 131 if n < 0 { 132 if n != -_EINTR { 133 println("runtime: kevent on fd", kq, "failed with", -n) 134 throw("runtime: netpoll failed") 135 } 136 // If a timed sleep was interrupted, just return to 137 // recalculate how long we should sleep now. 138 if delay > 0 { 139 return gList{} 140 } 141 goto retry 142 } 143 var toRun gList 144 for i := 0; i < int(n); i++ { 145 ev := &events[i] 146 147 if uintptr(ev.ident) == netpollBreakRd { 148 if ev.filter != _EVFILT_READ { 149 println("runtime: netpoll: break fd ready for", ev.filter) 150 throw("runtime: netpoll: break fd ready for something unexpected") 151 } 152 if delay != 0 { 153 // netpollBreak could be picked up by a 154 // nonblocking poll. Only read the byte 155 // if blocking. 156 var tmp [16]byte 157 read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp))) 158 netpollWakeSig.Store(0) 159 } 160 continue 161 } 162 163 var mode int32 164 switch ev.filter { 165 case _EVFILT_READ: 166 mode += 'r' 167 168 // On some systems when the read end of a pipe 169 // is closed the write end will not get a 170 // _EVFILT_WRITE event, but will get a 171 // _EVFILT_READ event with EV_EOF set. 172 // Note that setting 'w' here just means that we 173 // will wake up a goroutine waiting to write; 174 // that goroutine will try the write again, 175 // and the appropriate thing will happen based 176 // on what that write returns (success, EPIPE, EAGAIN). 177 if ev.flags&_EV_EOF != 0 { 178 mode += 'w' 179 } 180 case _EVFILT_WRITE: 181 mode += 'w' 182 } 183 if mode != 0 { 184 pd := (*pollDesc)(unsafe.Pointer(ev.udata)) 185 pd.setEventErr(ev.flags == _EV_ERROR) 186 netpollready(&toRun, pd, mode) 187 } 188 } 189 return toRun 190 }