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