v2 / vlib / os / notify / backend_linux.c.v
202 lines · 180 sloc · 4.89 KB · a87a4d73b9ab25cfff0822f4e94cf2a2d9e64323
Raw
1module notify
2
3import time
4import os
5
6#include <sys/epoll.h>
7
8pub struct C.epoll_event {
9 events u32
10 data C.epoll_data_t
11}
12
13@[typedef]
14union C.epoll_data_t {
15 ptr voidptr
16 fd int
17 u32 u32
18 u64 u64
19}
20
21fn C.epoll_create1(i32) i32
22
23fn C.epoll_ctl(i32, i32, i32, &C.epoll_event) i32
24
25fn C.epoll_wait(i32, &C.epoll_event, i32, i32) i32
26
27// EpollNotifier provides methods that implement FdNotifier using the
28// epoll I/O event notification facility (linux only)
29struct EpollNotifier {
30 epoll_fd int
31}
32
33// EpollEvent describes an event that occurred for a file descriptor in
34// the watch list
35struct EpollEvent {
36pub:
37 fd int
38 kind FdEventType
39}
40
41// new creates a new EpollNotifier.
42// The FdNotifier interface is returned to allow OS specific
43// implementations without exposing the concrete type
44pub fn new() !FdNotifier {
45 fd := C.epoll_create1(0) // 0 indicates default behavior
46 if fd == -1 {
47 return error(os.posix_get_error_msg(C.errno))
48 }
49 // Needed to circumvent V limitations
50 x := &EpollNotifier{
51 epoll_fd: fd
52 }
53 return x
54}
55
56const epoll_read = u32(C.EPOLLIN)
57const epoll_write = u32(C.EPOLLOUT)
58const epoll_peer_hangup = u32(C.EPOLLRDHUP)
59const epoll_exception = u32(C.EPOLLPRI)
60const epoll_error = u32(C.EPOLLERR)
61const epoll_hangup = u32(C.EPOLLHUP)
62const epoll_edge_trigger = u32(C.EPOLLET)
63const epoll_one_shot = u32(C.EPOLLONESHOT)
64const epoll_wake_up = u32(C.EPOLLWAKEUP)
65const epoll_exclusive = u32(C.EPOLLEXCLUSIVE)
66
67// ctl is a helper method for add, modify, and remove
68fn (mut en EpollNotifier) ctl(fd int, op int, mask u32) ! {
69 event := C.epoll_event{
70 events: mask
71 data: C.epoll_data_t{
72 fd: fd
73 }
74 }
75 if C.epoll_ctl(en.epoll_fd, op, fd, &event) == -1 {
76 return error(os.posix_get_error_msg(C.errno))
77 }
78}
79
80// add adds a file descriptor to the watch list
81fn (mut en EpollNotifier) add(fd int, events FdEventType, conf ...FdConfigFlags) ! {
82 mask := flags_to_mask(events, ...conf)
83 en.ctl(fd, C.EPOLL_CTL_ADD, mask)!
84}
85
86// modify sets an existing entry in the watch list to the provided events and configuration
87fn (mut en EpollNotifier) modify(fd int, events FdEventType, conf ...FdConfigFlags) ! {
88 mask := flags_to_mask(events, ...conf)
89 en.ctl(fd, C.EPOLL_CTL_MOD, mask)!
90}
91
92// remove removes a file descriptor from the watch list
93fn (mut en EpollNotifier) remove(fd int) ! {
94 en.ctl(fd, C.EPOLL_CTL_DEL, 0)!
95}
96
97// wait waits to be notified of events on the watch list,
98// returns at most 512 events
99fn (mut en EpollNotifier) wait(timeout time.Duration) []FdEvent {
100 // arbitrary 512 limit; events will round robin on successive
101 // waits if the number exceeds this
102 // NOTE: we use a fixed size array here for stack allocation; this has
103 // the added bonus of making EpollNotifier thread safe
104 events := [512]C.epoll_event{}
105 // populate events with the new events
106 to := timeout.sys_milliseconds()
107 count := C.epoll_wait(en.epoll_fd, &events[0], events.len, to)
108
109 if count > 0 {
110 mut arr := []FdEvent{cap: count}
111 for i := 0; i < count; i++ {
112 fd := unsafe { events[i].data.fd }
113 kind := event_mask_to_flag(events[i].events)
114 if kind.is_empty() {
115 // NOTE: tcc only reports the first event for some
116 // reason, leaving subsequent structs in the array as 0
117 // (or possibly garbage)
118 panic('encountered an empty event kind; this is most likely due to using tcc')
119 }
120 arr << &EpollEvent{
121 fd: fd
122 kind: kind
123 }
124 }
125 return arr
126 }
127 return []
128}
129
130// close closes the EpollNotifier,
131// any successive calls to add, modify, remove, and wait should fail
132fn (mut en EpollNotifier) close() ! {
133 if C.close(en.epoll_fd) == -1 {
134 return error(os.posix_get_error_msg(C.errno))
135 }
136}
137
138// event_mask_to_flag is a helper function that converts a bitmask
139// returned by epoll_wait to FdEventType
140fn event_mask_to_flag(mask u32) FdEventType {
141 mut flags := unsafe { FdEventType(0) }
142
143 if mask & epoll_read != 0 {
144 flags.set(.read)
145 }
146 if mask & epoll_write != 0 {
147 flags.set(.write)
148 }
149 if mask & epoll_peer_hangup != 0 {
150 flags.set(.peer_hangup)
151 }
152 if mask & epoll_exception != 0 {
153 flags.set(.exception)
154 }
155 if mask & epoll_error != 0 {
156 flags.set(.error)
157 }
158 if mask & epoll_hangup != 0 {
159 flags.set(.hangup)
160 }
161
162 return flags
163}
164
165// flags_to_mask is a helper function that converts FdEventType and
166// FdConfigFlags to a bitmask used by the C functions
167fn flags_to_mask(events FdEventType, confs ...FdConfigFlags) u32 {
168 mut mask := u32(0)
169 if events.has(.read) {
170 mask |= epoll_read
171 }
172 if events.has(.write) {
173 mask |= epoll_write
174 }
175 if events.has(.peer_hangup) {
176 mask |= epoll_peer_hangup
177 }
178 if events.has(.exception) {
179 mask |= epoll_exception
180 }
181 if events.has(.error) {
182 mask |= epoll_error
183 }
184 if events.has(.hangup) {
185 mask |= epoll_hangup
186 }
187 for conf in confs {
188 if conf.has(.edge_trigger) {
189 mask |= epoll_edge_trigger
190 }
191 if conf.has(.one_shot) {
192 mask |= epoll_one_shot
193 }
194 if conf.has(.wake_up) {
195 mask |= epoll_wake_up
196 }
197 if conf.has(.exclusive) {
198 mask |= epoll_exclusive
199 }
200 }
201 return mask
202}
203