github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/poller/netpoll/epoll.go (about) 1 //go:build linux 2 // +build linux 3 4 package netpoll 5 6 import ( 7 "sync" 8 9 "golang.org/x/sys/unix" 10 ) 11 12 // EpollEvent represents epoll events configuration bit mask. 13 type EpollEvent uint32 14 15 // EpollEvents that are mapped to epoll_event.events possible values. 16 const ( 17 EPOLLIN = unix.EPOLLIN 18 EPOLLOUT = unix.EPOLLOUT 19 EPOLLRDHUP = unix.EPOLLRDHUP 20 EPOLLPRI = unix.EPOLLPRI 21 EPOLLERR = unix.EPOLLERR 22 EPOLLHUP = unix.EPOLLHUP 23 EPOLLET = unix.EPOLLET 24 EPOLLONESHOT = unix.EPOLLONESHOT 25 26 // _EPOLLCLOSED is a special EpollEvent value the receipt of which means 27 // that the epoll instance is closed. 28 _EPOLLCLOSED = 0x20 29 ) 30 31 // String returns a string representation of EpollEvent. 32 func (evt EpollEvent) String() (str string) { 33 name := func(event EpollEvent, name string) { 34 if evt&event == 0 { 35 return 36 } 37 if str != "" { 38 str += "|" 39 } 40 str += name 41 } 42 43 name(EPOLLIN, "EPOLLIN") 44 name(EPOLLOUT, "EPOLLOUT") 45 name(EPOLLRDHUP, "EPOLLRDHUP") 46 name(EPOLLPRI, "EPOLLPRI") 47 name(EPOLLERR, "EPOLLERR") 48 name(EPOLLHUP, "EPOLLHUP") 49 name(EPOLLET, "EPOLLET") 50 name(EPOLLONESHOT, "EPOLLONESHOT") 51 name(_EPOLLCLOSED, "_EPOLLCLOSED") 52 53 return 54 } 55 56 // Epoll represents single epoll instance. 57 type Epoll struct { 58 mu sync.RWMutex 59 60 fd int 61 eventFd int 62 closed bool 63 waitDone chan struct{} 64 65 callbacks map[int]func(EpollEvent) 66 } 67 68 // EpollConfig contains options for Epoll instance configuration. 69 type EpollConfig struct { 70 // OnWaitError will be called from goroutine, waiting for events. 71 OnWaitError func(error) 72 } 73 74 func (c *EpollConfig) withDefaults() (config EpollConfig) { 75 if c != nil { 76 config = *c 77 } 78 if config.OnWaitError == nil { 79 config.OnWaitError = defaultOnWaitError 80 } 81 return config 82 } 83 84 // EpollCreate creates new epoll instance. 85 // It starts the wait loop in separate goroutine. 86 func EpollCreate(c *EpollConfig) (*Epoll, error) { 87 config := c.withDefaults() 88 89 fd, err := unix.EpollCreate1(0) 90 if err != nil { 91 return nil, err 92 } 93 94 r0, _, errno := unix.Syscall(unix.SYS_EVENTFD2, 0, 0, 0) 95 if errno != 0 { 96 return nil, errno 97 } 98 eventFd := int(r0) 99 100 // Set finalizer for write end of socket pair to avoid data races when 101 // closing Epoll instance and EBADF errors on writing ctl bytes from callers. 102 err = unix.EpollCtl(fd, unix.EPOLL_CTL_ADD, eventFd, &unix.EpollEvent{ 103 Events: unix.EPOLLIN, 104 Fd: int32(eventFd), 105 }) 106 if err != nil { 107 unix.Close(fd) 108 unix.Close(eventFd) 109 return nil, err 110 } 111 112 ep := &Epoll{ 113 fd: fd, 114 eventFd: eventFd, 115 callbacks: make(map[int]func(EpollEvent)), 116 waitDone: make(chan struct{}), 117 } 118 119 // Run wait loop. 120 go ep.wait(config.OnWaitError) 121 122 return ep, nil 123 } 124 125 // closeBytes used for writing to eventfd. 126 var closeBytes = []byte{1, 0, 0, 0, 0, 0, 0, 0} 127 128 // Close stops wait loop and closes all underlying resources. 129 func (ep *Epoll) Close() (err error) { 130 ep.mu.Lock() 131 { 132 if ep.closed { 133 ep.mu.Unlock() 134 return ErrClosed 135 } 136 ep.closed = true 137 138 if _, err = unix.Write(ep.eventFd, closeBytes); err != nil { 139 ep.mu.Unlock() 140 return 141 } 142 } 143 ep.mu.Unlock() 144 145 <-ep.waitDone 146 147 if err = unix.Close(ep.eventFd); err != nil { 148 return 149 } 150 151 ep.mu.Lock() 152 // Set callbacks to nil prETypeIng long mu.Lock() hold. 153 // This could increase the speed of retreiving ErrClosed in other calls to 154 // current epoll instance. 155 // Setting callbacks to nil is safe here because no one should read after 156 // closed flag is true. 157 callbacks := ep.callbacks 158 ep.callbacks = nil 159 ep.mu.Unlock() 160 161 for _, cb := range callbacks { 162 if cb != nil { 163 cb(_EPOLLCLOSED) 164 } 165 } 166 167 return 168 } 169 170 // Add adds fd to epoll set with given events. 171 // Callback will be called on each received event from epoll. 172 // Note that _EPOLLCLOSED is triggered for every cb when epoll closed. 173 func (ep *Epoll) Add(fd int, events EpollEvent, cb func(EpollEvent)) (err error) { 174 ev := &unix.EpollEvent{ 175 Events: uint32(events), 176 Fd: int32(fd), 177 } 178 179 ep.mu.Lock() 180 defer ep.mu.Unlock() 181 182 if ep.closed { 183 return ErrClosed 184 } 185 if _, has := ep.callbacks[fd]; has { 186 return ErrRegistered 187 } 188 ep.callbacks[fd] = cb 189 190 return unix.EpollCtl(ep.fd, unix.EPOLL_CTL_ADD, fd, ev) 191 } 192 193 // Del removes fd from epoll set. 194 func (ep *Epoll) Del(fd int) (err error) { 195 ep.mu.Lock() 196 defer ep.mu.Unlock() 197 198 if ep.closed { 199 return ErrClosed 200 } 201 if _, ok := ep.callbacks[fd]; !ok { 202 return ErrNotRegistered 203 } 204 205 delete(ep.callbacks, fd) 206 207 return unix.EpollCtl(ep.fd, unix.EPOLL_CTL_DEL, fd, nil) 208 } 209 210 // Mod sets to listen events on fd. 211 func (ep *Epoll) Mod(fd int, events EpollEvent) (err error) { 212 ev := &unix.EpollEvent{ 213 Events: uint32(events), 214 Fd: int32(fd), 215 } 216 217 ep.mu.RLock() 218 defer ep.mu.RUnlock() 219 220 if ep.closed { 221 return ErrClosed 222 } 223 if _, ok := ep.callbacks[fd]; !ok { 224 return ErrNotRegistered 225 } 226 227 return unix.EpollCtl(ep.fd, unix.EPOLL_CTL_MOD, fd, ev) 228 } 229 230 const ( 231 maxWaitEventsBegin = 1024 232 maxWaitEventsStop = 32768 233 ) 234 235 func (ep *Epoll) wait(onError func(error)) { 236 defer func() { 237 if err := unix.Close(ep.fd); err != nil { 238 onError(err) 239 } 240 close(ep.waitDone) 241 }() 242 243 events := make([]unix.EpollEvent, maxWaitEventsBegin) 244 callbacks := make([]func(EpollEvent), 0, maxWaitEventsBegin) 245 246 for { 247 n, err := unix.EpollWait(ep.fd, events, -1) 248 if err != nil { 249 if temporaryErr(err) { 250 continue 251 } 252 onError(err) 253 return 254 } 255 256 callbacks = callbacks[:n] 257 258 ep.mu.RLock() 259 for i := 0; i < n; i++ { 260 fd := int(events[i].Fd) 261 if fd == ep.eventFd { // signal to close 262 ep.mu.RUnlock() 263 return 264 } 265 callbacks[i] = ep.callbacks[fd] 266 } 267 ep.mu.RUnlock() 268 269 for i := 0; i < n; i++ { 270 if cb := callbacks[i]; cb != nil { 271 cb(EpollEvent(events[i].Events)) 272 callbacks[i] = nil 273 } 274 } 275 276 if n == len(events) && n*2 <= maxWaitEventsStop { 277 events = make([]unix.EpollEvent, n*2) 278 callbacks = make([]func(EpollEvent), 0, n*2) 279 } 280 } 281 }