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  }