github.com/sunvim/utils@v0.1.0/netpoll/poll_bsd.go (about)

     1  // Copyright (c) 2020 Meng Huang (mhboy@outlook.com)
     2  // This package is licensed under a MIT license that can be found in the LICENSE file.
     3  
     4  //go:build darwin || dragonfly || freebsd || netbsd || openbsd
     5  // +build darwin dragonfly freebsd netbsd openbsd
     6  
     7  package netpoll
     8  
     9  import (
    10  	"errors"
    11  	"sync"
    12  	"syscall"
    13  	"time"
    14  )
    15  
    16  // Tag is the poll type.
    17  var Tag = "kqueue"
    18  
    19  // ErrTimeout is the error returned by SetTimeout when time.Duration d < time.Millisecond.
    20  var ErrTimeout = errors.New("non-positive interval for SetTimeout")
    21  
    22  // Poll represents the poll that supports non-blocking I/O on file descriptors with polling.
    23  type Poll struct {
    24  	fd      int
    25  	events  []syscall.Kevent_t
    26  	pool    *sync.Pool
    27  	timeout *syscall.Timespec
    28  }
    29  
    30  // Create creates a new poll.
    31  func Create() (*Poll, error) {
    32  	fd, err := syscall.Kqueue()
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	return &Poll{
    37  		fd:     fd,
    38  		events: make([]syscall.Kevent_t, 1024),
    39  		pool: &sync.Pool{New: func() interface{} {
    40  			return []syscall.Kevent_t{{Filter: syscall.EVFILT_READ}, {Filter: syscall.EVFILT_WRITE}}
    41  		}},
    42  		timeout: &syscall.Timespec{Sec: 1},
    43  	}, nil
    44  }
    45  
    46  // SetTimeout sets the wait timeout.
    47  func (p *Poll) SetTimeout(d time.Duration) (err error) {
    48  	if d < time.Millisecond {
    49  		return ErrTimeout
    50  	}
    51  	p.timeout.Sec = int64(d / time.Second)
    52  	p.timeout.Nsec = int64(d % time.Second)
    53  	return nil
    54  }
    55  
    56  // Register registers a file descriptor.
    57  func (p *Poll) Register(fd int) (err error) {
    58  	changes := p.pool.Get().([]syscall.Kevent_t)
    59  	changes[0].Ident, changes[0].Flags = uint64(fd), syscall.EV_ADD
    60  	_, err = syscall.Kevent(p.fd, changes[:1], nil, nil)
    61  	p.pool.Put(changes)
    62  	return
    63  }
    64  
    65  // Write adds a write event.
    66  func (p *Poll) Write(fd int) (err error) {
    67  	changes := p.pool.Get().([]syscall.Kevent_t)
    68  	changes[1].Ident, changes[1].Flags = uint64(fd), syscall.EV_ADD
    69  	_, err = syscall.Kevent(p.fd, changes[1:], nil, nil)
    70  	p.pool.Put(changes)
    71  	return
    72  }
    73  
    74  // Unregister unregisters a file descriptor.
    75  func (p *Poll) Unregister(fd int) (err error) {
    76  	changes := p.pool.Get().([]syscall.Kevent_t)
    77  	changes[0].Ident, changes[0].Flags = uint64(fd), syscall.EV_DELETE
    78  	changes[1].Ident, changes[1].Flags = uint64(fd), syscall.EV_DELETE
    79  	_, err = syscall.Kevent(p.fd, changes, nil, nil)
    80  	p.pool.Put(changes)
    81  	return
    82  }
    83  
    84  // Wait waits events.
    85  func (p *Poll) Wait(events []Event) (n int, err error) {
    86  	if cap(p.events) >= len(events) {
    87  		p.events = p.events[:len(events)]
    88  	} else {
    89  		p.events = make([]syscall.Kevent_t, len(events))
    90  	}
    91  	n, err = syscall.Kevent(p.fd, nil, p.events, p.timeout)
    92  	if err != nil {
    93  		if err != syscall.EINTR {
    94  			return 0, err
    95  		}
    96  		err = nil
    97  	}
    98  	for i := 0; i < n; i++ {
    99  		ev := p.events[i]
   100  		events[i].Fd = int(ev.Ident)
   101  		switch ev.Filter {
   102  		case syscall.EVFILT_READ:
   103  			events[i].Mode = READ
   104  		case syscall.EVFILT_WRITE:
   105  			events[i].Mode = WRITE
   106  			changes := p.pool.Get().([]syscall.Kevent_t)
   107  			changes[1].Ident, changes[1].Flags = ev.Ident, syscall.EV_DELETE
   108  			syscall.Kevent(p.fd, changes[1:], nil, nil)
   109  			p.pool.Put(changes)
   110  		}
   111  	}
   112  	return
   113  }
   114  
   115  // Close closes the poll fd. The underlying file descriptor is closed by the
   116  // destroy method when there are no remaining references.
   117  func (p *Poll) Close() error {
   118  	return syscall.Close(p.fd)
   119  }