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  }