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 }