github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 // +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 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 if atomic.Cas(&netpollWakeSig, 0, 1) { 87 for { 88 var b byte 89 n := write(netpollBreakWr, unsafe.Pointer(&b), 1) 90 if n == 1 || n == -_EAGAIN { 91 break 92 } 93 if n == -_EINTR { 94 continue 95 } 96 println("runtime: netpollBreak write failed with", -n) 97 throw("runtime: netpollBreak write failed") 98 } 99 } 100 } 101 102 // netpoll checks for ready network connections. 103 // Returns list of goroutines that become runnable. 104 // delay < 0: blocks indefinitely 105 // delay == 0: does not block, just polls 106 // delay > 0: block for up to that many nanoseconds 107 func netpoll(delay int64) gList { 108 if kq == -1 { 109 return gList{} 110 } 111 var tp *timespec 112 var ts timespec 113 if delay < 0 { 114 tp = nil 115 } else if delay == 0 { 116 tp = &ts 117 } else { 118 ts.setNsec(delay) 119 if ts.tv_sec > 1e6 { 120 // Darwin returns EINVAL if the sleep time is too long. 121 ts.tv_sec = 1e6 122 } 123 tp = &ts 124 } 125 var events [64]keventt 126 retry: 127 n := kevent(kq, nil, 0, &events[0], int32(len(events)), tp) 128 if n < 0 { 129 if n != -_EINTR { 130 println("runtime: kevent on fd", kq, "failed with", -n) 131 throw("runtime: netpoll failed") 132 } 133 // If a timed sleep was interrupted, just return to 134 // recalculate how long we should sleep now. 135 if delay > 0 { 136 return gList{} 137 } 138 goto retry 139 } 140 var toRun gList 141 for i := 0; i < int(n); i++ { 142 ev := &events[i] 143 144 if uintptr(ev.ident) == netpollBreakRd { 145 if ev.filter != _EVFILT_READ { 146 println("runtime: netpoll: break fd ready for", ev.filter) 147 throw("runtime: netpoll: break fd ready for something unexpected") 148 } 149 if delay != 0 { 150 // netpollBreak could be picked up by a 151 // nonblocking poll. Only read the byte 152 // if blocking. 153 var tmp [16]byte 154 read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp))) 155 atomic.Store(&netpollWakeSig, 0) 156 } 157 continue 158 } 159 160 var mode int32 161 switch ev.filter { 162 case _EVFILT_READ: 163 mode += 'r' 164 165 // On some systems when the read end of a pipe 166 // is closed the write end will not get a 167 // _EVFILT_WRITE event, but will get a 168 // _EVFILT_READ event with EV_EOF set. 169 // Note that setting 'w' here just means that we 170 // will wake up a goroutine waiting to write; 171 // that goroutine will try the write again, 172 // and the appropriate thing will happen based 173 // on what that write returns (success, EPIPE, EAGAIN). 174 if ev.flags&_EV_EOF != 0 { 175 mode += 'w' 176 } 177 case _EVFILT_WRITE: 178 mode += 'w' 179 } 180 if mode != 0 { 181 pd := (*pollDesc)(unsafe.Pointer(ev.udata)) 182 pd.everr = false 183 if ev.flags == _EV_ERROR { 184 pd.everr = true 185 } 186 netpollready(&toRun, pd, mode) 187 } 188 } 189 return toRun 190 }