github.com/weedge/lib@v0.0.0-20230424045628-a36dcc1d90e4/poller/netpoll/kqueue.go (about)

     1  // +build darwin dragonfly freebsd netbsd openbsd
     2  
     3  package netpoll
     4  
     5  import (
     6  	"reflect"
     7  	"sync"
     8  	"unsafe"
     9  
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  // KeventFilter is a kqueue event filter.
    14  type KeventFilter int
    15  
    16  // String returns string representation of a filter.
    17  func (filter KeventFilter) String() (str string) {
    18  	switch filter {
    19  	case EVFILT_READ:
    20  		return "EVFILT_READ"
    21  	case EVFILT_WRITE:
    22  		return "EVFILT_WRITE"
    23  	case EVFILT_AIO:
    24  		return "EVFILT_AIO"
    25  	case EVFILT_VNODE:
    26  		return "EVFILT_VNODE"
    27  	case EVFILT_PROC:
    28  		return "EVFILT_PROC"
    29  	case EVFILT_SIGNAL:
    30  		return "EVFILT_SIGNAL"
    31  	case EVFILT_TIMER:
    32  		return "EVFILT_TIMER"
    33  	case EVFILT_USER:
    34  		return "EVFILT_USER"
    35  	case _EVFILT_CLOSED:
    36  		return "_EVFILT_CLOSED"
    37  	default:
    38  		return "_EVFILT_UNKNOWN"
    39  	}
    40  }
    41  
    42  const (
    43  	// EVFILT_READ takes a descriptor as the identifier, and returns whenever
    44  	// there is data available to read. The behavior of the filter is slightly
    45  	// different depending on the descriptor type.
    46  	EVFILT_READ = unix.EVFILT_READ
    47  
    48  	// EVFILT_WRITE takes a descriptor as the identifier, and returns whenever
    49  	// it is possible to write to the descriptor. For sockets, pipes and fifos,
    50  	// data will contain the amount of space remaining in the write buffer. The
    51  	// filter will set EV_EOF when the reader disconnects, and for the fifo
    52  	// case, this may be cleared by use of EV_CLEAR. Note that this filter is
    53  	// not supported for vnodes or BPF devices. For sockets, the low water mark
    54  	// and socket error handling is identical to the EVFILT_READ case.
    55  	EVFILT_WRITE = unix.EVFILT_WRITE
    56  
    57  	// EVFILT_AIO the sigevent portion of the AIO request is filled in, with
    58  	// sigev_notify_kqueue containing the descriptor of the kqueue that the
    59  	// event should be attached to, sigev_notify_kevent_flags containing the
    60  	// kevent flags which should be EV_ONESHOT, EV_CLEAR or EV_DISPATCH,
    61  	// sigev_value containing the udata value, and sigev_notify set to
    62  	// SIGEV_KEVENT. When the aio_*() system call is made, the event will be
    63  	// registered with the specified kqueue, and the ident argument set to the
    64  	// struct aiocb returned by the aio_*() system call. The filter returns
    65  	// under the same conditions as aio_error().
    66  	EVFILT_AIO = unix.EVFILT_AIO
    67  
    68  	// EVFILT_VNODE takes a file descriptor as the identifier and the events to
    69  	// watch for in fflags, and returns when one or more of the requested
    70  	// events occurs on the descriptor.
    71  	EVFILT_VNODE = unix.EVFILT_VNODE
    72  
    73  	// EVFILT_PROC takes the process ID to monitor as the identifier and the
    74  	// events to watch for in fflags, and returns when the process performs one
    75  	// or more of the requested events. If a process can normally see another
    76  	// process, it can attach an event to it.
    77  	EVFILT_PROC = unix.EVFILT_PROC
    78  
    79  	// EVFILT_SIGNAL takes the signal number to monitor as the identifier and
    80  	// returns when the given signal is delivered to the process. This coexists
    81  	// with the signal() and sigaction() facilities, and has a lower
    82  	// precedence. The filter will record all attempts to deliver a signal to
    83  	// a process, even if the signal has been marked as SIG_IGN, except for the
    84  	// SIGCHLD signal, which, if ignored, won't be recorded by the filter.
    85  	// Event notification happens after normal signal delivery processing. data
    86  	// returns the number of times the signal has occurred since the last call
    87  	// to kevent(). This filter automatically sets the EV_CLEAR flag
    88  	// internally.
    89  	EVFILT_SIGNAL = unix.EVFILT_SIGNAL
    90  
    91  	// EVFILT_TIMER establishes an arbitrary timer identified by ident. When
    92  	// adding a timer, data specifies the timeout period. The timer will be
    93  	// periodic unless EV_ONESHOT is specified. On return, data contains the
    94  	// number of times the timeout has expired since the last call to kevent().
    95  	// This filter automatically sets the EV_CLEAR flag internally. There is a
    96  	// system wide limit on the number of timers which is controlled by the
    97  	// kern.kq_calloutmax sysctl.
    98  	EVFILT_TIMER = unix.EVFILT_TIMER
    99  
   100  	// EVFILT_USER establishes a user event identified by ident which is not
   101  	// associated with any kernel mechanism but is trig- gered by user level
   102  	// code.
   103  	EVFILT_USER = unix.EVFILT_USER
   104  
   105  	// Custom filter value signaling that kqueue instance get closed.
   106  	_EVFILT_CLOSED = -0x7f
   107  )
   108  
   109  // KeventFlag represents kqueue event flag.
   110  type KeventFlag int
   111  
   112  // String returns string representation of flag bits of the form
   113  // "EV_A|EV_B|...".
   114  func (flag KeventFlag) String() (str string) {
   115  	name := func(f KeventFlag, name string) {
   116  		if flag&f == 0 {
   117  			return
   118  		}
   119  		if str != "" {
   120  			str += "|"
   121  		}
   122  		str += name
   123  	}
   124  	name(EV_ADD, "EV_ADD")
   125  	name(EV_ENABLE, "EV_ENABLE")
   126  	name(EV_DISABLE, "EV_DISABLE")
   127  	name(EV_DISPATCH, "EV_DISPATCH")
   128  	name(EV_DELETE, "EV_DELETE")
   129  	name(EV_RECEIPT, "EV_RECEIPT")
   130  	name(EV_ONESHOT, "EV_ONESHOT")
   131  	name(EV_CLEAR, "EV_CLEAR")
   132  	name(EV_EOF, "EV_EOF")
   133  	name(EV_ERROR, "EV_ERROR")
   134  	return
   135  }
   136  
   137  const (
   138  	// EV_ADD adds the event to the kqueue. Re-adding an existing event will modify
   139  	// the parameters of the original event, and not result in a duplicate
   140  	// entry. Adding an event automatically enables it, unless overridden by
   141  	// the EV_DISABLE flag.
   142  	EV_ADD = unix.EV_ADD
   143  
   144  	// EV_ENABLE permits kevent() to return the event if it is triggered.
   145  	EV_ENABLE = unix.EV_ENABLE
   146  
   147  	// EV_DISABLE disables the event so kevent() will not return it. The filter itself is
   148  	// not disabled.
   149  	EV_DISABLE = unix.EV_DISABLE
   150  
   151  	// EV_DISPATCH disables the event source immediately after delivery of an event. See
   152  	// EV_DISABLE above.
   153  	EV_DISPATCH = unix.EV_DISPATCH
   154  
   155  	// EV_DELETE removes the event from the kqueue. Events which are attached to file
   156  	// descriptors are automatically deleted on the last close of the
   157  	// descriptor.
   158  	EV_DELETE = unix.EV_DELETE
   159  
   160  	// EV_RECEIPT is useful for making bulk changes to a kqueue without draining
   161  	// any pending events. When passed as input, it forces EV_ERROR to always
   162  	// be returned. When a filter is successfully added the data field will be
   163  	// zero.
   164  	EV_RECEIPT = unix.EV_RECEIPT
   165  
   166  	// EV_ONESHOT causes the event to return only the first occurrence of the
   167  	// filter being triggered. After the user retrieves the event from the
   168  	// kqueue, it is deleted.
   169  	EV_ONESHOT = unix.EV_ONESHOT
   170  
   171  	// EV_CLEAR makes event state be reset after the event is retrieved by the
   172  	// user. This is useful for filters which report state transitions instead
   173  	// of the current state. Note that some filters may automatically set this
   174  	// flag internally.
   175  	EV_CLEAR = unix.EV_CLEAR
   176  
   177  	// EV_EOF may be set by the filters to indicate filter-specific EOF
   178  	// condition.
   179  	EV_EOF = unix.EV_EOF
   180  
   181  	// EV_ERROR is set to indiacate an error occured with the identtifier.
   182  	EV_ERROR = unix.EV_ERROR
   183  )
   184  
   185  // filterCount is a constant number of available filters which can be
   186  // registered for an identifier.
   187  const filterCount = 8
   188  
   189  // Kevent represents kevent.
   190  type Kevent struct {
   191  	Filter KeventFilter
   192  	Flags  KeventFlag
   193  	Fflags uint32
   194  	Data   int64
   195  }
   196  
   197  // Kevents is a fixed number of pairs of event filter and flags which can be
   198  // registered for an identifier.
   199  type Kevents [8]Kevent
   200  
   201  // KeventHandler is a function that will be called when event occures on
   202  // registered identifier.
   203  type KeventHandler func(Kevent)
   204  
   205  // KqueueConfig contains options for configuration kqueue instance.
   206  type KqueueConfig struct {
   207  	// OnWaitError will be called from goroutine, waiting for events.
   208  	OnWaitError func(error)
   209  }
   210  
   211  func (c *KqueueConfig) withDefaults() (config KqueueConfig) {
   212  	if c != nil {
   213  		config = *c
   214  	}
   215  	if config.OnWaitError == nil {
   216  		config.OnWaitError = defaultOnWaitError
   217  	}
   218  	return config
   219  }
   220  
   221  // Kqueue represents kqueue instance.
   222  type Kqueue struct {
   223  	mu     sync.RWMutex
   224  	fd     int
   225  	cb     map[int]KeventHandler
   226  	done   chan struct{}
   227  	closed bool
   228  }
   229  
   230  // KqueueCreate creates new kqueue instance.
   231  // It starts wait loop in a separate goroutine.
   232  func KqueueCreate(c *KqueueConfig) (*Kqueue, error) {
   233  	config := c.withDefaults()
   234  
   235  	fd, err := unix.Kqueue()
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	kq := &Kqueue{
   241  		fd:   fd,
   242  		cb:   make(map[int]KeventHandler),
   243  		done: make(chan struct{}),
   244  	}
   245  
   246  	go kq.wait(config.OnWaitError)
   247  
   248  	return kq, nil
   249  }
   250  
   251  // Close closes kqueue instance.
   252  // NOTE: not implemented yet.
   253  func (k *Kqueue) Close() error {
   254  	// TODO(): implement close.
   255  	return nil
   256  }
   257  
   258  // Add adds a event handler for identifier fd with given n events.
   259  func (k *Kqueue) Add(fd int, events Kevents, n int, cb KeventHandler) error {
   260  	var kevs [filterCount]unix.Kevent_t
   261  	for i := 0; i < n; i++ {
   262  		kevs[i] = evGet(fd, events[i].Filter, events[i].Flags)
   263  	}
   264  
   265  	arr := unsafe.Pointer(&kevs)
   266  	hdr := &reflect.SliceHeader{
   267  		Data: uintptr(arr),
   268  		Len:  n,
   269  		Cap:  n,
   270  	}
   271  	changes := *(*[]unix.Kevent_t)(unsafe.Pointer(hdr))
   272  
   273  	k.mu.Lock()
   274  	defer k.mu.Unlock()
   275  
   276  	if k.closed {
   277  		return ErrClosed
   278  	}
   279  	if _, has := k.cb[fd]; has {
   280  		return ErrRegistered
   281  	}
   282  	k.cb[fd] = cb
   283  
   284  	_, err := unix.Kevent(k.fd, changes, nil, nil)
   285  
   286  	return err
   287  }
   288  
   289  // Mod modifies events registered for fd.
   290  func (k *Kqueue) Mod(fd int, events Kevents, n int) error {
   291  	var kevs [filterCount]unix.Kevent_t
   292  	for i := 0; i < n; i++ {
   293  		kevs[i] = evGet(fd, events[i].Filter, events[i].Flags)
   294  	}
   295  
   296  	arr := unsafe.Pointer(&kevs)
   297  	hdr := &reflect.SliceHeader{
   298  		Data: uintptr(arr),
   299  		Len:  n,
   300  		Cap:  n,
   301  	}
   302  	changes := *(*[]unix.Kevent_t)(unsafe.Pointer(hdr))
   303  
   304  	k.mu.RLock()
   305  	defer k.mu.RUnlock()
   306  
   307  	if k.closed {
   308  		return ErrClosed
   309  	}
   310  	if _, has := k.cb[fd]; !has {
   311  		return ErrNotRegistered
   312  	}
   313  
   314  	_, err := unix.Kevent(k.fd, changes, nil, nil)
   315  
   316  	return err
   317  }
   318  
   319  // Del removes callback for fd. Note that it does not cleanups events for fd in
   320  // kqueue. You should close fd or call Mod() with EV_DELETE flag set.
   321  func (k *Kqueue) Del(fd int) error {
   322  	k.mu.Lock()
   323  	defer k.mu.Unlock()
   324  
   325  	if k.closed {
   326  		return ErrClosed
   327  	}
   328  	if _, has := k.cb[fd]; !has {
   329  		return ErrNotRegistered
   330  	}
   331  
   332  	delete(k.cb, fd)
   333  
   334  	return nil
   335  }
   336  
   337  func (k *Kqueue) wait(onError func(error)) {
   338  	const (
   339  		maxWaitEventsBegin = 1 << 10 // 1024
   340  		maxWaitEventsStop  = 1 << 15 // 32768
   341  	)
   342  
   343  	defer func() {
   344  		if err := unix.Close(k.fd); err != nil {
   345  			onError(err)
   346  		}
   347  		close(k.done)
   348  	}()
   349  
   350  	evs := make([]unix.Kevent_t, maxWaitEventsBegin)
   351  	cbs := make([]KeventHandler, maxWaitEventsBegin)
   352  
   353  	for {
   354  		n, err := unix.Kevent(k.fd, nil, evs, nil)
   355  		if err != nil {
   356  			if temporaryErr(err) {
   357  				continue
   358  			}
   359  			onError(err)
   360  			return
   361  		}
   362  
   363  		cbs = cbs[:n]
   364  		k.mu.RLock()
   365  		for i := 0; i < n; i++ {
   366  			fd := int(evs[i].Ident)
   367  			if fd == -1 { //todo
   368  				k.mu.RUnlock()
   369  				return
   370  			}
   371  			cbs[i] = k.cb[fd]
   372  		}
   373  		k.mu.RUnlock()
   374  
   375  		for i, cb := range cbs {
   376  			if cb != nil {
   377  				e := evs[i]
   378  				cb(Kevent{
   379  					Filter: KeventFilter(e.Filter),
   380  					Flags:  KeventFlag(e.Flags),
   381  					Data:   e.Data,
   382  					Fflags: e.Fflags,
   383  				})
   384  				cbs[i] = nil
   385  			}
   386  		}
   387  
   388  		if n == len(evs) && n*2 <= maxWaitEventsStop {
   389  			evs = make([]unix.Kevent_t, n*2)
   390  			cbs = make([]KeventHandler, n*2)
   391  		}
   392  	}
   393  }
   394  
   395  func evGet(fd int, filter KeventFilter, flags KeventFlag) unix.Kevent_t {
   396  	return unix.Kevent_t{
   397  		Ident:  uint64(fd),
   398  		Filter: int16(filter),
   399  		Flags:  uint16(flags),
   400  	}
   401  }