github.com/sunvim/utils@v0.1.0/netpoll/poll_bsd.go (about) 1 // Copyright (c) 2020 Meng Huang (mhboy@outlook.com) 2 // This package is licensed under a MIT license that can be found in the LICENSE file. 3 4 //go:build darwin || dragonfly || freebsd || netbsd || openbsd 5 // +build darwin dragonfly freebsd netbsd openbsd 6 7 package netpoll 8 9 import ( 10 "errors" 11 "sync" 12 "syscall" 13 "time" 14 ) 15 16 // Tag is the poll type. 17 var Tag = "kqueue" 18 19 // ErrTimeout is the error returned by SetTimeout when time.Duration d < time.Millisecond. 20 var ErrTimeout = errors.New("non-positive interval for SetTimeout") 21 22 // Poll represents the poll that supports non-blocking I/O on file descriptors with polling. 23 type Poll struct { 24 fd int 25 events []syscall.Kevent_t 26 pool *sync.Pool 27 timeout *syscall.Timespec 28 } 29 30 // Create creates a new poll. 31 func Create() (*Poll, error) { 32 fd, err := syscall.Kqueue() 33 if err != nil { 34 return nil, err 35 } 36 return &Poll{ 37 fd: fd, 38 events: make([]syscall.Kevent_t, 1024), 39 pool: &sync.Pool{New: func() interface{} { 40 return []syscall.Kevent_t{{Filter: syscall.EVFILT_READ}, {Filter: syscall.EVFILT_WRITE}} 41 }}, 42 timeout: &syscall.Timespec{Sec: 1}, 43 }, nil 44 } 45 46 // SetTimeout sets the wait timeout. 47 func (p *Poll) SetTimeout(d time.Duration) (err error) { 48 if d < time.Millisecond { 49 return ErrTimeout 50 } 51 p.timeout.Sec = int64(d / time.Second) 52 p.timeout.Nsec = int64(d % time.Second) 53 return nil 54 } 55 56 // Register registers a file descriptor. 57 func (p *Poll) Register(fd int) (err error) { 58 changes := p.pool.Get().([]syscall.Kevent_t) 59 changes[0].Ident, changes[0].Flags = uint64(fd), syscall.EV_ADD 60 _, err = syscall.Kevent(p.fd, changes[:1], nil, nil) 61 p.pool.Put(changes) 62 return 63 } 64 65 // Write adds a write event. 66 func (p *Poll) Write(fd int) (err error) { 67 changes := p.pool.Get().([]syscall.Kevent_t) 68 changes[1].Ident, changes[1].Flags = uint64(fd), syscall.EV_ADD 69 _, err = syscall.Kevent(p.fd, changes[1:], nil, nil) 70 p.pool.Put(changes) 71 return 72 } 73 74 // Unregister unregisters a file descriptor. 75 func (p *Poll) Unregister(fd int) (err error) { 76 changes := p.pool.Get().([]syscall.Kevent_t) 77 changes[0].Ident, changes[0].Flags = uint64(fd), syscall.EV_DELETE 78 changes[1].Ident, changes[1].Flags = uint64(fd), syscall.EV_DELETE 79 _, err = syscall.Kevent(p.fd, changes, nil, nil) 80 p.pool.Put(changes) 81 return 82 } 83 84 // Wait waits events. 85 func (p *Poll) Wait(events []Event) (n int, err error) { 86 if cap(p.events) >= len(events) { 87 p.events = p.events[:len(events)] 88 } else { 89 p.events = make([]syscall.Kevent_t, len(events)) 90 } 91 n, err = syscall.Kevent(p.fd, nil, p.events, p.timeout) 92 if err != nil { 93 if err != syscall.EINTR { 94 return 0, err 95 } 96 err = nil 97 } 98 for i := 0; i < n; i++ { 99 ev := p.events[i] 100 events[i].Fd = int(ev.Ident) 101 switch ev.Filter { 102 case syscall.EVFILT_READ: 103 events[i].Mode = READ 104 case syscall.EVFILT_WRITE: 105 events[i].Mode = WRITE 106 changes := p.pool.Get().([]syscall.Kevent_t) 107 changes[1].Ident, changes[1].Flags = ev.Ident, syscall.EV_DELETE 108 syscall.Kevent(p.fd, changes[1:], nil, nil) 109 p.pool.Put(changes) 110 } 111 } 112 return 113 } 114 115 // Close closes the poll fd. The underlying file descriptor is closed by the 116 // destroy method when there are no remaining references. 117 func (p *Poll) Close() error { 118 return syscall.Close(p.fd) 119 }