github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/epoll/epoll.go (about) 1 /* 2 Copyright 2020 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package epoll 18 19 import ( 20 "errors" 21 "os" 22 "syscall" 23 24 "k8s.io/klog/v2" 25 ) 26 27 const ( 28 EPOLLIN EventType = syscall.EPOLLIN 29 EPOLLPRI EventType = syscall.EPOLLPRI 30 EPOLLERR EventType = syscall.EPOLLERR 31 EPOLLOUT EventType = syscall.EPOLLOUT 32 EPOLLHUP EventType = syscall.EPOLLHUP 33 34 // timeout is the duration in milliseconds after which the EpollWait system 35 // call returns even if there are no events. Setting it to -1 makes the call 36 // return only when there is an event. 37 timeout int = -1 38 ) 39 40 var ( 41 ErrFileAlreadyWatched error = errors.New("file being watched already") 42 ErrInvalidEventType error = errors.New("invalid event type") 43 ErrWatcherNotFound error = errors.New("watcher not found") 44 ) 45 46 type Epoll struct { 47 watchers []Watcher 48 fileToWatcher map[string]*Watcher 49 fdToWatcher map[int32]*Watcher 50 eventChan chan Event 51 eventChanSize int 52 epfd int 53 active bool 54 } 55 56 type Event struct { 57 fileName string 58 events uint32 59 } 60 61 type EventType uint32 62 type Watcher struct { 63 fd *os.File 64 // The complete path of the file to watch. The file name is also used to 65 // uniquely identify the watcher 66 FileName string 67 // The event types to watch for 68 EventTypes []EventType 69 } 70 71 type NewOpt func(*Epoll) 72 73 func BufferSize(size int) NewOpt { 74 return func(e *Epoll) { 75 e.eventChanSize = size 76 } 77 } 78 79 func New(opts ...NewOpt) (Epoll, error) { 80 epfd, err := syscall.EpollCreate(1) 81 if err != nil { 82 return Epoll{}, err 83 } 84 e := Epoll{ 85 watchers: make([]Watcher, 0), 86 fileToWatcher: make(map[string]*Watcher), 87 fdToWatcher: make(map[int32]*Watcher), 88 epfd: epfd, 89 eventChanSize: 0, 90 } 91 for _, opt := range opts { 92 opt(&e) 93 } 94 return e, nil 95 } 96 97 func (e *Epoll) Start() (<-chan Event, error) { 98 if e.epfd == 0 { 99 return nil, errors.New("invalid epoll struct") 100 } 101 102 if e.active { 103 return nil, errors.New("epoll already started") 104 } 105 e.eventChan = make(chan Event, e.eventChanSize) 106 e.active = true 107 go e.listen() 108 return e.eventChan, nil 109 } 110 111 func (e *Epoll) Stop() { 112 if !e.active { 113 return 114 } 115 e.active = false 116 close(e.eventChan) 117 } 118 119 func (e *Epoll) Close() { 120 e.Stop() 121 syscall.Close(e.epfd) 122 for i := range e.watchers { 123 e.watchers[i].fd.Close() 124 } 125 e.fdToWatcher = nil 126 e.fileToWatcher = nil 127 e.eventChan = nil 128 e.watchers = nil 129 } 130 131 func (e *Epoll) AddWatcher(w Watcher) error { 132 for _, evt := range w.EventTypes { 133 if !isValidEventType(evt) { 134 return ErrInvalidEventType 135 } 136 } 137 138 if _, ok := e.fileToWatcher[w.FileName]; ok { 139 return ErrFileAlreadyWatched 140 } 141 fd, err := os.Open(w.FileName) 142 if err != nil { 143 return err 144 } 145 w.fd = fd 146 if err = e.addToEpoll(fd, w.EventTypes); err != nil { 147 return err 148 } 149 150 e.watchers = append(e.watchers, w) 151 n := len(e.watchers) - 1 152 e.fileToWatcher[w.FileName] = &e.watchers[n] 153 e.fdToWatcher[int32(fd.Fd())] = &e.watchers[n] 154 return nil 155 } 156 157 func (e *Epoll) DeleteWatcher(fileName string) error { 158 w, ok := e.fileToWatcher[fileName] 159 if !ok { 160 return ErrWatcherNotFound 161 } 162 err := e.deleteFromEpoll(w.fd) 163 if err != nil { 164 return err 165 } 166 delete(e.fileToWatcher, fileName) 167 delete(e.fdToWatcher, int32(w.fd.Fd())) 168 return nil 169 } 170 171 func (e *Epoll) addToEpoll(fd *os.File, events []EventType) error { 172 ifd := int32(fd.Fd()) 173 var evs EventType 174 for _, e := range events { 175 evs |= e 176 } 177 return syscall.EpollCtl(e.epfd, syscall.EPOLL_CTL_ADD, int(ifd), 178 &syscall.EpollEvent{ 179 Events: uint32(evs), 180 Fd: ifd, 181 }) 182 } 183 184 func (e *Epoll) deleteFromEpoll(fd *os.File) error { 185 return syscall.EpollCtl(e.epfd, syscall.EPOLL_CTL_DEL, int(fd.Fd()), nil) 186 } 187 188 func (e *Epoll) dispatchEvent(ev *syscall.EpollEvent) { 189 w, ok := e.fdToWatcher[ev.Fd] 190 // Skip if no watcher found 191 if !ok { 192 return 193 } 194 klog.V(4).Infof("epoll event for file %s dispatched", w.FileName) 195 196 e.eventChan <- Event{ 197 fileName: w.FileName, 198 events: ev.Events, 199 } 200 } 201 202 func (e *Epoll) listen() { 203 events := make([]syscall.EpollEvent, 2) 204 for e.active { 205 // TODO: Handle errors here 206 klog.V(4).Info("waiting for epoll events...") 207 count, _ := syscall.EpollWait(e.epfd, events, timeout) 208 klog.V(4).Infof("received %d events from epoll. dispatching...", count) 209 for i := 0; e.active && i < count; i++ { 210 e.dispatchEvent(&events[i]) 211 } 212 } 213 } 214 215 func (ev *Event) IsType(evt EventType) bool { 216 return ev.events&uint32(evt) != 0 217 } 218 219 func (ev *Event) FileName() string { 220 return ev.fileName 221 } 222 223 func isValidEventType(evt EventType) bool { 224 switch evt { 225 case EPOLLERR, EPOLLHUP, EPOLLIN, EPOLLOUT, EPOLLPRI: 226 return true 227 } 228 229 return false 230 }