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 7package runtime 8 9// Integrated network poller (kqueue-based implementation). 10 11import "unsafe" 12 13//extern kqueue 14func kqueue() int32 15 16//go:noescape 17//extern kevent 18func kevent(kq int32, ch *keventt, nch uintptr, ev *keventt, nev uintptr, ts *timespec) int32 19 20var ( 21 kq int32 = -1 22 23 netpollBreakRd, netpollBreakWr uintptr // for netpollBreak 24) 25 26func netpollinit() { 27 kq = kqueue() 28 if kq < 0 { 29 println("netpollinit: kqueue failed with", errno()) 30 throw("runtime: netpollinit failed") 31 } 32 closeonexec(kq) 33 r, w, errno := nonblockingPipe() 34 if errno != 0 { 35 println("runtime: pipe failed with", -errno) 36 throw("runtime: pipe failed") 37 } 38 ev := keventt{ 39 filter: _EVFILT_READ, 40 flags: _EV_ADD, 41 } 42 *(*uintptr)(unsafe.Pointer(&ev.ident)) = uintptr(r) 43 n := kevent(kq, &ev, 1, nil, 0, nil) 44 if n < 0 { 45 println("runtime: kevent failed with", -n) 46 throw("runtime: kevent failed") 47 } 48 netpollBreakRd = uintptr(r) 49 netpollBreakWr = uintptr(w) 50} 51 52func netpollIsPollDescriptor(fd uintptr) bool { 53 return fd == uintptr(kq) || fd == netpollBreakRd || fd == netpollBreakWr 54} 55 56func netpollopen(fd uintptr, pd *pollDesc) int32 { 57 // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR) 58 // for the whole fd lifetime. The notifications are automatically unregistered 59 // when fd is closed. 60 var ev [2]keventt 61 *(*uintptr)(unsafe.Pointer(&ev[0].ident)) = fd 62 ev[0].filter = _EVFILT_READ 63 ev[0].flags = _EV_ADD | _EV_CLEAR 64 ev[0].fflags = 0 65 ev[0].data = 0 66 ev[0].udata = (*byte)(unsafe.Pointer(pd)) 67 ev[1] = ev[0] 68 ev[1].filter = _EVFILT_WRITE 69 n := kevent(kq, &ev[0], 2, nil, 0, nil) 70 if n < 0 { 71 return int32(errno()) 72 } 73 return 0 74} 75 76func netpollclose(fd uintptr) int32 { 77 // Don't need to unregister because calling close() 78 // on fd will remove any kevents that reference the descriptor. 79 return 0 80} 81 82func netpollarm(pd *pollDesc, mode int) { 83 throw("runtime: unused") 84} 85 86// netpollBreak interrupts an epollwait. 87func netpollBreak() { 88 for { 89 var b byte 90 n := write(netpollBreakWr, unsafe.Pointer(&b), 1) 91 if n == 1 || n == -_EAGAIN { 92 break 93 } 94 if n == -_EINTR { 95 continue 96 } 97 println("runtime: netpollBreak write failed with", -n) 98 throw("runtime: netpollBreak write failed") 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 107func 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 126retry: 127 n := kevent(kq, nil, 0, &events[0], uintptr(len(events)), tp) 128 if n < 0 { 129 e := errno() 130 if e != _EINTR { 131 println("runtime: kevent on fd", kq, "failed with", e) 132 throw("runtime: netpoll failed") 133 } 134 // If a timed sleep was interrupted, just return to 135 // recalculate how long we should sleep now. 136 if delay > 0 { 137 return gList{} 138 } 139 goto retry 140 } 141 var toRun gList 142 for i := 0; i < int(n); i++ { 143 ev := &events[i] 144 145 if uintptr(ev.ident) == netpollBreakRd { 146 if ev.filter != _EVFILT_READ { 147 println("runtime: netpoll: break fd ready for", ev.filter) 148 throw("runtime: netpoll: break fd ready for something unexpected") 149 } 150 if delay != 0 { 151 // netpollBreak could be picked up by a 152 // nonblocking poll. Only read the byte 153 // if blocking. 154 var tmp [16]byte 155 read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp))) 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} 191