github.com/GuanceCloud/cliutils@v1.1.21/network/ws/epoll_bsd.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  //go:build darwin || netbsd || freebsd || openbsd || dragonfly
     7  // +build darwin netbsd freebsd openbsd dragonfly
     8  
     9  package ws
    10  
    11  import (
    12  	"errors"
    13  	"net"
    14  	"sync"
    15  	"syscall"
    16  )
    17  
    18  type epoll struct {
    19  	fd          int
    20  	ts          syscall.Timespec
    21  	connections map[int]net.Conn
    22  	changes     []syscall.Kevent_t
    23  	lock        *sync.RWMutex
    24  }
    25  
    26  func MkEpoll() (*epoll, error) {
    27  	fd, err := syscall.Kqueue()
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	_, err = syscall.Kevent(fd, []syscall.Kevent_t{
    33  		{
    34  			Ident:  0,
    35  			Filter: syscall.EVFILT_USER,
    36  			Flags:  syscall.EV_ADD | syscall.EV_CLEAR,
    37  		},
    38  	}, nil, nil)
    39  
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	return &epoll{
    45  		fd:          fd,
    46  		lock:        &sync.RWMutex{},
    47  		ts:          syscall.NsecToTimespec(1e9),
    48  		connections: make(map[int]net.Conn),
    49  	}, nil
    50  }
    51  
    52  func (e *epoll) Add(conn net.Conn) error {
    53  	fd := websocketFD(conn)
    54  
    55  	e.lock.Lock()
    56  	defer e.lock.Unlock()
    57  
    58  	e.changes = append(e.changes,
    59  		syscall.Kevent_t{
    60  			Ident:  uint64(fd),
    61  			Flags:  syscall.EV_ADD | syscall.EV_EOF,
    62  			Filter: syscall.EVFILT_READ,
    63  		})
    64  	e.connections[fd] = conn
    65  
    66  	return nil
    67  }
    68  
    69  func (e *epoll) Remove(conn net.Conn) error {
    70  	fd := websocketFD(conn)
    71  
    72  	e.lock.Lock()
    73  	defer e.lock.Unlock()
    74  
    75  	if len(e.changes) <= 1 {
    76  		e.changes = nil
    77  	} else {
    78  		changes := make([]syscall.Kevent_t, 0, len(e.changes)-1)
    79  		ident := uint64(fd)
    80  		for _, ke := range e.changes {
    81  			if ke.Ident != ident {
    82  				changes = append(changes, ke)
    83  			}
    84  		}
    85  
    86  		e.changes = changes
    87  	}
    88  
    89  	delete(e.connections, fd)
    90  	return nil
    91  }
    92  
    93  func (e *epoll) Wait(count int) ([]net.Conn, error) {
    94  	events := make([]syscall.Kevent_t, count)
    95  
    96  	e.lock.RLock()
    97  	changes := e.changes
    98  	e.lock.RUnlock()
    99  
   100  retry:
   101  
   102  	n, err := syscall.Kevent(e.fd, changes, events, &e.ts)
   103  	if err != nil {
   104  		if errors.Is(err, syscall.EINTR) {
   105  			goto retry
   106  		}
   107  
   108  		l.Error("Wait() error: %s", err.Error())
   109  		return nil, err
   110  	}
   111  
   112  	connections := make([]net.Conn, 0, n)
   113  	e.lock.RLock()
   114  
   115  	for i := 0; i < n; i++ {
   116  		conn := e.connections[int(events[i].Ident)]
   117  		if (events[i].Flags & syscall.EV_EOF) == syscall.EV_EOF {
   118  			if err := conn.Close(); err != nil {
   119  				l.Warnf("Close: %s, ignored", err)
   120  			}
   121  		}
   122  		connections = append(connections, conn)
   123  	}
   124  
   125  	e.lock.RUnlock()
   126  
   127  	return connections, nil
   128  }
   129  
   130  func (e *epoll) Close() error {
   131  	e.lock.Lock()
   132  	defer e.lock.Unlock()
   133  
   134  	e.connections = nil
   135  	e.changes = nil
   136  	l.Debugf("epoll closed")
   137  	return syscall.Close(e.fd)
   138  }