Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief I/O EV binding.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 : #include <stdlib.h>
10 :
11 : #include <ev.h>
12 :
13 : #include <kdbassert.h>
14 : #include <kdbhelper.h>
15 : #include <kdbio.h>
16 : #include <kdblogger.h>
17 :
18 : typedef struct ev_loop ev_loop_t;
19 :
20 : /**
21 : * Container for required additional information for
22 : * I/O binding operations & libev Handles
23 : */
24 : typedef struct EvBindingData
25 : {
26 : union
27 : {
28 : ElektraIoFdOperation * fd;
29 : ElektraIoTimerOperation * timer;
30 : ElektraIoIdleOperation * idle;
31 : } operation;
32 : union
33 : {
34 : ev_io fd;
35 : ev_timer timer;
36 : ev_idle idle;
37 : } handle;
38 : } EvBindingData;
39 :
40 : /**
41 : * Convert I/O flags to libuv event bit mask
42 : * @param flags I/O flags bit mask
43 : * @return libuv events bit mask
44 : */
45 : static int flagsToEvents (int flags)
46 : {
47 51 : int events = 0;
48 51 : if (flags & ELEKTRA_IO_READABLE)
49 : {
50 24 : events |= EV_READ;
51 : }
52 51 : if (flags & ELEKTRA_IO_WRITABLE)
53 : {
54 27 : events |= EV_WRITE;
55 : }
56 51 : return events;
57 : }
58 :
59 : /**
60 : * Convert libuv event bit mask to I/O flags
61 : * @param events libev events bit mask
62 : * @return I/O flags bit mask
63 : */
64 : static int eventsToFlags (int events)
65 : {
66 15 : int flags = 0;
67 15 : if (events & EV_READ)
68 : {
69 6 : flags |= ELEKTRA_IO_READABLE;
70 : }
71 15 : if (events & EV_WRITE)
72 : {
73 9 : flags |= ELEKTRA_IO_WRITABLE;
74 : }
75 15 : return flags;
76 : }
77 :
78 : static EvBindingData * newBindingData (void)
79 : {
80 204 : EvBindingData * bindingData = elektraCalloc (sizeof (*bindingData));
81 102 : if (bindingData == NULL)
82 : {
83 : ELEKTRA_LOG_WARNING ("elektraCalloc failed");
84 : return NULL;
85 : }
86 :
87 : return bindingData;
88 : }
89 :
90 : /**
91 : * Converts the events bit mask and calls the associated callback
92 : * Called by libuv whenever a file descriptor status has changed.
93 : *
94 : * @param handle libuv poll handle
95 : * @param events events bit mask
96 : */
97 15 : static void ioEvBindingFdCallback (ev_loop_t * loop ELEKTRA_UNUSED, ev_io * handle, int revents ELEKTRA_UNUSED)
98 : {
99 15 : ELEKTRA_NOT_NULL (handle->data);
100 15 : EvBindingData * bindingData = (EvBindingData *) handle->data;
101 15 : ElektraIoFdOperation * fdOp = (ElektraIoFdOperation *) bindingData->operation.fd;
102 :
103 30 : elektraIoFdGetCallback (fdOp) (fdOp, eventsToFlags (revents));
104 15 : }
105 :
106 : /**
107 : * Calls the associated callback.
108 : * Called by libuv whenever a timer has elapsed.
109 : *
110 : * @param handle libuv timer handle
111 : */
112 105 : static void ioEvBindingTimerCallback (ev_loop_t * loop ELEKTRA_UNUSED, ev_timer * handle, int revents ELEKTRA_UNUSED)
113 : {
114 105 : ELEKTRA_NOT_NULL (handle->data);
115 105 : EvBindingData * bindingData = (EvBindingData *) handle->data;
116 105 : ElektraIoTimerOperation * timerOp = (ElektraIoTimerOperation *) bindingData->operation.timer;
117 :
118 105 : elektraIoTimerGetCallback (timerOp) (timerOp);
119 105 : }
120 :
121 : /**
122 : * Calls the associated callback.
123 : * Called by libuv whenever an idle operation can perform its operations.
124 : *
125 : * @param handle libuv idle handle
126 : */
127 1731273 : static void ioEvBindingIdleCallback (ev_loop_t * loop ELEKTRA_UNUSED, ev_idle * handle, int revents ELEKTRA_UNUSED)
128 : {
129 1731273 : ELEKTRA_NOT_NULL (handle->data);
130 1731273 : EvBindingData * bindingData = (EvBindingData *) handle->data;
131 1731273 : ElektraIoIdleOperation * idleOp = (ElektraIoIdleOperation *) bindingData->operation.idle;
132 :
133 1731273 : elektraIoIdleGetCallback (idleOp) (idleOp);
134 1731273 : }
135 :
136 : /**
137 : * Update information about a file descriptor watched by I/O binding.
138 : * @see kdbio.h ::ElektraIoBindingUpdateFd
139 : */
140 27 : static int ioEvBindingUpdateFd (ElektraIoFdOperation * fdOp)
141 : {
142 27 : EvBindingData * bindingData = (EvBindingData *) elektraIoFdGetBindingData (fdOp);
143 27 : ELEKTRA_NOT_NULL (bindingData);
144 27 : ev_loop_t * loop = elektraIoBindingGetData (elektraIoFdGetBinding (fdOp));
145 27 : ev_io * fd = &bindingData->handle.fd;
146 27 : if (elektraIoFdIsEnabled (fdOp))
147 : {
148 27 : ev_io_stop (loop, fd);
149 27 : ev_io_set (fd, elektraIoFdGetFd (fdOp), flagsToEvents (elektraIoFdGetFlags (fdOp)));
150 27 : ev_io_start (loop, fd);
151 : }
152 : else
153 : {
154 0 : ev_io_stop (loop, fd);
155 : }
156 27 : return 1;
157 : }
158 :
159 : /**
160 : * Add file descriptor to I/O binding
161 : * @see kdbio.h ::ElektraIoBindingAddFd
162 : */
163 24 : static int ioEvBindingAddFd (ElektraIoInterface * binding ELEKTRA_UNUSED, ElektraIoFdOperation * fdOp)
164 : {
165 24 : EvBindingData * bindingData = newBindingData ();
166 24 : if (bindingData == NULL)
167 : {
168 : return 0;
169 : }
170 24 : elektraIoFdSetBindingData (fdOp, bindingData);
171 24 : bindingData->operation.fd = fdOp;
172 24 : bindingData->handle.fd.data = bindingData;
173 :
174 24 : ev_io * fd = &bindingData->handle.fd;
175 24 : ev_io_init (fd, ioEvBindingFdCallback, elektraIoFdGetFd (fdOp), flagsToEvents (elektraIoFdGetFlags (fdOp)));
176 :
177 : // Start polling if enabled
178 24 : if (elektraIoFdIsEnabled (fdOp))
179 : {
180 18 : ioEvBindingUpdateFd (fdOp);
181 : }
182 :
183 : return 1;
184 : }
185 :
186 : /**
187 : * Remove file descriptor from I/O binding.
188 : * @see kdbio.h ::ElektraIoBindingRemoveFd
189 : */
190 24 : static int ioEvBindingRemoveFd (ElektraIoFdOperation * fdOp)
191 : {
192 24 : EvBindingData * bindingData = (EvBindingData *) elektraIoFdGetBindingData (fdOp);
193 24 : ELEKTRA_NOT_NULL (bindingData);
194 24 : ev_loop_t * loop = elektraIoBindingGetData (elektraIoFdGetBinding (fdOp));
195 24 : ev_io * fd = &bindingData->handle.fd;
196 24 : ev_io_stop (loop, fd);
197 24 : elektraFree (bindingData);
198 24 : return 1;
199 : }
200 :
201 : /**
202 : * Update timer in I/O binding.
203 : * @see kdbio.h ::ElektraIoBindingUpdateTimer
204 : */
205 57 : static int ioEvBindingUpdateTimer (ElektraIoTimerOperation * timerOp)
206 : {
207 57 : EvBindingData * bindingData = (EvBindingData *) elektraIoTimerGetBindingData (timerOp);
208 57 : ELEKTRA_NOT_NULL (bindingData);
209 57 : ev_loop_t * loop = elektraIoBindingGetData (elektraIoTimerGetBinding (timerOp));
210 57 : ev_timer * timer = &bindingData->handle.timer;
211 57 : if (elektraIoTimerIsEnabled (timerOp))
212 : {
213 54 : double interval = elektraIoTimerGetInterval (timerOp) / 1000.0;
214 54 : ev_timer_set (timer, interval, interval);
215 54 : ev_timer_start (loop, timer);
216 : }
217 : else
218 : {
219 3 : ev_timer_stop (loop, timer);
220 : }
221 57 : return 1;
222 : }
223 :
224 : /**
225 : * Add timer for I/O binding.
226 : * @see kdbio.h ::ElektraIoBindingAddTimer
227 : */
228 51 : static int ioEvBindingAddTimer (ElektraIoInterface * binding ELEKTRA_UNUSED, ElektraIoTimerOperation * timerOp)
229 : {
230 51 : EvBindingData * bindingData = newBindingData ();
231 51 : if (bindingData == NULL)
232 : {
233 : return 0;
234 : }
235 51 : elektraIoTimerSetBindingData (timerOp, bindingData);
236 51 : bindingData->operation.timer = timerOp;
237 51 : bindingData->handle.timer.data = bindingData;
238 :
239 51 : ev_timer * timer = &bindingData->handle.timer;
240 51 : ev_timer_init (timer, ioEvBindingTimerCallback, 0, 0);
241 :
242 : // Start timer if enabled
243 51 : if (elektraIoTimerIsEnabled (timerOp))
244 : {
245 48 : ioEvBindingUpdateTimer (timerOp);
246 : }
247 :
248 : return 1;
249 : }
250 :
251 : /**
252 : * Remove timer from I/O binding
253 : * @see kdbio.h ::ElektraIoBindingRemoveTimer
254 : */
255 51 : static int ioEvBindingRemoveTimer (ElektraIoTimerOperation * timerOp)
256 : {
257 51 : EvBindingData * bindingData = (EvBindingData *) elektraIoTimerGetBindingData (timerOp);
258 51 : ELEKTRA_NOT_NULL (bindingData);
259 51 : ev_loop_t * loop = elektraIoBindingGetData (elektraIoTimerGetBinding (timerOp));
260 51 : ev_timer * timer = &bindingData->handle.timer;
261 51 : ev_timer_stop (loop, timer);
262 51 : elektraFree (bindingData);
263 51 : return 1;
264 : }
265 :
266 : /**
267 : * Update idle operation in I/O binding
268 : * @see kdbio.h ::ElektraIoBindingUpdateIdle
269 : */
270 33 : static int ioEvBindingUpdateIdle (ElektraIoIdleOperation * idleOp)
271 : {
272 33 : EvBindingData * bindingData = elektraIoIdleGetBindingData (idleOp);
273 33 : ELEKTRA_NOT_NULL (bindingData);
274 33 : ev_loop_t * loop = elektraIoBindingGetData (elektraIoIdleGetBinding (idleOp));
275 33 : ev_idle * idle = &bindingData->handle.idle;
276 33 : if (elektraIoIdleIsEnabled (idleOp))
277 : {
278 27 : ev_idle_start (loop, idle);
279 : }
280 : else
281 : {
282 6 : ev_idle_stop (loop, idle);
283 : }
284 33 : return 1;
285 : }
286 :
287 : /**
288 : * Add idle operation to I/O binding
289 : * @see kdbio.h ::ElektraIoBindingAddIdle
290 : */
291 27 : static int ioEvBindingAddIdle (ElektraIoInterface * binding ELEKTRA_UNUSED, ElektraIoIdleOperation * idleOp)
292 : {
293 27 : EvBindingData * bindingData = newBindingData ();
294 27 : if (bindingData == NULL)
295 : {
296 : return 0;
297 : }
298 27 : elektraIoIdleSetBindingData (idleOp, bindingData);
299 27 : bindingData->operation.idle = idleOp;
300 27 : bindingData->handle.idle.data = bindingData;
301 :
302 27 : ev_idle * idle = &bindingData->handle.idle;
303 27 : ev_idle_init (idle, ioEvBindingIdleCallback);
304 :
305 : // Add idle to loop if enabled
306 27 : if (elektraIoIdleIsEnabled (idleOp))
307 : {
308 24 : ioEvBindingUpdateIdle (idleOp);
309 : }
310 :
311 : return 1;
312 : }
313 :
314 : /**
315 : * Remove idle operation from I/O binding
316 : * @see kdbio.h ::ElektraIoBindingRemoveIdle
317 : */
318 27 : static int ioEvBindingRemoveIdle (ElektraIoIdleOperation * idleOp)
319 : {
320 27 : EvBindingData * bindingData = elektraIoIdleGetBindingData (idleOp);
321 27 : ELEKTRA_NOT_NULL (bindingData);
322 27 : ev_loop_t * loop = elektraIoBindingGetData (elektraIoIdleGetBinding (idleOp));
323 27 : ev_idle * idle = &bindingData->handle.idle;
324 27 : ev_idle_stop (loop, idle);
325 27 : elektraFree (bindingData);
326 27 : return 1;
327 : }
328 :
329 : /**
330 : * Cleanup
331 : * @param binding I/O binding
332 : * @see kdbio.h ::ElektraIoBindingCleanup
333 : */
334 57 : static int ioEvBindingCleanup (ElektraIoInterface * binding)
335 : {
336 57 : ELEKTRA_NOT_NULL (binding);
337 57 : elektraFree (binding);
338 57 : return 1;
339 : }
340 :
341 : /**
342 : * Create and initialize a new I/O binding.
343 : * @param loop Loop to use for I/O operations
344 : * @return Populated I/O interface
345 : */
346 57 : ElektraIoInterface * elektraIoEvNew (struct ev_loop * loop)
347 : {
348 57 : if (loop == NULL)
349 : {
350 : ELEKTRA_LOG_WARNING ("loop was NULL");
351 : return NULL;
352 : }
353 : // Initialize I/O interface
354 57 : ElektraIoInterface * binding = elektraIoNewBinding (
355 : // file descriptors
356 : ioEvBindingAddFd, ioEvBindingUpdateFd, ioEvBindingRemoveFd,
357 : // timers
358 : ioEvBindingAddTimer, ioEvBindingUpdateTimer, ioEvBindingRemoveTimer,
359 : // idle
360 : ioEvBindingAddIdle, ioEvBindingUpdateIdle, ioEvBindingRemoveIdle,
361 : // cleanup
362 : ioEvBindingCleanup);
363 57 : if (binding == NULL)
364 : {
365 : ELEKTRA_LOG_WARNING ("elektraIoNewBinding failed");
366 : return NULL;
367 : }
368 :
369 : // Save the libuv loop we are using
370 57 : elektraIoBindingSetData (binding, loop);
371 :
372 57 : return binding;
373 : }
|