github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/net/internal/netpoll/epoll.go (about) 1 // +build linux 2 3 package netpoll 4 5 import ( 6 "log" 7 "unsafe" 8 9 "github.com/angenalZZZ/gofunc/net/internal" 10 "golang.org/x/sys/unix" 11 ) 12 13 // Poller represents a poller which is in charge of monitoring file-descriptors. 14 type Poller struct { 15 fd int // epoll fd 16 wfd int // wake fd 17 wfdBuf []byte // wfd buffer to read packet 18 asyncJobQueue internal.AsyncJobQueue 19 } 20 21 // OpenPoller instantiates a poller. 22 func OpenPoller() (poller *Poller, err error) { 23 poller = new(Poller) 24 if poller.fd, err = unix.EpollCreate1(unix.EPOLL_CLOEXEC); err != nil { 25 poller = nil 26 return 27 } 28 if poller.wfd, err = unix.Eventfd(0, unix.EFD_NONBLOCK|unix.EFD_CLOEXEC); err != nil { 29 _ = poller.Close() 30 poller = nil 31 return 32 } 33 poller.wfdBuf = make([]byte, 8) 34 if err = poller.AddRead(poller.wfd); err != nil { 35 _ = poller.Close() 36 poller = nil 37 return 38 } 39 poller.asyncJobQueue = internal.NewAsyncJobQueue() 40 return 41 } 42 43 // Close closes the poller. 44 func (p *Poller) Close() error { 45 if err := unix.Close(p.fd); err != nil { 46 return err 47 } 48 return unix.Close(p.wfd) 49 } 50 51 // Make the endianness of bytes compatible with more linux OSs under different processor-architectures, 52 // according to http://man7.org/linux/man-pages/man2/eventfd.2.html. 53 var ( 54 u uint64 = 1 55 b = (*(*[8]byte)(unsafe.Pointer(&u)))[:] 56 ) 57 58 // Trigger wakes up the poller blocked in waiting for network-events and runs jobs in asyncJobQueue. 59 func (p *Poller) Trigger(job internal.Job) error { 60 if p.asyncJobQueue.Push(job) == 1 { 61 _, err := unix.Write(p.wfd, b) 62 return err 63 } 64 return nil 65 } 66 67 // Polling blocks the current goroutine, waiting for network-events. 68 func (p *Poller) Polling(callback func(fd int, ev uint32) error) (err error) { 69 el := newEventList(InitEvents) 70 var wakenUp bool 71 for { 72 n, err0 := unix.EpollWait(p.fd, el.events, -1) 73 if err0 != nil && err0 != unix.EINTR { 74 log.Println(err0) 75 continue 76 } 77 for i := 0; i < n; i++ { 78 if fd := int(el.events[i].Fd); fd != p.wfd { 79 if err = callback(fd, el.events[i].Events); err != nil { 80 return 81 } 82 } else { 83 wakenUp = true 84 _, _ = unix.Read(p.wfd, p.wfdBuf) 85 } 86 } 87 if wakenUp { 88 wakenUp = false 89 if err = p.asyncJobQueue.ForEach(); err != nil { 90 return 91 } 92 } 93 if n == el.size { 94 el.increase() 95 } 96 } 97 } 98 99 const ( 100 readEvents = unix.EPOLLPRI | unix.EPOLLIN 101 writeEvents = unix.EPOLLOUT 102 readWriteEvents = readEvents | writeEvents 103 ) 104 105 // AddReadWrite registers the given file-descriptor with readable and writable events to the poller. 106 func (p *Poller) AddReadWrite(fd int) error { 107 return unix.EpollCtl(p.fd, unix.EPOLL_CTL_ADD, fd, &unix.EpollEvent{Fd: int32(fd), Events: readWriteEvents}) 108 } 109 110 // AddRead registers the given file-descriptor with readable event to the poller. 111 func (p *Poller) AddRead(fd int) error { 112 return unix.EpollCtl(p.fd, unix.EPOLL_CTL_ADD, fd, &unix.EpollEvent{Fd: int32(fd), Events: readEvents}) 113 } 114 115 // AddWrite registers the given file-descriptor with writable event to the poller. 116 func (p *Poller) AddWrite(fd int) error { 117 return unix.EpollCtl(p.fd, unix.EPOLL_CTL_ADD, fd, &unix.EpollEvent{Fd: int32(fd), Events: writeEvents}) 118 } 119 120 // ModRead renews the given file-descriptor with readable event in the poller. 121 func (p *Poller) ModRead(fd int) error { 122 return unix.EpollCtl(p.fd, unix.EPOLL_CTL_MOD, fd, &unix.EpollEvent{Fd: int32(fd), Events: readEvents}) 123 } 124 125 // ModReadWrite renews the given file-descriptor with readable and writable events in the poller. 126 func (p *Poller) ModReadWrite(fd int) error { 127 return unix.EpollCtl(p.fd, unix.EPOLL_CTL_MOD, fd, &unix.EpollEvent{Fd: int32(fd), Events: readWriteEvents}) 128 } 129 130 // Delete removes the given file-descriptor from the poller. 131 func (p *Poller) Delete(fd int) error { 132 return unix.EpollCtl(p.fd, unix.EPOLL_CTL_DEL, fd, nil) 133 }