├── .travis.yml ├── LICENSE ├── README.md └── netpoll ├── epoll.go ├── epoll_test.go ├── handle.go ├── handle_stub.go ├── handle_unix.go ├── kqueue.go ├── netpoll.go ├── netpoll_epoll.go ├── netpoll_kqueue.go ├── netpoll_stub.go ├── netpoll_unix_test.go └── util.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | go: 8 | - 1.8 9 | 10 | install: 11 | - go get golang.org/x/sys/unix 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Mail.Ru Group 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easygo 2 | 3 | [![GoDoc][godoc:image]][godoc:url] 4 | [![Travis][travis:image]][travis:url] 5 | 6 | > Tools for building go apps. 7 | 8 | [godoc:image]: https://godoc.org/github.com/mailru/easygo?status.svg 9 | [godoc:url]: https://godoc.org/github.com/mailru/easygo 10 | [travis:image]: https://travis-ci.org/mailru/easygo.svg?branch=master 11 | [travis:url]: https://travis-ci.org/mailru/easygo 12 | -------------------------------------------------------------------------------- /netpoll/epoll.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package netpoll 4 | 5 | import ( 6 | "sync" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | // EpollEvent represents epoll events configuration bit mask. 12 | type EpollEvent uint32 13 | 14 | // EpollEvents that are mapped to epoll_event.events possible values. 15 | const ( 16 | EPOLLIN = unix.EPOLLIN 17 | EPOLLOUT = unix.EPOLLOUT 18 | EPOLLRDHUP = unix.EPOLLRDHUP 19 | EPOLLPRI = unix.EPOLLPRI 20 | EPOLLERR = unix.EPOLLERR 21 | EPOLLHUP = unix.EPOLLHUP 22 | EPOLLET = unix.EPOLLET 23 | EPOLLONESHOT = unix.EPOLLONESHOT 24 | 25 | // _EPOLLCLOSED is a special EpollEvent value the receipt of which means 26 | // that the epoll instance is closed. 27 | _EPOLLCLOSED = 0x20 28 | ) 29 | 30 | // String returns a string representation of EpollEvent. 31 | func (evt EpollEvent) String() (str string) { 32 | name := func(event EpollEvent, name string) { 33 | if evt&event == 0 { 34 | return 35 | } 36 | if str != "" { 37 | str += "|" 38 | } 39 | str += name 40 | } 41 | 42 | name(EPOLLIN, "EPOLLIN") 43 | name(EPOLLOUT, "EPOLLOUT") 44 | name(EPOLLRDHUP, "EPOLLRDHUP") 45 | name(EPOLLPRI, "EPOLLPRI") 46 | name(EPOLLERR, "EPOLLERR") 47 | name(EPOLLHUP, "EPOLLHUP") 48 | name(EPOLLET, "EPOLLET") 49 | name(EPOLLONESHOT, "EPOLLONESHOT") 50 | name(_EPOLLCLOSED, "_EPOLLCLOSED") 51 | 52 | return 53 | } 54 | 55 | // Epoll represents single epoll instance. 56 | type Epoll struct { 57 | mu sync.RWMutex 58 | 59 | fd int 60 | eventFd int 61 | closed bool 62 | waitDone chan struct{} 63 | 64 | callbacks map[int]func(EpollEvent) 65 | } 66 | 67 | // EpollConfig contains options for Epoll instance configuration. 68 | type EpollConfig struct { 69 | // OnWaitError will be called from goroutine, waiting for events. 70 | OnWaitError func(error) 71 | } 72 | 73 | func (c *EpollConfig) withDefaults() (config EpollConfig) { 74 | if c != nil { 75 | config = *c 76 | } 77 | if config.OnWaitError == nil { 78 | config.OnWaitError = defaultOnWaitError 79 | } 80 | return config 81 | } 82 | 83 | // EpollCreate creates new epoll instance. 84 | // It starts the wait loop in separate goroutine. 85 | func EpollCreate(c *EpollConfig) (*Epoll, error) { 86 | config := c.withDefaults() 87 | 88 | fd, err := unix.EpollCreate1(0) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | r0, _, errno := unix.Syscall(unix.SYS_EVENTFD2, 0, 0, 0) 94 | if errno != 0 { 95 | return nil, errno 96 | } 97 | eventFd := int(r0) 98 | 99 | // Set finalizer for write end of socket pair to avoid data races when 100 | // closing Epoll instance and EBADF errors on writing ctl bytes from callers. 101 | err = unix.EpollCtl(fd, unix.EPOLL_CTL_ADD, eventFd, &unix.EpollEvent{ 102 | Events: unix.EPOLLIN, 103 | Fd: int32(eventFd), 104 | }) 105 | if err != nil { 106 | unix.Close(fd) 107 | unix.Close(eventFd) 108 | return nil, err 109 | } 110 | 111 | ep := &Epoll{ 112 | fd: fd, 113 | eventFd: eventFd, 114 | callbacks: make(map[int]func(EpollEvent)), 115 | waitDone: make(chan struct{}), 116 | } 117 | 118 | // Run wait loop. 119 | go ep.wait(config.OnWaitError) 120 | 121 | return ep, nil 122 | } 123 | 124 | // closeBytes used for writing to eventfd. 125 | var closeBytes = []byte{1, 0, 0, 0, 0, 0, 0, 0} 126 | 127 | // Close stops wait loop and closes all underlying resources. 128 | func (ep *Epoll) Close() (err error) { 129 | ep.mu.Lock() 130 | { 131 | if ep.closed { 132 | ep.mu.Unlock() 133 | return ErrClosed 134 | } 135 | ep.closed = true 136 | 137 | if _, err = unix.Write(ep.eventFd, closeBytes); err != nil { 138 | ep.mu.Unlock() 139 | return 140 | } 141 | } 142 | ep.mu.Unlock() 143 | 144 | <-ep.waitDone 145 | 146 | if err = unix.Close(ep.eventFd); err != nil { 147 | return 148 | } 149 | 150 | ep.mu.Lock() 151 | // Set callbacks to nil preventing long mu.Lock() hold. 152 | // This could increase the speed of retreiving ErrClosed in other calls to 153 | // current epoll instance. 154 | // Setting callbacks to nil is safe here because no one should read after 155 | // closed flag is true. 156 | callbacks := ep.callbacks 157 | ep.callbacks = nil 158 | ep.mu.Unlock() 159 | 160 | for _, cb := range callbacks { 161 | if cb != nil { 162 | cb(_EPOLLCLOSED) 163 | } 164 | } 165 | 166 | return 167 | } 168 | 169 | // Add adds fd to epoll set with given events. 170 | // Callback will be called on each received event from epoll. 171 | // Note that _EPOLLCLOSED is triggered for every cb when epoll closed. 172 | func (ep *Epoll) Add(fd int, events EpollEvent, cb func(EpollEvent)) (err error) { 173 | ev := &unix.EpollEvent{ 174 | Events: uint32(events), 175 | Fd: int32(fd), 176 | } 177 | 178 | ep.mu.Lock() 179 | defer ep.mu.Unlock() 180 | 181 | if ep.closed { 182 | return ErrClosed 183 | } 184 | if _, has := ep.callbacks[fd]; has { 185 | return ErrRegistered 186 | } 187 | ep.callbacks[fd] = cb 188 | 189 | return unix.EpollCtl(ep.fd, unix.EPOLL_CTL_ADD, fd, ev) 190 | } 191 | 192 | // Del removes fd from epoll set. 193 | func (ep *Epoll) Del(fd int) (err error) { 194 | ep.mu.Lock() 195 | defer ep.mu.Unlock() 196 | 197 | if ep.closed { 198 | return ErrClosed 199 | } 200 | if _, ok := ep.callbacks[fd]; !ok { 201 | return ErrNotRegistered 202 | } 203 | 204 | delete(ep.callbacks, fd) 205 | 206 | return unix.EpollCtl(ep.fd, unix.EPOLL_CTL_DEL, fd, nil) 207 | } 208 | 209 | // Mod sets to listen events on fd. 210 | func (ep *Epoll) Mod(fd int, events EpollEvent) (err error) { 211 | ev := &unix.EpollEvent{ 212 | Events: uint32(events), 213 | Fd: int32(fd), 214 | } 215 | 216 | ep.mu.RLock() 217 | defer ep.mu.RUnlock() 218 | 219 | if ep.closed { 220 | return ErrClosed 221 | } 222 | if _, ok := ep.callbacks[fd]; !ok { 223 | return ErrNotRegistered 224 | } 225 | 226 | return unix.EpollCtl(ep.fd, unix.EPOLL_CTL_MOD, fd, ev) 227 | } 228 | 229 | const ( 230 | maxWaitEventsBegin = 1024 231 | maxWaitEventsStop = 32768 232 | ) 233 | 234 | func (ep *Epoll) wait(onError func(error)) { 235 | defer func() { 236 | if err := unix.Close(ep.fd); err != nil { 237 | onError(err) 238 | } 239 | close(ep.waitDone) 240 | }() 241 | 242 | events := make([]unix.EpollEvent, maxWaitEventsBegin) 243 | callbacks := make([]func(EpollEvent), 0, maxWaitEventsBegin) 244 | 245 | for { 246 | n, err := unix.EpollWait(ep.fd, events, -1) 247 | if err != nil { 248 | if temporaryErr(err) { 249 | continue 250 | } 251 | onError(err) 252 | return 253 | } 254 | 255 | callbacks = callbacks[:n] 256 | 257 | ep.mu.RLock() 258 | for i := 0; i < n; i++ { 259 | fd := int(events[i].Fd) 260 | if fd == ep.eventFd { // signal to close 261 | ep.mu.RUnlock() 262 | return 263 | } 264 | callbacks[i] = ep.callbacks[fd] 265 | } 266 | ep.mu.RUnlock() 267 | 268 | for i := 0; i < n; i++ { 269 | if cb := callbacks[i]; cb != nil { 270 | cb(EpollEvent(events[i].Events)) 271 | callbacks[i] = nil 272 | } 273 | } 274 | 275 | if n == len(events) && n*2 <= maxWaitEventsStop { 276 | events = make([]unix.EpollEvent, n*2) 277 | callbacks = make([]func(EpollEvent), 0, n*2) 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /netpoll/epoll_test.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package netpoll 4 | 5 | import ( 6 | "bytes" 7 | "io" 8 | "net" 9 | "strings" 10 | "testing" 11 | "time" 12 | 13 | "golang.org/x/sys/unix" 14 | ) 15 | 16 | func TestEpollCreate(t *testing.T) { 17 | s, err := EpollCreate(epollConfig(t)) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | if err = s.Close(); err != nil { 22 | t.Fatal(err) 23 | } 24 | } 25 | 26 | func TestEpollAddClosed(t *testing.T) { 27 | s, err := EpollCreate(epollConfig(t)) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | if err = s.Close(); err != nil { 32 | t.Fatal(err) 33 | } 34 | if err = s.Add(42, 0, nil); err != ErrClosed { 35 | t.Fatalf("Add() = %s; want %s", err, ErrClosed) 36 | } 37 | } 38 | 39 | func TestEpollDel(t *testing.T) { 40 | ln := RunEchoServer(t) 41 | defer ln.Close() 42 | 43 | conn, err := net.Dial("tcp", ln.Addr().String()) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | defer conn.Close() 48 | 49 | s, err := EpollCreate(epollConfig(t)) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | f, err := conn.(filer).File() 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | 59 | err = s.Add(int(f.Fd()), EPOLLIN, func(events EpollEvent) {}) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | if err = s.Del(int(f.Fd())); err != nil { 64 | t.Errorf("unexpected error: %s", err) 65 | } 66 | 67 | if err = s.Close(); err != nil { 68 | t.Fatal(err) 69 | } 70 | } 71 | 72 | func TestEpollServer(t *testing.T) { 73 | ep, err := EpollCreate(epollConfig(t)) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | 78 | // Create listener on port 4444. 79 | ln, err := listen(4444) 80 | if err != nil { 81 | t.Fatal(err) 82 | } 83 | defer unix.Close(ln) 84 | 85 | var received bytes.Buffer 86 | done := make(chan struct{}) 87 | 88 | // Add listener fd to epoll instance to know when there are new incoming 89 | // connections. 90 | ep.Add(ln, EPOLLIN, func(evt EpollEvent) { 91 | if evt&_EPOLLCLOSED != 0 { 92 | return 93 | } 94 | 95 | // Accept new incoming connection. 96 | conn, _, err := unix.Accept(ln) 97 | if err != nil { 98 | t.Fatalf("could not accept: %s", err) 99 | } 100 | 101 | // Socket must not block read() from it. 102 | unix.SetNonblock(conn, true) 103 | 104 | // Add connection fd to epoll instance to get notifications about 105 | // available data. 106 | ep.Add(conn, EPOLLIN|EPOLLET|EPOLLHUP|EPOLLRDHUP, func(evt EpollEvent) { 107 | // If EPOLLRDHUP is supported, it will be triggered after conn 108 | // close() or shutdown(). In older versions EPOLLHUP is triggered. 109 | if evt&_EPOLLCLOSED != 0 { 110 | return 111 | } 112 | 113 | var buf [128]byte 114 | for { 115 | n, _ := unix.Read(conn, buf[:]) 116 | if n == 0 { 117 | close(done) 118 | } 119 | if n <= 0 { 120 | break 121 | } 122 | received.Write(buf[:n]) 123 | } 124 | }) 125 | }) 126 | 127 | conn, err := dial(4444) 128 | if err != nil { 129 | t.Fatal(err) 130 | } 131 | 132 | // Write some data bytes one by one to the conn. 133 | data := []byte("hello, epoll!") 134 | for i := 0; i < len(data); i++ { 135 | if _, err := unix.Write(conn, data[i:i+1]); err != nil { 136 | t.Fatalf("could not make %d-th write (%v): %s", i, string(data[i]), err) 137 | } 138 | time.Sleep(time.Millisecond) 139 | } 140 | 141 | unix.Close(conn) 142 | <-done 143 | 144 | if err = ep.Close(); err != nil { 145 | t.Fatal(err) 146 | } 147 | if !bytes.Equal(received.Bytes(), data) { 148 | t.Errorf("bytes not equal") 149 | } 150 | } 151 | 152 | func dial(port int) (conn int, err error) { 153 | conn, err = unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) 154 | if err != nil { 155 | return 156 | } 157 | 158 | addr := &unix.SockaddrInet4{ 159 | Port: port, 160 | Addr: [4]byte{0x7f, 0, 0, 1}, // 127.0.0.1 161 | } 162 | 163 | err = unix.Connect(conn, addr) 164 | if err == nil { 165 | err = unix.SetNonblock(conn, true) 166 | } 167 | 168 | return 169 | } 170 | 171 | func listen(port int) (ln int, err error) { 172 | ln, err = unix.Socket(unix.AF_INET, unix.O_NONBLOCK|unix.SOCK_STREAM, 0) 173 | if err != nil { 174 | return 175 | } 176 | 177 | // Need for avoid receiving EADDRINUSE error. 178 | // Closed listener could be in TIME_WAIT state some time. 179 | unix.SetsockoptInt(ln, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) 180 | 181 | addr := &unix.SockaddrInet4{ 182 | Port: port, 183 | Addr: [4]byte{0x7f, 0, 0, 1}, // 127.0.0.1 184 | } 185 | 186 | if err = unix.Bind(ln, addr); err != nil { 187 | return 188 | } 189 | err = unix.Listen(ln, 4) 190 | 191 | return 192 | } 193 | 194 | // RunEchoServer starts tcp echo server. 195 | func RunEchoServer(tb testing.TB) net.Listener { 196 | ln, err := net.Listen("tcp", "localhost:") 197 | if err != nil { 198 | tb.Fatal(err) 199 | return nil 200 | } 201 | go func() { 202 | for { 203 | conn, err := ln.Accept() 204 | if err != nil { 205 | if strings.Contains(err.Error(), "use of closed network connection") { 206 | // Server closed. 207 | return 208 | } 209 | 210 | tb.Fatal(err) 211 | } 212 | go func() { 213 | if _, err := io.Copy(conn, conn); err != nil && err != io.EOF { 214 | tb.Fatal(err) 215 | } 216 | }() 217 | } 218 | }() 219 | return ln 220 | } 221 | 222 | func epollConfig(tb testing.TB) *EpollConfig { 223 | return &EpollConfig{ 224 | OnWaitError: func(err error) { 225 | tb.Fatal(err) 226 | }, 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /netpoll/handle.go: -------------------------------------------------------------------------------- 1 | package netpoll 2 | 3 | import ( 4 | "net" 5 | "os" 6 | ) 7 | 8 | // filer describes an object that has ability to return os.File. 9 | type filer interface { 10 | // File returns a copy of object's file descriptor. 11 | File() (*os.File, error) 12 | } 13 | 14 | // Desc is a network connection within netpoll descriptor. 15 | // It's methods are not goroutine safe. 16 | type Desc struct { 17 | file *os.File 18 | event Event 19 | } 20 | 21 | // NewDesc creates descriptor from custom fd. 22 | func NewDesc(fd uintptr, ev Event) *Desc { 23 | return &Desc{os.NewFile(fd, ""), ev} 24 | } 25 | 26 | // Close closes underlying file. 27 | func (h *Desc) Close() error { 28 | return h.file.Close() 29 | } 30 | 31 | func (h *Desc) fd() int { 32 | return int(h.file.Fd()) 33 | } 34 | 35 | // Must is a helper that wraps a call to a function returning (*Desc, error). 36 | // It panics if the error is non-nil and returns desc if not. 37 | // It is intended for use in short Desc initializations. 38 | func Must(desc *Desc, err error) *Desc { 39 | if err != nil { 40 | panic(err) 41 | } 42 | return desc 43 | } 44 | 45 | // HandleRead creates read descriptor for further use in Poller methods. 46 | // It is the same as Handle(conn, EventRead|EventEdgeTriggered). 47 | func HandleRead(conn net.Conn) (*Desc, error) { 48 | return Handle(conn, EventRead|EventEdgeTriggered) 49 | } 50 | 51 | // HandleReadOnce creates read descriptor for further use in Poller methods. 52 | // It is the same as Handle(conn, EventRead|EventOneShot). 53 | func HandleReadOnce(conn net.Conn) (*Desc, error) { 54 | return Handle(conn, EventRead|EventOneShot) 55 | } 56 | 57 | // HandleWrite creates write descriptor for further use in Poller methods. 58 | // It is the same as Handle(conn, EventWrite|EventEdgeTriggered). 59 | func HandleWrite(conn net.Conn) (*Desc, error) { 60 | return Handle(conn, EventWrite|EventEdgeTriggered) 61 | } 62 | 63 | // HandleWriteOnce creates write descriptor for further use in Poller methods. 64 | // It is the same as Handle(conn, EventWrite|EventOneShot). 65 | func HandleWriteOnce(conn net.Conn) (*Desc, error) { 66 | return Handle(conn, EventWrite|EventOneShot) 67 | } 68 | 69 | // HandleReadWrite creates read and write descriptor for further use in Poller 70 | // methods. 71 | // It is the same as Handle(conn, EventRead|EventWrite|EventEdgeTriggered). 72 | func HandleReadWrite(conn net.Conn) (*Desc, error) { 73 | return Handle(conn, EventRead|EventWrite|EventEdgeTriggered) 74 | } 75 | 76 | // Handle creates new Desc with given conn and event. 77 | // Returned descriptor could be used as argument to Start(), Resume() and 78 | // Stop() methods of some Poller implementation. 79 | func Handle(conn net.Conn, event Event) (*Desc, error) { 80 | desc, err := handle(conn, event) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | // Set the file back to non blocking mode since conn.File() sets underlying 86 | // os.File to blocking mode. This is useful to get conn.Set{Read}Deadline 87 | // methods still working on source Conn. 88 | // 89 | // See https://golang.org/pkg/net/#TCPConn.File 90 | // See /usr/local/go/src/net/net.go: conn.File() 91 | if err = setNonblock(desc.fd(), true); err != nil { 92 | return nil, os.NewSyscallError("setnonblock", err) 93 | } 94 | 95 | return desc, nil 96 | } 97 | 98 | // HandleListener returns descriptor for a net.Listener. 99 | func HandleListener(ln net.Listener, event Event) (*Desc, error) { 100 | return handle(ln, event) 101 | } 102 | 103 | func handle(x interface{}, event Event) (*Desc, error) { 104 | f, ok := x.(filer) 105 | if !ok { 106 | return nil, ErrNotFiler 107 | } 108 | 109 | // Get a copy of fd. 110 | file, err := f.File() 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | return &Desc{ 116 | file: file, 117 | event: event, 118 | }, nil 119 | } 120 | -------------------------------------------------------------------------------- /netpoll/handle_stub.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd 2 | 3 | package netpoll 4 | 5 | import "fmt" 6 | 7 | func setNonblock(fd int, nonblocking bool) (err error) { 8 | return fmt.Errorf("setNonblock is not supported on this operating system") 9 | } 10 | -------------------------------------------------------------------------------- /netpoll/handle_unix.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin dragonfly freebsd netbsd openbsd 2 | 3 | package netpoll 4 | 5 | import "syscall" 6 | 7 | func setNonblock(fd int, nonblocking bool) (err error) { 8 | return syscall.SetNonblock(fd, nonblocking) 9 | } 10 | -------------------------------------------------------------------------------- /netpoll/kqueue.go: -------------------------------------------------------------------------------- 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 | } 402 | -------------------------------------------------------------------------------- /netpoll/netpoll.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package netpoll provides a portable interface for network I/O event 3 | notification facility. 4 | 5 | Its API is intended for monitoring multiple file descriptors to see if I/O is 6 | possible on any of them. It supports edge-triggered and level-triggered 7 | interfaces. 8 | 9 | To get more info you could look at operating system API documentation of 10 | particular netpoll implementations: 11 | - epoll on linux; 12 | - kqueue on bsd; 13 | 14 | The Handle function creates netpoll.Desc for further use in Poller's methods: 15 | 16 | desc, err := netpoll.Handle(conn, netpoll.EventRead | netpoll.EventEdgeTriggered) 17 | if err != nil { 18 | // handle error 19 | } 20 | 21 | The Poller describes os-dependent network poller: 22 | 23 | poller, err := netpoll.New(nil) 24 | if err != nil { 25 | // handle error 26 | } 27 | 28 | // Get netpoll descriptor with EventRead|EventEdgeTriggered. 29 | desc := netpoll.Must(netpoll.HandleRead(conn)) 30 | 31 | poller.Start(desc, func(ev netpoll.Event) { 32 | if ev&netpoll.EventReadHup != 0 { 33 | poller.Stop(desc) 34 | conn.Close() 35 | return 36 | } 37 | 38 | _, err := ioutil.ReadAll(conn) 39 | if err != nil { 40 | // handle error 41 | } 42 | }) 43 | 44 | Currently, Poller is implemented only for Linux. 45 | */ 46 | package netpoll 47 | 48 | import ( 49 | "fmt" 50 | "log" 51 | ) 52 | 53 | var ( 54 | // ErrNotFiler is returned by Handle* functions to indicate that given 55 | // net.Conn does not provide access to its file descriptor. 56 | ErrNotFiler = fmt.Errorf("could not get file descriptor") 57 | 58 | // ErrClosed is returned by Poller methods to indicate that instance is 59 | // closed and operation could not be processed. 60 | ErrClosed = fmt.Errorf("poller instance is closed") 61 | 62 | // ErrRegistered is returned by Poller Start() method to indicate that 63 | // connection with the same underlying file descriptor was already 64 | // registered within the poller instance. 65 | ErrRegistered = fmt.Errorf("file descriptor is already registered in poller instance") 66 | 67 | // ErrNotRegistered is returned by Poller Stop() and Resume() methods to 68 | // indicate that connection with the same underlying file descriptor was 69 | // not registered before within the poller instance. 70 | ErrNotRegistered = fmt.Errorf("file descriptor was not registered before in poller instance") 71 | ) 72 | 73 | // Event represents netpoll configuration bit mask. 74 | type Event uint16 75 | 76 | // Event values that denote the type of events that caller want to receive. 77 | const ( 78 | EventRead Event = 0x1 79 | EventWrite = 0x2 80 | ) 81 | 82 | // Event values that configure the Poller's behavior. 83 | const ( 84 | EventOneShot Event = 0x4 85 | EventEdgeTriggered = 0x8 86 | ) 87 | 88 | // Event values that could be passed to CallbackFn as additional information 89 | // event. 90 | const ( 91 | // EventHup is indicates that some side of i/o operations (receive, send or 92 | // both) is closed. 93 | // Usually (depending on operating system and its version) the EventReadHup 94 | // or EventWriteHup are also set int Event value. 95 | EventHup Event = 0x10 96 | 97 | EventReadHup = 0x20 98 | EventWriteHup = 0x40 99 | 100 | EventErr = 0x80 101 | 102 | // EventPollerClosed is a special Event value the receipt of which means that the 103 | // Poller instance is closed. 104 | EventPollerClosed = 0x8000 105 | ) 106 | 107 | // String returns a string representation of Event. 108 | func (ev Event) String() (str string) { 109 | name := func(event Event, name string) { 110 | if ev&event == 0 { 111 | return 112 | } 113 | if str != "" { 114 | str += "|" 115 | } 116 | str += name 117 | } 118 | 119 | name(EventRead, "EventRead") 120 | name(EventWrite, "EventWrite") 121 | name(EventOneShot, "EventOneShot") 122 | name(EventEdgeTriggered, "EventEdgeTriggered") 123 | name(EventReadHup, "EventReadHup") 124 | name(EventWriteHup, "EventWriteHup") 125 | name(EventHup, "EventHup") 126 | name(EventErr, "EventErr") 127 | name(EventPollerClosed, "EventPollerClosed") 128 | 129 | return 130 | } 131 | 132 | // Poller describes an object that implements logic of polling connections for 133 | // i/o events such as availability of read() or write() operations. 134 | type Poller interface { 135 | // Start adds desc to the observation list. 136 | // 137 | // Note that if desc was configured with OneShot event, then poller will 138 | // remove it from its observation list. If you will be interested in 139 | // receiving events after the callback, call Resume(desc). 140 | // 141 | // Note that Resume() call directly inside desc's callback could cause 142 | // deadlock. 143 | // 144 | // Note that multiple calls with same desc will produce unexpected 145 | // behavior. 146 | Start(*Desc, CallbackFn) error 147 | 148 | // Stop removes desc from the observation list. 149 | // 150 | // Note that it does not call desc.Close(). 151 | Stop(*Desc) error 152 | 153 | // Resume enables observation of desc. 154 | // 155 | // It is useful when desc was configured with EventOneShot. 156 | // It should be called only after Start(). 157 | // 158 | // Note that if there no need to observe desc anymore, you should call 159 | // Stop() to prevent memory leaks. 160 | Resume(*Desc) error 161 | } 162 | 163 | // CallbackFn is a function that will be called on kernel i/o event 164 | // notification. 165 | type CallbackFn func(Event) 166 | 167 | // Config contains options for Poller configuration. 168 | type Config struct { 169 | // OnWaitError will be called from goroutine, waiting for events. 170 | OnWaitError func(error) 171 | } 172 | 173 | func (c *Config) withDefaults() (config Config) { 174 | if c != nil { 175 | config = *c 176 | } 177 | if config.OnWaitError == nil { 178 | config.OnWaitError = defaultOnWaitError 179 | } 180 | return config 181 | } 182 | 183 | func defaultOnWaitError(err error) { 184 | log.Printf("netpoll: wait loop error: %s", err) 185 | } 186 | -------------------------------------------------------------------------------- /netpoll/netpoll_epoll.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package netpoll 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | // New creates new epoll-based Poller instance with given config. 10 | func New(c *Config) (Poller, error) { 11 | cfg := c.withDefaults() 12 | 13 | epoll, err := EpollCreate(&EpollConfig{ 14 | OnWaitError: cfg.OnWaitError, 15 | }) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return poller{epoll}, nil 21 | } 22 | 23 | // poller implements Poller interface. 24 | type poller struct { 25 | *Epoll 26 | } 27 | 28 | // Start implements Poller.Start() method. 29 | func (ep poller) Start(desc *Desc, cb CallbackFn) error { 30 | err := ep.Add(desc.fd(), toEpollEvent(desc.event), 31 | func(ep EpollEvent) { 32 | var event Event 33 | 34 | if ep&EPOLLHUP != 0 { 35 | event |= EventHup 36 | } 37 | if ep&EPOLLRDHUP != 0 { 38 | event |= EventReadHup 39 | } 40 | if ep&EPOLLIN != 0 { 41 | event |= EventRead 42 | } 43 | if ep&EPOLLOUT != 0 { 44 | event |= EventWrite 45 | } 46 | if ep&EPOLLERR != 0 { 47 | event |= EventErr 48 | } 49 | if ep&_EPOLLCLOSED != 0 { 50 | event |= EventPollerClosed 51 | } 52 | 53 | cb(event) 54 | }, 55 | ) 56 | if err == nil { 57 | if err = setNonblock(desc.fd(), true); err != nil { 58 | return os.NewSyscallError("setnonblock", err) 59 | } 60 | } 61 | return err 62 | } 63 | 64 | // Stop implements Poller.Stop() method. 65 | func (ep poller) Stop(desc *Desc) error { 66 | return ep.Del(desc.fd()) 67 | } 68 | 69 | // Resume implements Poller.Resume() method. 70 | func (ep poller) Resume(desc *Desc) error { 71 | return ep.Mod(desc.fd(), toEpollEvent(desc.event)) 72 | } 73 | 74 | func toEpollEvent(event Event) (ep EpollEvent) { 75 | if event&EventRead != 0 { 76 | ep |= EPOLLIN | EPOLLRDHUP 77 | } 78 | if event&EventWrite != 0 { 79 | ep |= EPOLLOUT 80 | } 81 | if event&EventOneShot != 0 { 82 | ep |= EPOLLONESHOT 83 | } 84 | if event&EventEdgeTriggered != 0 { 85 | ep |= EPOLLET 86 | } 87 | return ep 88 | } 89 | -------------------------------------------------------------------------------- /netpoll/netpoll_kqueue.go: -------------------------------------------------------------------------------- 1 | // +build darwin dragonfly freebsd netbsd openbsd 2 | 3 | package netpoll 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | // New creates new kqueue-based Poller instance with given config. 10 | func New(c *Config) (Poller, error) { 11 | cfg := c.withDefaults() 12 | 13 | kq, err := KqueueCreate(&KqueueConfig{ 14 | OnWaitError: cfg.OnWaitError, 15 | }) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return poller{kq}, nil 21 | } 22 | 23 | type poller struct { 24 | *Kqueue 25 | } 26 | 27 | func (p poller) Start(desc *Desc, cb CallbackFn) error { 28 | n, events := toKevents(desc.event, true) 29 | err := p.Add(desc.fd(), events, n, func(kev Kevent) { 30 | var ( 31 | event Event 32 | 33 | flags = kev.Flags 34 | filter = kev.Filter 35 | ) 36 | 37 | // Set EventHup for any EOF flag. Below will be more precise detection 38 | // of what exatcly HUP occured. 39 | if flags&EV_EOF != 0 { 40 | event |= EventHup 41 | } 42 | 43 | if filter == EVFILT_READ { 44 | event |= EventRead 45 | if flags&EV_EOF != 0 { 46 | event |= EventReadHup 47 | } 48 | } 49 | if filter == EVFILT_WRITE { 50 | event |= EventWrite 51 | if flags&EV_EOF != 0 { 52 | event |= EventWriteHup 53 | } 54 | } 55 | if flags&EV_ERROR != 0 { 56 | event |= EventErr 57 | } 58 | if filter == _EVFILT_CLOSED { 59 | event |= EventPollerClosed 60 | } 61 | 62 | cb(event) 63 | }) 64 | if err == nil { 65 | if err = setNonblock(desc.fd(), true); err != nil { 66 | return os.NewSyscallError("setnonblock", err) 67 | } 68 | } 69 | return err 70 | } 71 | 72 | func (p poller) Stop(desc *Desc) error { 73 | n, events := toKevents(desc.event, false) 74 | if err := p.Del(desc.fd()); err != nil { 75 | return err 76 | } 77 | if err := p.Mod(desc.fd(), events, n); err != nil && err != ErrNotRegistered { 78 | return err 79 | } 80 | return nil 81 | } 82 | 83 | func (p poller) Resume(desc *Desc) error { 84 | n, events := toKevents(desc.event, true) 85 | return p.Mod(desc.fd(), events, n) 86 | } 87 | 88 | func toKevents(event Event, add bool) (n int, ks Kevents) { 89 | var flags KeventFlag 90 | if add { 91 | flags = EV_ADD 92 | if event&EventOneShot != 0 { 93 | flags |= EV_ONESHOT 94 | } 95 | if event&EventEdgeTriggered != 0 { 96 | flags |= EV_CLEAR 97 | } 98 | } else { 99 | flags = EV_DELETE 100 | } 101 | if event&EventRead != 0 { 102 | ks[n].Flags = flags 103 | ks[n].Filter = EVFILT_READ 104 | n++ 105 | } 106 | if event&EventWrite != 0 { 107 | ks[n].Flags = flags 108 | ks[n].Filter = EVFILT_WRITE 109 | n++ 110 | } 111 | return 112 | } 113 | -------------------------------------------------------------------------------- /netpoll/netpoll_stub.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd 2 | 3 | package netpoll 4 | 5 | import "fmt" 6 | 7 | // New always returns an error to indicate that Poller is not implemented for 8 | // current operating system. 9 | func New(*Config) (Poller, error) { 10 | return nil, fmt.Errorf("poller is not supported on this operating system") 11 | } 12 | -------------------------------------------------------------------------------- /netpoll/netpoll_unix_test.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin dragonfly freebsd netbsd openbsd 2 | 3 | package netpoll 4 | 5 | import ( 6 | "bytes" 7 | "io" 8 | "log" 9 | "net" 10 | "os" 11 | "sync" 12 | "sync/atomic" 13 | "syscall" 14 | "testing" 15 | "time" 16 | 17 | "golang.org/x/sys/unix" 18 | ) 19 | 20 | func TestPollerReadOnce(t *testing.T) { 21 | poller, err := New(config(t)) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | r, w, err := socketPair() 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | conn, err := net.FileConn(os.NewFile(uintptr(r), "|0")) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | var ( 37 | data = []byte("hello") 38 | done = make(chan struct{}) 39 | received = make([]byte, 0, len(data)) 40 | ) 41 | 42 | var ( 43 | mu sync.Mutex 44 | events []Event 45 | ) 46 | desc := Must(HandleReadOnce(conn)) 47 | err = poller.Start(desc, func(event Event) { 48 | mu.Lock() 49 | events = append(events, event) 50 | mu.Unlock() 51 | 52 | if event&EventRead == 0 { 53 | return 54 | } 55 | 56 | bts := make([]byte, 128) 57 | n, err := conn.Read(bts) 58 | switch { 59 | case err == io.EOF: 60 | defer close(done) 61 | if err = poller.Stop(desc); err != nil { 62 | t.Fatalf("poller.Stop() error: %v", err) 63 | } 64 | 65 | case err != nil: 66 | t.Fatal(err) 67 | 68 | default: 69 | received = append(received, bts[:n]...) 70 | if err := poller.Resume(desc); err != nil { 71 | t.Fatalf("poller.Resume() error: %v", err) 72 | } 73 | } 74 | }) 75 | if err != nil { 76 | t.Fatal(err) 77 | } 78 | 79 | // Write data by a single byte. 80 | for i := 0; i < len(data); i++ { 81 | _, err = unix.Write(w, data[i:i+1]) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | time.Sleep(time.Millisecond) 86 | } 87 | 88 | if err = unix.Close(w); err != nil { 89 | t.Fatalf("unix.Close() error: %v", err) 90 | } 91 | 92 | <-done 93 | 94 | if count, exp := len(events), len(data)+1; count != exp { // expect +1 for EPOLLRDHUP or EPOLLHUP 95 | t.Errorf("callback called %d times (%v); want %d", count, events, exp) 96 | return 97 | } 98 | 99 | if last, want := events[len(events)-1], EventRead|EventHup|EventReadHup; last != want { 100 | t.Errorf("last callback call was made with %s; want %s", last, want) 101 | } 102 | for i, m := range events[:len(events)-1] { 103 | if m != EventRead { 104 | t.Errorf("callback call #%d was made with %s; want %s", i, m, EventRead) 105 | } 106 | } 107 | if !bytes.Equal(data, received) { 108 | t.Errorf("bytes are not equal:\ngot: %v\nwant: %v\n", received, data) 109 | } 110 | } 111 | 112 | func TestPollerWriteOnce(t *testing.T) { 113 | poller, err := New(config(t)) 114 | if err != nil { 115 | t.Fatal(err) 116 | } 117 | 118 | r, w, err := socketPair() 119 | if err != nil { 120 | t.Fatal(err) 121 | } 122 | 123 | sendLoWat, err := unix.GetsockoptInt(w, unix.SOL_SOCKET, unix.SO_SNDLOWAT) 124 | if err != nil { 125 | t.Fatalf("get SO_SNDLOWAT error: %v", err) 126 | } 127 | sendBuf, err := unix.GetsockoptInt(w, unix.SOL_SOCKET, unix.SO_SNDBUF) 128 | if err != nil { 129 | t.Fatalf("get SO_SNDBUF error: %v", err) 130 | } 131 | 132 | log.Printf("send buf is %d", sendBuf) 133 | log.Printf("send low watermark is %d", sendLoWat) 134 | 135 | filled, err := fillSendBuffer(w) 136 | if err != nil { 137 | t.Fatalf("fill send buffer error: %v", err) 138 | } 139 | log.Printf("filled send buffer: %d", filled) 140 | 141 | var ( 142 | writeEvents = new(uint32) 143 | writeHup = new(uint32) 144 | ) 145 | 146 | wc, err := net.FileConn(os.NewFile(uintptr(w), "w")) 147 | if err != nil { 148 | t.Fatal(err) 149 | } 150 | 151 | desc := Must(HandleWriteOnce(wc)) 152 | err = poller.Start(desc, func(e Event) { 153 | log.Printf("received event from poller: %s", e) 154 | atomic.AddUint32(writeEvents, 1) 155 | 156 | if e&EventHup != 0 { 157 | atomic.AddUint32(writeHup, 1) 158 | return 159 | } 160 | if e&EventErr != 0 { 161 | return 162 | } 163 | 164 | filled, err := fillSendBuffer(w) 165 | if err != nil { 166 | t.Fatalf("fill send buffer error: %v", err) 167 | } 168 | log.Printf("filled send buffer: %d", filled) 169 | 170 | if err := poller.Resume(desc); err != nil { 171 | t.Fatalf("poller.Resume() error %v", err) 172 | } 173 | }) 174 | if err != nil { 175 | t.Fatalf("poller.Start(w) error: %v", err) 176 | } 177 | 178 | // Read sendLoWat-1 bytes such that next read will trigger write event. 179 | if n, err := unix.Read(r, make([]byte, sendLoWat-1)); err != nil { 180 | t.Fatalf("unix.Read() error: %v", err) 181 | } else { 182 | log.Printf("read %d (from %d: SNDLOWAT-1)", n, sendLoWat-1) 183 | } 184 | 185 | time.Sleep(time.Millisecond * 50) 186 | if n := atomic.LoadUint32(writeEvents); n > 0 { 187 | t.Fatalf("write events: %v; want 0", n) 188 | } 189 | 190 | if n, err := unix.Read(r, make([]byte, filled)); err != nil { 191 | t.Fatalf("empty receive buffer error: %v", err) 192 | } else { 193 | log.Printf("emptied receive buffer: %d", n) 194 | } 195 | time.Sleep(time.Millisecond * 50) 196 | if n := atomic.LoadUint32(writeEvents); n != 1 { 197 | t.Errorf("write events: %v; want 1", n) 198 | } 199 | 200 | if err := unix.Close(r); err != nil { 201 | t.Fatalf("unix.Close() error: %v", err) 202 | } 203 | log.Println("closed read-end of pair") 204 | time.Sleep(time.Millisecond * 50) 205 | if n := atomic.LoadUint32(writeHup); n != 1 { 206 | t.Errorf("hup events: %v; want 1", n) 207 | } 208 | } 209 | 210 | func emptyRecvBuffer(fd int, k int) (n int, err error) { 211 | for eagain := 0; eagain < 10; { 212 | var x int 213 | p := make([]byte, k+1) 214 | x, err = unix.Read(fd, p) 215 | if err != nil { 216 | if err == syscall.EAGAIN { 217 | err = nil 218 | eagain++ 219 | continue 220 | } 221 | return 222 | } 223 | n += x 224 | } 225 | return 226 | } 227 | 228 | func fillSendBuffer(fd int) (n int, err error) { 229 | p := bytes.Repeat([]byte{'x'}, 1) 230 | for eagain := 0; eagain < 10; { 231 | var x int 232 | x, err = unix.Write(fd, p) 233 | if err != nil { 234 | if err == syscall.EAGAIN { 235 | err = nil 236 | eagain++ 237 | continue 238 | } 239 | return 240 | } 241 | n += x 242 | } 243 | return 244 | } 245 | 246 | func socketPair() (r, w int, err error) { 247 | fd, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0) 248 | if err != nil { 249 | return 250 | } 251 | 252 | if err = unix.SetNonblock(fd[0], true); err != nil { 253 | return 254 | } 255 | if err = unix.SetNonblock(fd[1], true); err != nil { 256 | return 257 | } 258 | 259 | buf := 4096 260 | if err = unix.SetsockoptInt(fd[0], unix.SOL_SOCKET, unix.SO_SNDBUF, buf); err != nil { 261 | return 262 | } 263 | if err = unix.SetsockoptInt(fd[1], unix.SOL_SOCKET, unix.SO_SNDBUF, buf); err != nil { 264 | return 265 | } 266 | if err = unix.SetsockoptInt(fd[0], unix.SOL_SOCKET, unix.SO_RCVBUF, buf); err != nil { 267 | return 268 | } 269 | if err = unix.SetsockoptInt(fd[1], unix.SOL_SOCKET, unix.SO_RCVBUF, buf); err != nil { 270 | return 271 | } 272 | return fd[0], fd[1], nil 273 | } 274 | 275 | func config(tb testing.TB) *Config { 276 | return &Config{ 277 | OnWaitError: func(err error) { 278 | tb.Fatal(err) 279 | }, 280 | } 281 | } 282 | 283 | type stubConn struct{} 284 | 285 | func (s stubConn) Read(b []byte) (n int, err error) { return 0, nil } 286 | func (s stubConn) Write(b []byte) (n int, err error) { return 0, nil } 287 | func (s stubConn) Close() error { return nil } 288 | func (s stubConn) LocalAddr() (addr net.Addr) { return } 289 | func (s stubConn) RemoteAddr() (addr net.Addr) { return } 290 | func (s stubConn) SetDeadline(t time.Time) error { return nil } 291 | func (s stubConn) SetReadDeadline(t time.Time) error { return nil } 292 | func (s stubConn) SetWriteDeadline(t time.Time) error { return nil } 293 | -------------------------------------------------------------------------------- /netpoll/util.go: -------------------------------------------------------------------------------- 1 | package netpoll 2 | 3 | import "syscall" 4 | 5 | func temporaryErr(err error) bool { 6 | errno, ok := err.(syscall.Errno) 7 | if !ok { 8 | return false 9 | } 10 | return errno.Temporary() 11 | } 12 | --------------------------------------------------------------------------------