Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief I/O UV binding.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 : #include <stdlib.h>
10 :
11 : #include <uv.h>
12 :
13 : #include <kdbassert.h>
14 : #include <kdbhelper.h>
15 : #include <kdbio.h>
16 : #include <kdblogger.h>
17 :
18 : /**
19 : * Container for required additional information for
20 : * I/O binding operations & libuv Handles
21 : */
22 : typedef struct UvBindingData
23 : {
24 : union
25 : {
26 : ElektraIoFdOperation * fd;
27 : ElektraIoTimerOperation * timer;
28 : ElektraIoIdleOperation * idle;
29 : } operation;
30 : union
31 : {
32 : uv_poll_t fd;
33 : uv_timer_t timer;
34 : uv_idle_t idle;
35 : } handle;
36 : } UvBindingData;
37 :
38 : /**
39 : * Convert I/O flags to libuv event bit mask
40 : * @param flags I/O flags bit mask
41 : * @return libuv events bit mask
42 : */
43 : static int flagsToEvents (int flags)
44 : {
45 22 : int events = 0;
46 22 : if (flags & ELEKTRA_IO_READABLE)
47 : {
48 12 : events |= UV_READABLE;
49 : }
50 22 : if (flags & ELEKTRA_IO_WRITABLE)
51 : {
52 10 : events |= UV_WRITABLE;
53 : }
54 : return events;
55 : }
56 :
57 : /**
58 : * Convert libuv event bit mask to I/O flags
59 : * @param events libuv events bit mask
60 : * @return I/O flags bit mask
61 : */
62 : static int eventsToFlags (int events)
63 : {
64 16 : int flags = 0;
65 16 : if (events & UV_READABLE)
66 : {
67 10 : flags |= ELEKTRA_IO_READABLE;
68 : }
69 16 : if (events & UV_WRITABLE)
70 : {
71 6 : flags |= ELEKTRA_IO_WRITABLE;
72 : }
73 : return flags;
74 : }
75 :
76 : /**
77 : * @internal
78 : * Create new data structure for binding operations.
79 : *
80 : * @return new data structure
81 : */
82 : static UvBindingData * newBindingData (void)
83 : {
84 80 : UvBindingData * bindingData = elektraCalloc (sizeof (*bindingData));
85 80 : if (bindingData == NULL)
86 : {
87 : ELEKTRA_LOG_WARNING ("elektraCalloc failed");
88 : return NULL;
89 : }
90 :
91 : return bindingData;
92 : }
93 :
94 : /**
95 : * @internal
96 : * Get error message from libuv.
97 : *
98 : * Function is only used if logging is enabled.
99 : *
100 : * @param result return von libuv operation
101 : * @param loop loop
102 : * @return error message
103 : */
104 : static ELEKTRA_UNUSED const char * ioUvBindingGetError (int result ELEKTRA_UNUSED, uv_loop_t * loop ELEKTRA_UNUSED)
105 : {
106 : #ifdef HAVE_LIBUV0
107 : return uv_strerror (uv_last_error (loop));
108 : #else
109 : return uv_strerror (result);
110 : #endif
111 : }
112 :
113 : /**
114 : * Free allocated memory after a handle has been closed.
115 : * Called by libuv.
116 : *
117 : * @param handle any libuv handle type (e.g. poll, timer, idle)
118 : */
119 80 : static void ioUvBindingHandleClosedCallback (uv_handle_t * handle)
120 : {
121 80 : ELEKTRA_NOT_NULL (handle->data);
122 80 : UvBindingData * bindingData = (UvBindingData *) handle->data;
123 :
124 80 : elektraFree (bindingData);
125 80 : }
126 :
127 : /**
128 : * Converts the events bit mask and calls the associated callback
129 : * Called by libuv whenever a file descriptor status has changed.
130 : *
131 : * @param handle libuv poll handle
132 : * @param status 0 means no error
133 : * @param events events bit mask
134 : */
135 16 : static void ioUvBindingFdCallback (uv_poll_t * handle, int status, int events)
136 : {
137 16 : if (status != 0)
138 : {
139 : return;
140 : }
141 :
142 16 : ELEKTRA_NOT_NULL (handle->data);
143 16 : UvBindingData * bindingData = (UvBindingData *) handle->data;
144 16 : ElektraIoFdOperation * fdOp = (ElektraIoFdOperation *) bindingData->operation.fd;
145 :
146 32 : elektraIoFdGetCallback (fdOp) (fdOp, eventsToFlags (events));
147 : }
148 :
149 : /**
150 : * Calls the associated callback.
151 : * Called by libuv whenever a timer has elapsed.
152 : *
153 : * @param handle libuv timer handle
154 : */
155 : #ifdef HAVE_LIBUV0
156 : static void ioUvBindingTimerCallback (uv_timer_t * handle, int unknown ELEKTRA_UNUSED)
157 : #else
158 78 : static void ioUvBindingTimerCallback (uv_timer_t * handle)
159 : #endif
160 : {
161 78 : ELEKTRA_NOT_NULL (handle->data);
162 78 : UvBindingData * bindingData = (UvBindingData *) handle->data;
163 78 : ElektraIoTimerOperation * timerOp = (ElektraIoTimerOperation *) bindingData->operation.timer;
164 :
165 78 : elektraIoTimerGetCallback (timerOp) (timerOp);
166 78 : }
167 :
168 : /**
169 : * Calls the associated callback.
170 : * Called by libuv whenever an idle operation can perform its operations.
171 : *
172 : * @param handle libuv idle handle
173 : */
174 : #ifdef HAVE_LIBUV0
175 : static void ioUvBindingIdleCallback (uv_idle_t * handle, int unknown ELEKTRA_UNUSED)
176 : #else
177 80478 : static void ioUvBindingIdleCallback (uv_idle_t * handle)
178 : #endif
179 : {
180 80478 : ELEKTRA_NOT_NULL (handle->data);
181 80478 : UvBindingData * bindingData = (UvBindingData *) handle->data;
182 80478 : ElektraIoIdleOperation * idleOp = (ElektraIoIdleOperation *) bindingData->operation.idle;
183 :
184 80478 : elektraIoIdleGetCallback (idleOp) (idleOp);
185 80478 : }
186 :
187 : /**
188 : * Update information about a file descriptor watched by I/O binding.
189 : * @see kdbio.h ::ElektraIoBindingUpdateFd
190 : */
191 22 : static int ioUvBindingUpdateFd (ElektraIoFdOperation * fdOp)
192 : {
193 22 : ELEKTRA_NOT_NULL (elektraIoFdGetBindingData (fdOp));
194 22 : UvBindingData * bindingData = (UvBindingData *) elektraIoFdGetBindingData (fdOp);
195 :
196 : int result;
197 22 : if (elektraIoFdIsEnabled (fdOp))
198 : {
199 44 : result = uv_poll_start (&bindingData->handle.fd, flagsToEvents (elektraIoFdGetFlags (fdOp)), ioUvBindingFdCallback);
200 : }
201 : else
202 : {
203 0 : result = uv_poll_stop (&bindingData->handle.fd);
204 : }
205 :
206 22 : if (result != 0)
207 : {
208 : ELEKTRA_LOG_WARNING ("could not update poll: %s", ioUvBindingGetError (result, bindingData->handle.fd.loop));
209 : return 0;
210 : }
211 : else
212 : {
213 22 : return 1;
214 : }
215 : }
216 :
217 : /**
218 : * Add file descriptor to I/O binding
219 : * @see kdbio.h ::ElektraIoBindingAddFd
220 : */
221 20 : static int ioUvBindingAddFd (ElektraIoInterface * binding, ElektraIoFdOperation * fdOp)
222 : {
223 20 : UvBindingData * bindingData = newBindingData ();
224 20 : if (bindingData == NULL)
225 : {
226 : return 0;
227 : }
228 20 : uv_loop_t * loop = (uv_loop_t *) elektraIoBindingGetData (binding);
229 20 : ELEKTRA_NOT_NULL (loop);
230 :
231 20 : elektraIoFdSetBindingData (fdOp, bindingData);
232 20 : bindingData->operation.fd = fdOp;
233 20 : bindingData->handle.fd.data = bindingData;
234 :
235 20 : int result = uv_poll_init (loop, &bindingData->handle.fd, elektraIoFdGetFd (fdOp));
236 20 : if (result != 0)
237 : {
238 : ELEKTRA_LOG_WARNING ("could not initialize poll: %s", ioUvBindingGetError (result, loop));
239 : return 0;
240 : }
241 :
242 : // Start polling if enabled
243 20 : if (elektraIoFdIsEnabled (fdOp))
244 : {
245 16 : ioUvBindingUpdateFd (fdOp);
246 : }
247 :
248 : return 1;
249 : }
250 :
251 : /**
252 : * Remove file descriptor from I/O binding.
253 : * @see kdbio.h ::ElektraIoBindingRemoveFd
254 : */
255 20 : static int ioUvBindingRemoveFd (ElektraIoFdOperation * fdOp)
256 : {
257 20 : ELEKTRA_NOT_NULL (elektraIoFdGetBindingData (fdOp));
258 20 : UvBindingData * bindingData = (UvBindingData *) elektraIoFdGetBindingData (fdOp);
259 20 : int result = uv_poll_stop (&bindingData->handle.fd);
260 : if (result != 0)
261 : {
262 : ELEKTRA_LOG_WARNING ("could not stop polling: %s", ioUvBindingGetError (result, bindingData->handle.fd.loop));
263 : }
264 20 : uv_close ((uv_handle_t *) &bindingData->handle.fd, ioUvBindingHandleClosedCallback);
265 20 : return 1;
266 : }
267 :
268 : /**
269 : * Update timer in I/O binding.
270 : * @see kdbio.h ::ElektraIoBindingUpdateTimer
271 : */
272 42 : static int ioUvBindingUpdateTimer (ElektraIoTimerOperation * timerOp)
273 : {
274 42 : ELEKTRA_NOT_NULL (elektraIoTimerGetBindingData (timerOp));
275 42 : UvBindingData * bindingData = (UvBindingData *) elektraIoTimerGetBindingData (timerOp);
276 : int result;
277 42 : if (elektraIoTimerIsEnabled (timerOp))
278 : {
279 40 : unsigned int interval = elektraIoTimerGetInterval (timerOp);
280 40 : result = uv_timer_start (&bindingData->handle.timer, ioUvBindingTimerCallback, interval, interval);
281 : }
282 : else
283 : {
284 2 : result = uv_timer_stop (&bindingData->handle.timer);
285 : }
286 :
287 42 : if (result != 0)
288 : {
289 : ELEKTRA_LOG_WARNING ("could update timer: %s", ioUvBindingGetError (result, bindingData->handle.timer.loop));
290 : return 0;
291 : }
292 : else
293 : {
294 42 : return 1;
295 : }
296 : }
297 :
298 : /**
299 : * Add timer for I/O binding.
300 : * @see kdbio.h ::ElektraIoBindingAddTimer
301 : */
302 38 : static int ioUvBindingAddTimer (ElektraIoInterface * binding, ElektraIoTimerOperation * timerOp)
303 : {
304 38 : UvBindingData * bindingData = newBindingData ();
305 38 : if (bindingData == NULL)
306 : {
307 : return 0;
308 : }
309 38 : uv_loop_t * loop = (uv_loop_t *) elektraIoBindingGetData (binding);
310 38 : ELEKTRA_NOT_NULL (loop);
311 :
312 38 : elektraIoTimerSetBindingData (timerOp, bindingData);
313 38 : bindingData->operation.timer = timerOp;
314 38 : bindingData->handle.timer.data = bindingData;
315 :
316 : int result;
317 38 : result = uv_timer_init (loop, &bindingData->handle.timer);
318 38 : if (result != 0)
319 : {
320 : ELEKTRA_LOG_WARNING ("could not initialize timer: %s", ioUvBindingGetError (result, loop));
321 : return 0;
322 : }
323 :
324 : // Start timer if enabled
325 38 : if (elektraIoTimerIsEnabled (timerOp))
326 : {
327 36 : ioUvBindingUpdateTimer (timerOp);
328 : }
329 :
330 : return 1;
331 : }
332 :
333 : /**
334 : * Remove timer from I/O binding
335 : * @see kdbio.h ::ElektraIoBindingRemoveTimer
336 : */
337 38 : static int ioUvBindingRemoveTimer (ElektraIoTimerOperation * timerOp)
338 : {
339 38 : ELEKTRA_NOT_NULL (elektraIoTimerGetBindingData (timerOp));
340 38 : UvBindingData * bindingData = (UvBindingData *) elektraIoTimerGetBindingData (timerOp);
341 38 : int result = uv_timer_stop (&bindingData->handle.timer);
342 : if (result != 0)
343 : {
344 : ELEKTRA_LOG_WARNING ("could not stop timer: %s", ioUvBindingGetError (result, bindingData->handle.timer.loop));
345 : }
346 38 : uv_close ((uv_handle_t *) &bindingData->handle.timer, ioUvBindingHandleClosedCallback);
347 38 : return 1;
348 : }
349 :
350 : /**
351 : * Update idle operation in I/O binding
352 : * @see kdbio.h ::ElektraIoBindingUpdateIdle
353 : */
354 32 : static int ioUvBindingUpdateIdle (ElektraIoIdleOperation * idleOp)
355 : {
356 32 : ELEKTRA_NOT_NULL (elektraIoIdleGetBindingData (idleOp));
357 32 : UvBindingData * bindingData = (UvBindingData *) elektraIoIdleGetBindingData (idleOp);
358 : int result;
359 32 : if (elektraIoIdleIsEnabled (idleOp))
360 : {
361 24 : result = uv_idle_start (&bindingData->handle.idle, ioUvBindingIdleCallback);
362 : }
363 : else
364 : {
365 8 : result = uv_idle_stop (&bindingData->handle.idle);
366 : }
367 :
368 32 : if (result != 0)
369 : {
370 : ELEKTRA_LOG_WARNING ("could update idle: %s", ioUvBindingGetError (result, bindingData->handle.idle.loop));
371 : return 0;
372 : }
373 : else
374 : {
375 32 : return 1;
376 : }
377 : }
378 :
379 : /**
380 : * Add idle operation to I/O binding
381 : * @see kdbio.h ::ElektraIoBindingAddIdle
382 : */
383 22 : static int ioUvBindingAddIdle (ElektraIoInterface * binding, ElektraIoIdleOperation * idleOp)
384 : {
385 22 : UvBindingData * bindingData = newBindingData ();
386 22 : if (bindingData == NULL)
387 : {
388 : return 0;
389 : }
390 22 : uv_loop_t * loop = (uv_loop_t *) elektraIoBindingGetData (binding);
391 22 : ELEKTRA_NOT_NULL (loop);
392 :
393 22 : elektraIoIdleSetBindingData (idleOp, bindingData);
394 22 : bindingData->operation.idle = idleOp;
395 22 : bindingData->handle.idle.data = bindingData;
396 :
397 : int result;
398 22 : result = uv_idle_init (loop, &bindingData->handle.idle);
399 22 : if (result != 0)
400 : {
401 : ELEKTRA_LOG_WARNING ("could not initialize idle: %s", ioUvBindingGetError (result, loop));
402 : return 0;
403 : }
404 :
405 : // Add idle to loop if enabled
406 22 : if (elektraIoIdleIsEnabled (idleOp))
407 : {
408 16 : ioUvBindingUpdateIdle (idleOp);
409 : }
410 :
411 : return 1;
412 : }
413 :
414 : /**
415 : * Remove idle operation from I/O binding
416 : * @see kdbio.h ::ElektraIoBindingRemoveIdle
417 : */
418 22 : static int ioUvBindingRemoveIdle (ElektraIoIdleOperation * idleOp)
419 : {
420 22 : ELEKTRA_NOT_NULL (elektraIoIdleGetBindingData (idleOp));
421 22 : UvBindingData * bindingData = (UvBindingData *) elektraIoIdleGetBindingData (idleOp);
422 22 : int result = uv_idle_stop (&bindingData->handle.idle);
423 : if (result != 0)
424 : {
425 : ELEKTRA_LOG_WARNING ("could not stop idle: %s", ioUvBindingGetError (result, bindingData->handle.idle.loop));
426 : }
427 22 : uv_close ((uv_handle_t *) &bindingData->handle.idle, ioUvBindingHandleClosedCallback);
428 22 : return 1;
429 : }
430 :
431 : /**
432 : * Cleanup
433 : * @param binding I/O binding
434 : * @see kdbio.h ::ElektraIoBindingCleanup
435 : */
436 40 : static int ioUvBindingCleanup (ElektraIoInterface * binding)
437 : {
438 40 : ELEKTRA_NOT_NULL (binding);
439 40 : elektraFree (binding);
440 40 : return 1;
441 : }
442 :
443 : /**
444 : * Create and initialize a new I/O binding.
445 : * @param loop Loop to use for I/O operations
446 : * @return Populated I/O interface
447 : */
448 40 : ElektraIoInterface * elektraIoUvNew (uv_loop_t * loop)
449 : {
450 40 : if (loop == NULL)
451 : {
452 : ELEKTRA_LOG_WARNING ("loop was NULL");
453 : return NULL;
454 : }
455 : // Initialize I/O interface
456 40 : ElektraIoInterface * binding = elektraIoNewBinding (
457 : // file descriptors
458 : ioUvBindingAddFd, ioUvBindingUpdateFd, ioUvBindingRemoveFd,
459 : // timers
460 : ioUvBindingAddTimer, ioUvBindingUpdateTimer, ioUvBindingRemoveTimer,
461 : // idle
462 : ioUvBindingAddIdle, ioUvBindingUpdateIdle, ioUvBindingRemoveIdle,
463 : // cleanup
464 : ioUvBindingCleanup);
465 40 : if (binding == NULL)
466 : {
467 : ELEKTRA_LOG_WARNING ("elektraIoNewBinding failed");
468 : return NULL;
469 : }
470 :
471 : // Save the libuv loop we are using
472 40 : elektraIoBindingSetData (binding, loop);
473 :
474 40 : return binding;
475 : }
|