v / vlib / picoev / loop_freebsd.c.v
203 lines · 171 sloc · 4.69 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1module picoev
2
3#include <errno.h>
4#include <sys/types.h>
5#include <sys/event.h>
6
7fn C.kevent(i32, changelist voidptr, nchanges i32, eventlist voidptr, nevents i32, timeout &C.timespec) i32
8fn C.kqueue() i32
9fn C.EV_SET(kev voidptr, ident i32, filter i16, flags u16, fflags u32, data voidptr, udata voidptr)
10
11pub struct C.kevent {
12pub mut:
13 ident int
14 // uintptr_t
15 filter i16
16 flags u16
17 fflags u32
18 data voidptr
19 // intptr_t
20 udata voidptr
21}
22
23@[heap]
24pub struct KqueueLoop {
25mut:
26 id int
27 now i64
28 kq_id int
29 // -1 if not changed
30 changed_fds int
31 events [1024]C.kevent
32 changelist [256]C.kevent
33}
34
35type LoopType = KqueueLoop
36
37// create_kqueue_loop creates a new kernel event queue with loop_id=`id`.
38pub fn create_kqueue_loop(id int) !&KqueueLoop {
39 mut loop := &KqueueLoop{
40 id: id
41 }
42
43 loop.kq_id = C.kqueue()
44 if loop.kq_id == -1 {
45 return error('could not create kqueue loop!')
46 }
47 loop.changed_fds = -1
48 return loop
49}
50
51// ev_set sets a new `kevent` with file descriptor `index`.
52@[inline]
53pub fn (mut pv Picoev) ev_set(index int, operation int, events int) {
54 mut filter := 0
55 if events & picoev_read != 0 {
56 filter |= C.EVFILT_READ
57 }
58 if events & picoev_write != 0 {
59 filter |= C.EVFILT_WRITE
60 }
61 filter = i16(filter)
62
63 C.EV_SET(&pv.loop.changelist[index], pv.loop.changed_fds, filter, operation, 0, 0, 0)
64}
65
66// backend_build uses the lower 8 bits to store the old events and the higher 8
67// bits to store the next file descriptor in `Target.backend`.
68@[inline]
69fn backend_build(next_fd int, events u32) int {
70 return int((u32(next_fd) << 8) | (events & 0xff))
71}
72
73// get the lower 8 bits.
74@[inline]
75fn backend_get_old_events(backend int) int {
76 return backend & 0xff
77}
78
79// get the higher 8 bits.
80@[inline]
81fn backend_get_next_fd(backend int) int {
82 return backend >> 8
83}
84
85// apply pending processes all changes for the file descriptors and updates `loop.changelist`
86// if `aplly_all` is `true` the changes are immediately applied.
87fn (mut pv Picoev) apply_pending_changes(apply_all bool) int {
88 mut total, mut nevents := 0, 0
89
90 for pv.loop.changed_fds != -1 {
91 mut target := pv.file_descriptors[pv.loop.changed_fds]
92 old_events := backend_get_old_events(target.backend)
93 if target.events != old_events {
94 // events have been changed
95 if old_events != 0 {
96 pv.ev_set(total, C.EV_DISABLE, old_events)
97 total++
98 }
99 if target.events != 0 {
100 pv.ev_set(total, C.EV_ADD | C.EV_ENABLE, int(target.events))
101 total++
102 }
103 // Apply the changes if the total changes exceed the changelist size
104 if total + 1 >= pv.loop.changelist.len {
105 nevents = C.kevent(pv.loop.kq_id, &pv.loop.changelist, total, C.NULL, 0, C.NULL)
106 assert nevents == 0
107 total = 0
108 }
109 }
110
111 pv.loop.changed_fds = backend_get_next_fd(target.backend)
112 target.backend = -1
113 }
114
115 if apply_all && total != 0 {
116 nevents = C.kevent(pv.loop.kq_id, &pv.loop.changelist, total, C.NULL, 0, C.NULL)
117 assert nevents == 0
118 total = 0
119 }
120
121 return total
122}
123
124@[direct_array_access]
125fn (mut pv Picoev) update_events(fd int, events int) int {
126 // check if fd is in range
127 assert fd < max_fds
128
129 mut target := pv.file_descriptors[fd]
130
131 // initialize if adding the fd
132 if events & picoev_add != 0 {
133 target.backend = -1
134 }
135
136 // return if nothing to do
137 if (events == picoev_del && target.backend == -1)
138 || (events != picoev_del && events & picoev_readwrite == target.events) {
139 return 0
140 }
141
142 // add to changed list if not yet being done
143 if target.backend == -1 {
144 target.backend = backend_build(pv.loop.changed_fds, target.events)
145 pv.loop.changed_fds = fd
146 }
147
148 // update events
149 target.events = u32(events & picoev_readwrite)
150 // apply immediately if is a DELETE
151 if events & picoev_del != 0 {
152 pv.apply_pending_changes(true)
153 }
154
155 return 0
156}
157
158@[direct_array_access]
159fn (mut pv Picoev) poll_once(max_wait_in_sec int) int {
160 ts := C.timespec{
161 tv_sec: max_wait_in_sec
162 tv_nsec: 0
163 }
164
165 mut total, mut nevents := 0, 0
166 // apply changes later when the callback is called.
167 total = pv.apply_pending_changes(false)
168
169 nevents = C.kevent(pv.loop.kq_id, &pv.loop.changelist, total, &pv.loop.events,
170 pv.loop.events.len, &ts)
171 if nevents == -1 {
172 // the errors we can only rescue
173 assert C.errno == C.EACCES || C.errno == C.EFAULT || C.errno == C.EINTR
174 return -1
175 }
176
177 for i := 0; i < nevents; i++ {
178 event := pv.loop.events[i]
179 target := pv.file_descriptors[event.ident]
180
181 // changelist errors are fatal
182 assert event.flags & C.EV_ERROR == 0
183
184 if pv.loop.id == target.loop_id && event.filter & (C.EVFILT_READ | C.EVFILT_WRITE) != 0 {
185 read_events := match int(event.filter) {
186 C.EVFILT_READ {
187 picoev_read
188 }
189 C.EVFILT_WRITE {
190 picoev_write
191 }
192 else {
193 0
194 }
195 }
196
197 // do callback!
198 unsafe { target.cb(target.fd, read_events, &pv) }
199 }
200 }
201
202 return 0
203}
204