v / vlib / picoev / loop_linux.c.v
144 lines · 121 sloc · 3.14 KB · a87a4d73b9ab25cfff0822f4e94cf2a2d9e64323
Raw
1module picoev
2
3#include <sys/epoll.h>
4
5$if !musl ? {
6 #include <sys/cdefs.h> // needed for cross compiling to linux
7}
8
9fn C.epoll_create(__flags i32) i32
10fn C.epoll_wait(__epfd i32, __events &C.epoll_event, __maxevents i32, __timeout i32) i32
11fn C.epoll_ctl(__epfd i32, __op i32, __fd i32, __event &C.epoll_event) i32
12
13@[typedef]
14union C.epoll_data {
15mut:
16 ptr voidptr
17 fd int
18 u32 u32
19 u64 u64
20}
21
22@[packed]
23pub struct C.epoll_event {
24 events u32
25 data C.epoll_data
26}
27
28@[heap]
29pub struct EpollLoop {
30mut:
31 id int
32 epoll_fd int
33 events [1024]C.epoll_event
34 now i64
35}
36
37type LoopType = EpollLoop
38
39// create_epoll_loop creates a new epoll instance and returns an `EpollLoop` struct with `id`.
40pub fn create_epoll_loop(id int) !&EpollLoop {
41 mut loop := &EpollLoop{
42 id: id
43 }
44
45 loop.epoll_fd = C.epoll_create(max_fds)
46 if loop.epoll_fd == -1 {
47 return error('could not create epoll loop!')
48 }
49
50 return loop
51}
52
53// updates the events associated with a file descriptor in the event loop.
54@[direct_array_access]
55fn (mut pv Picoev) update_events(fd int, events int) int {
56 // check if fd is in range
57 assert fd < max_fds
58
59 mut target := pv.file_descriptors[fd]
60 mut ev := C.epoll_event{}
61
62 // fd belongs to loop
63 if events & picoev_del != target.events && target.loop_id != pv.loop.id {
64 return -1
65 }
66
67 if events & picoev_readwrite == target.events {
68 return 0
69 }
70
71 // vfmt off
72 ev.events = u32(
73 (if events & picoev_read != 0 { C.EPOLLIN } else { 0 })
74 |
75 (if events & picoev_write != 0 { C.EPOLLOUT } else { 0 })
76 )
77 // vfmt on
78 ev.data.fd = fd
79
80 if events & picoev_del != 0 {
81 // nothing to do
82 } else if events & picoev_readwrite == 0 {
83 // delete the file if it exists
84 epoll_ret := C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_DEL, fd, &ev)
85
86 // check error
87 assert epoll_ret == 0
88 } else {
89 // change settings to 0
90 mut epoll_ret := C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_MOD, fd, &ev)
91 if epoll_ret != 0 {
92 // if the file is not present we want to add it
93 assert C.errno == C.ENOENT
94 epoll_ret = C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_ADD, fd, &ev)
95
96 // check error
97 assert epoll_ret == 0
98 }
99 }
100
101 // convert to u32?
102 target.events = u32(events)
103 return 0
104}
105
106// performs a single iteration of the select-based event loop.
107@[direct_array_access]
108fn (mut pv Picoev) poll_once(max_wait_in_sec int) int {
109 nevents := C.epoll_wait(pv.loop.epoll_fd, &pv.loop.events[0], max_fds, max_wait_in_sec * 1000)
110
111 if nevents == -1 {
112 // timeout has occurred
113 return -1
114 }
115
116 for i := 0; i < nevents; i++ {
117 mut event := pv.loop.events[i]
118 target := unsafe { pv.file_descriptors[event.data.fd] }
119 unsafe {
120 assert event.data.fd < max_fds
121 }
122 if pv.loop.id == target.loop_id && target.events & picoev_readwrite != 0 {
123 mut read_events := 0
124 if event.events & u32(C.EPOLLIN) != 0 {
125 read_events |= picoev_read
126 }
127 if event.events & u32(C.EPOLLOUT) != 0 {
128 read_events |= picoev_write
129 }
130
131 if read_events != 0 {
132 // do callback!
133 unsafe { target.cb(event.data.fd, read_events, &pv) }
134 }
135 } else {
136 // defer epoll delete
137 event.events = 0
138 unsafe {
139 C.epoll_ctl(pv.loop.epoll_fd, C.EPOLL_CTL_DEL, event.data.fd, &event)
140 }
141 }
142 }
143 return 0
144}
145