github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/poller/netpoll/kqueue.go (about) 1 // +build darwin dragonfly freebsd netbsd openbsd 2 3 package netpoll 4 5 import ( 6 "reflect" 7 "sync" 8 "unsafe" 9 10 "golang.org/x/sys/unix" 11 ) 12 13 // KeventFilter is a kqueue event filter. 14 type KeventFilter int 15 16 // String returns string representation of a filter. 17 func (filter KeventFilter) String() (str string) { 18 switch filter { 19 case EVFILT_READ: 20 return "EVFILT_READ" 21 case EVFILT_WRITE: 22 return "EVFILT_WRITE" 23 case EVFILT_AIO: 24 return "EVFILT_AIO" 25 case EVFILT_VNODE: 26 return "EVFILT_VNODE" 27 case EVFILT_PROC: 28 return "EVFILT_PROC" 29 case EVFILT_SIGNAL: 30 return "EVFILT_SIGNAL" 31 case EVFILT_TIMER: 32 return "EVFILT_TIMER" 33 case EVFILT_USER: 34 return "EVFILT_USER" 35 case _EVFILT_CLOSED: 36 return "_EVFILT_CLOSED" 37 default: 38 return "_EVFILT_UNKNOWN" 39 } 40 } 41 42 const ( 43 // EVFILT_READ takes a descriptor as the identifier, and returns whenever 44 // there is data available to read. The behavior of the filter is slightly 45 // different depending on the descriptor type. 46 EVFILT_READ = unix.EVFILT_READ 47 48 // EVFILT_WRITE takes a descriptor as the identifier, and returns whenever 49 // it is possible to write to the descriptor. For sockets, pipes and fifos, 50 // data will contain the amount of space remaining in the write buffer. The 51 // filter will set EV_EOF when the reader disconnects, and for the fifo 52 // case, this may be cleared by use of EV_CLEAR. Note that this filter is 53 // not supported for vnodes or BPF devices. For sockets, the low water mark 54 // and socket error handling is identical to the EVFILT_READ case. 55 EVFILT_WRITE = unix.EVFILT_WRITE 56 57 // EVFILT_AIO the sigevent portion of the AIO request is filled in, with 58 // sigev_notify_kqueue containing the descriptor of the kqueue that the 59 // event should be attached to, sigev_notify_kevent_flags containing the 60 // kevent flags which should be EV_ONESHOT, EV_CLEAR or EV_DISPATCH, 61 // sigev_value containing the udata value, and sigev_notify set to 62 // SIGEV_KEVENT. When the aio_*() system call is made, the event will be 63 // registered with the specified kqueue, and the ident argument set to the 64 // struct aiocb returned by the aio_*() system call. The filter returns 65 // under the same conditions as aio_error(). 66 EVFILT_AIO = unix.EVFILT_AIO 67 68 // EVFILT_VNODE takes a file descriptor as the identifier and the events to 69 // watch for in fflags, and returns when one or more of the requested 70 // events occurs on the descriptor. 71 EVFILT_VNODE = unix.EVFILT_VNODE 72 73 // EVFILT_PROC takes the process ID to monitor as the identifier and the 74 // events to watch for in fflags, and returns when the process performs one 75 // or more of the requested events. If a process can normally see another 76 // process, it can attach an event to it. 77 EVFILT_PROC = unix.EVFILT_PROC 78 79 // EVFILT_SIGNAL takes the signal number to monitor as the identifier and 80 // returns when the given signal is delivered to the process. This coexists 81 // with the signal() and sigaction() facilities, and has a lower 82 // precedence. The filter will record all attempts to deliver a signal to 83 // a process, even if the signal has been marked as SIG_IGN, except for the 84 // SIGCHLD signal, which, if ignored, won't be recorded by the filter. 85 // Event notification happens after normal signal delivery processing. data 86 // returns the number of times the signal has occurred since the last call 87 // to kevent(). This filter automatically sets the EV_CLEAR flag 88 // internally. 89 EVFILT_SIGNAL = unix.EVFILT_SIGNAL 90 91 // EVFILT_TIMER establishes an arbitrary timer identified by ident. When 92 // adding a timer, data specifies the timeout period. The timer will be 93 // periodic unless EV_ONESHOT is specified. On return, data contains the 94 // number of times the timeout has expired since the last call to kevent(). 95 // This filter automatically sets the EV_CLEAR flag internally. There is a 96 // system wide limit on the number of timers which is controlled by the 97 // kern.kq_calloutmax sysctl. 98 EVFILT_TIMER = unix.EVFILT_TIMER 99 100 // EVFILT_USER establishes a user event identified by ident which is not 101 // associated with any kernel mechanism but is trig- gered by user level 102 // code. 103 EVFILT_USER = unix.EVFILT_USER 104 105 // Custom filter value signaling that kqueue instance get closed. 106 _EVFILT_CLOSED = -0x7f 107 ) 108 109 // KeventFlag represents kqueue event flag. 110 type KeventFlag int 111 112 // String returns string representation of flag bits of the form 113 // "EV_A|EV_B|...". 114 func (flag KeventFlag) String() (str string) { 115 name := func(f KeventFlag, name string) { 116 if flag&f == 0 { 117 return 118 } 119 if str != "" { 120 str += "|" 121 } 122 str += name 123 } 124 name(EV_ADD, "EV_ADD") 125 name(EV_ENABLE, "EV_ENABLE") 126 name(EV_DISABLE, "EV_DISABLE") 127 name(EV_DISPATCH, "EV_DISPATCH") 128 name(EV_DELETE, "EV_DELETE") 129 name(EV_RECEIPT, "EV_RECEIPT") 130 name(EV_ONESHOT, "EV_ONESHOT") 131 name(EV_CLEAR, "EV_CLEAR") 132 name(EV_EOF, "EV_EOF") 133 name(EV_ERROR, "EV_ERROR") 134 return 135 } 136 137 const ( 138 // EV_ADD adds the event to the kqueue. Re-adding an existing event will modify 139 // the parameters of the original event, and not result in a duplicate 140 // entry. Adding an event automatically enables it, unless overridden by 141 // the EV_DISABLE flag. 142 EV_ADD = unix.EV_ADD 143 144 // EV_ENABLE permits kevent() to return the event if it is triggered. 145 EV_ENABLE = unix.EV_ENABLE 146 147 // EV_DISABLE disables the event so kevent() will not return it. The filter itself is 148 // not disabled. 149 EV_DISABLE = unix.EV_DISABLE 150 151 // EV_DISPATCH disables the event source immediately after delivery of an event. See 152 // EV_DISABLE above. 153 EV_DISPATCH = unix.EV_DISPATCH 154 155 // EV_DELETE removes the event from the kqueue. Events which are attached to file 156 // descriptors are automatically deleted on the last close of the 157 // descriptor. 158 EV_DELETE = unix.EV_DELETE 159 160 // EV_RECEIPT is useful for making bulk changes to a kqueue without draining 161 // any pending events. When passed as input, it forces EV_ERROR to always 162 // be returned. When a filter is successfully added the data field will be 163 // zero. 164 EV_RECEIPT = unix.EV_RECEIPT 165 166 // EV_ONESHOT causes the event to return only the first occurrence of the 167 // filter being triggered. After the user retrieves the event from the 168 // kqueue, it is deleted. 169 EV_ONESHOT = unix.EV_ONESHOT 170 171 // EV_CLEAR makes event state be reset after the event is retrieved by the 172 // user. This is useful for filters which report state transitions instead 173 // of the current state. Note that some filters may automatically set this 174 // flag internally. 175 EV_CLEAR = unix.EV_CLEAR 176 177 // EV_EOF may be set by the filters to indicate filter-specific EOF 178 // condition. 179 EV_EOF = unix.EV_EOF 180 181 // EV_ERROR is set to indiacate an error occured with the identtifier. 182 EV_ERROR = unix.EV_ERROR 183 ) 184 185 // filterCount is a constant number of available filters which can be 186 // registered for an identifier. 187 const filterCount = 8 188 189 // Kevent represents kevent. 190 type Kevent struct { 191 Filter KeventFilter 192 Flags KeventFlag 193 Fflags uint32 194 Data int64 195 } 196 197 // Kevents is a fixed number of pairs of event filter and flags which can be 198 // registered for an identifier. 199 type Kevents [8]Kevent 200 201 // KeventHandler is a function that will be called when event occures on 202 // registered identifier. 203 type KeventHandler func(Kevent) 204 205 // KqueueConfig contains options for configuration kqueue instance. 206 type KqueueConfig struct { 207 // OnWaitError will be called from goroutine, waiting for events. 208 OnWaitError func(error) 209 } 210 211 func (c *KqueueConfig) withDefaults() (config KqueueConfig) { 212 if c != nil { 213 config = *c 214 } 215 if config.OnWaitError == nil { 216 config.OnWaitError = defaultOnWaitError 217 } 218 return config 219 } 220 221 // Kqueue represents kqueue instance. 222 type Kqueue struct { 223 mu sync.RWMutex 224 fd int 225 cb map[int]KeventHandler 226 done chan struct{} 227 closed bool 228 } 229 230 // KqueueCreate creates new kqueue instance. 231 // It starts wait loop in a separate goroutine. 232 func KqueueCreate(c *KqueueConfig) (*Kqueue, error) { 233 config := c.withDefaults() 234 235 fd, err := unix.Kqueue() 236 if err != nil { 237 return nil, err 238 } 239 240 kq := &Kqueue{ 241 fd: fd, 242 cb: make(map[int]KeventHandler), 243 done: make(chan struct{}), 244 } 245 246 go kq.wait(config.OnWaitError) 247 248 return kq, nil 249 } 250 251 // Close closes kqueue instance. 252 // NOTE: not implemented yet. 253 func (k *Kqueue) Close() error { 254 // TODO(): implement close. 255 return nil 256 } 257 258 // Add adds a event handler for identifier fd with given n events. 259 func (k *Kqueue) Add(fd int, events Kevents, n int, cb KeventHandler) error { 260 var kevs [filterCount]unix.Kevent_t 261 for i := 0; i < n; i++ { 262 kevs[i] = evGet(fd, events[i].Filter, events[i].Flags) 263 } 264 265 arr := unsafe.Pointer(&kevs) 266 hdr := &reflect.SliceHeader{ 267 Data: uintptr(arr), 268 Len: n, 269 Cap: n, 270 } 271 changes := *(*[]unix.Kevent_t)(unsafe.Pointer(hdr)) 272 273 k.mu.Lock() 274 defer k.mu.Unlock() 275 276 if k.closed { 277 return ErrClosed 278 } 279 if _, has := k.cb[fd]; has { 280 return ErrRegistered 281 } 282 k.cb[fd] = cb 283 284 _, err := unix.Kevent(k.fd, changes, nil, nil) 285 286 return err 287 } 288 289 // Mod modifies events registered for fd. 290 func (k *Kqueue) Mod(fd int, events Kevents, n int) error { 291 var kevs [filterCount]unix.Kevent_t 292 for i := 0; i < n; i++ { 293 kevs[i] = evGet(fd, events[i].Filter, events[i].Flags) 294 } 295 296 arr := unsafe.Pointer(&kevs) 297 hdr := &reflect.SliceHeader{ 298 Data: uintptr(arr), 299 Len: n, 300 Cap: n, 301 } 302 changes := *(*[]unix.Kevent_t)(unsafe.Pointer(hdr)) 303 304 k.mu.RLock() 305 defer k.mu.RUnlock() 306 307 if k.closed { 308 return ErrClosed 309 } 310 if _, has := k.cb[fd]; !has { 311 return ErrNotRegistered 312 } 313 314 _, err := unix.Kevent(k.fd, changes, nil, nil) 315 316 return err 317 } 318 319 // Del removes callback for fd. Note that it does not cleanups events for fd in 320 // kqueue. You should close fd or call Mod() with EV_DELETE flag set. 321 func (k *Kqueue) Del(fd int) error { 322 k.mu.Lock() 323 defer k.mu.Unlock() 324 325 if k.closed { 326 return ErrClosed 327 } 328 if _, has := k.cb[fd]; !has { 329 return ErrNotRegistered 330 } 331 332 delete(k.cb, fd) 333 334 return nil 335 } 336 337 func (k *Kqueue) wait(onError func(error)) { 338 const ( 339 maxWaitEventsBegin = 1 << 10 // 1024 340 maxWaitEventsStop = 1 << 15 // 32768 341 ) 342 343 defer func() { 344 if err := unix.Close(k.fd); err != nil { 345 onError(err) 346 } 347 close(k.done) 348 }() 349 350 evs := make([]unix.Kevent_t, maxWaitEventsBegin) 351 cbs := make([]KeventHandler, maxWaitEventsBegin) 352 353 for { 354 n, err := unix.Kevent(k.fd, nil, evs, nil) 355 if err != nil { 356 if temporaryErr(err) { 357 continue 358 } 359 onError(err) 360 return 361 } 362 363 cbs = cbs[:n] 364 k.mu.RLock() 365 for i := 0; i < n; i++ { 366 fd := int(evs[i].Ident) 367 if fd == -1 { //todo 368 k.mu.RUnlock() 369 return 370 } 371 cbs[i] = k.cb[fd] 372 } 373 k.mu.RUnlock() 374 375 for i, cb := range cbs { 376 if cb != nil { 377 e := evs[i] 378 cb(Kevent{ 379 Filter: KeventFilter(e.Filter), 380 Flags: KeventFlag(e.Flags), 381 Data: e.Data, 382 Fflags: e.Fflags, 383 }) 384 cbs[i] = nil 385 } 386 } 387 388 if n == len(evs) && n*2 <= maxWaitEventsStop { 389 evs = make([]unix.Kevent_t, n*2) 390 cbs = make([]KeventHandler, n*2) 391 } 392 } 393 } 394 395 func evGet(fd int, filter KeventFilter, flags KeventFlag) unix.Kevent_t { 396 return unix.Kevent_t{ 397 Ident: uint64(fd), 398 Filter: int16(filter), 399 Flags: uint16(flags), 400 } 401 }