Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief I/O glib binding.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 : #include <stdlib.h>
10 :
11 : #include <glib.h>
12 :
13 : #include <kdbassert.h>
14 : #include <kdbhelper.h>
15 : #include <kdbio.h>
16 : #include <kdblogger.h>
17 :
18 : typedef struct _GlibBindingData GlibBindingData;
19 :
20 : /**
21 : * Custom glib source for fd operations.
22 : *
23 : * @internal
24 : */
25 : typedef struct
26 : {
27 : GSource gSource; /*!< FdSource and GSource start at the same address */
28 : GPollFD pollFd; /*!< polling information: includes file descriptor and flags */
29 : int isPolling; /*!< indicates wheter source is currently polling */
30 : GlibBindingData * bindingData; /*!< backreference to binding data */
31 : } FdSource;
32 :
33 :
34 : /**
35 : * Container for required additional information for
36 : * I/O binding operations & glib sources
37 : *
38 : * @internal
39 : */
40 : struct _GlibBindingData
41 : {
42 : union
43 : {
44 : ElektraIoFdOperation * fd;
45 : ElektraIoTimerOperation * timer;
46 : ElektraIoIdleOperation * idle;
47 : } operation;
48 : union
49 : {
50 : GSource * gSource;
51 : FdSource * fdSource;
52 : } source;
53 :
54 : int enabled; /*!< operation's enabled flag. Only updated on elektraIoBindingUpdateOPERATION */
55 : union
56 : {
57 : unsigned int timerInterval; /*!< timer interval. Only updated on elektraIoBindingUpdateTimer */
58 : } cache;
59 : GSourceFuncs * fdFuncs; /*!< functions for fd source */
60 : };
61 :
62 : /**
63 : * Convert I/O flags to glib event bit mask
64 : * @param flags I/O flags bit mask
65 : * @return glib events bit mask
66 : */
67 : static int flagsToEvents (int flags)
68 : {
69 34 : int events = 0;
70 34 : if (flags & ELEKTRA_IO_READABLE)
71 : {
72 16 : events |= G_IO_IN;
73 : }
74 34 : if (flags & ELEKTRA_IO_WRITABLE)
75 : {
76 18 : events |= G_IO_OUT;
77 : }
78 : return events;
79 : }
80 :
81 : /**
82 : * Convert glib event bit mask to I/O flags
83 : * @param events glib events bit mask
84 : * @return I/O flags bit mask
85 : */
86 : static int eventsToFlags (int events)
87 : {
88 10 : int flags = 0;
89 10 : if (events & G_IO_IN)
90 : {
91 4 : flags |= ELEKTRA_IO_READABLE;
92 : }
93 10 : if (events & G_IO_OUT)
94 : {
95 6 : flags |= ELEKTRA_IO_WRITABLE;
96 : }
97 : return flags;
98 : }
99 :
100 : static GlibBindingData * newBindingData (void)
101 : {
102 68 : GlibBindingData * bindingData = elektraCalloc (sizeof (*bindingData));
103 68 : if (bindingData == NULL)
104 : {
105 : ELEKTRA_LOG_WARNING ("elektraCalloc failed");
106 : return NULL;
107 : }
108 :
109 : return bindingData;
110 : }
111 :
112 : /**
113 : * Prepare the file descriptor source before poll() is called.
114 : *
115 : * @param source glib source
116 : * @param timeout allows to set a timeout for poll()
117 : * @return G_SOURCE_CONTINUE if I/O operation is enabled and events are present
118 : */
119 261870 : static int ioGlibBindingFdPrepare (GSource * source, gint * timeout)
120 : {
121 261870 : FdSource * fdSource = (FdSource *) source;
122 261870 : *timeout = -1; // we can wait until data is available
123 :
124 261870 : int enabled = fdSource->bindingData->enabled;
125 261870 : int eventsEqualAndNotEmpty = !!(fdSource->pollFd.events & fdSource->pollFd.revents);
126 261870 : return enabled && eventsEqualAndNotEmpty;
127 : }
128 :
129 : /**
130 : * Check the file descriptor source after poll() was called.
131 : *
132 : * @param source glib source
133 : * @return G_SOURCE_CONTINUE if I/O operation is enabled and events are present
134 : */
135 261870 : static int ioGlibBindingFdCheck (GSource * source)
136 : {
137 261870 : FdSource * fdSource = (FdSource *) source;
138 261870 : int enabled = fdSource->bindingData->enabled;
139 261870 : int eventsEqualAndNotEmpty = !!(fdSource->pollFd.events & fdSource->pollFd.revents);
140 261870 : return enabled && eventsEqualAndNotEmpty;
141 : }
142 :
143 : /**
144 : * Dispatch the file descriptor source.
145 : *
146 : * @param source glib source
147 : * @param callback source callback (not I/O operation callback). can be set with g_source_set_callback
148 : * @param data source callback data
149 : * @return G_SOURCE_CONTINUE or G_SOURCE_REMOVE
150 : */
151 10 : static int ioGlibBindingFdDispatch (GSource * source, GSourceFunc callback, void * data)
152 : {
153 10 : FdSource * fdSource = (FdSource *) source;
154 20 : int flags = eventsToFlags (fdSource->pollFd.revents);
155 :
156 : // clear flag if source is writable
157 10 : if ((fdSource->pollFd.revents & G_IO_OUT))
158 : {
159 6 : fdSource->pollFd.revents &= ~G_IO_OUT;
160 : }
161 :
162 : // clear flag if source is readable
163 10 : if ((fdSource->pollFd.revents & G_IO_IN))
164 : {
165 4 : fdSource->pollFd.revents &= ~G_IO_IN;
166 : }
167 :
168 10 : if (callback)
169 : {
170 0 : return callback (data);
171 : }
172 :
173 : // Call operation callback
174 10 : GlibBindingData * bindingData = fdSource->bindingData;
175 10 : ElektraIoFdOperation * fdOp = (ElektraIoFdOperation *) bindingData->operation.fd;
176 10 : elektraIoFdGetCallback (fdOp) (fdOp, flags);
177 :
178 10 : return G_SOURCE_CONTINUE;
179 : }
180 :
181 : /**
182 : * Cleanup after file descriptor has been detached
183 : * @param source glib source
184 : */
185 16 : static void ioGlibBindingFdCleanup (GSource * source)
186 : {
187 16 : FdSource * fdSource = (FdSource *) source;
188 16 : elektraFree (fdSource->bindingData->fdFuncs);
189 16 : elektraFree (fdSource->bindingData);
190 16 : }
191 :
192 : /**
193 : * Calls the associated callback.
194 : * Called by glib whenever a timer has elapsed.
195 : *
196 : * @param data source callback data
197 : */
198 80 : static int ioGlibBindingTimerCallback (void * data)
199 : {
200 80 : ELEKTRA_NOT_NULL (data);
201 80 : GlibBindingData * bindingData = (GlibBindingData *) data;
202 80 : ElektraIoTimerOperation * timerOp = (ElektraIoTimerOperation *) bindingData->operation.timer;
203 :
204 80 : if (bindingData->enabled)
205 : {
206 78 : elektraIoTimerGetCallback (timerOp) (timerOp);
207 : }
208 :
209 80 : return G_SOURCE_CONTINUE;
210 : }
211 :
212 : /**
213 : * Calls the associated callback.
214 : * Called by glib whenever an idle operation can perform its operations.
215 : *
216 : * @param data source callback data
217 : */
218 218280 : static int ioGlibBindingIdleCallback (void * data)
219 : {
220 218280 : ELEKTRA_NOT_NULL (data);
221 218280 : GlibBindingData * bindingData = (GlibBindingData *) data;
222 218280 : ElektraIoIdleOperation * idleOp = (ElektraIoIdleOperation *) bindingData->operation.idle;
223 :
224 218280 : if (bindingData->enabled)
225 : {
226 218278 : elektraIoIdleGetCallback (idleOp) (idleOp);
227 : }
228 :
229 218280 : return G_SOURCE_CONTINUE;
230 : }
231 :
232 : /**
233 : * Update information about a file descriptor watched by I/O binding.
234 : * @see kdbio.h ::ElektraIoBindingUpdateFd
235 : */
236 22 : static int ioGlibBindingUpdateFd (ElektraIoFdOperation * fdOp)
237 : {
238 22 : ELEKTRA_NOT_NULL (elektraIoFdGetBindingData (fdOp));
239 22 : GlibBindingData * bindingData = (GlibBindingData *) elektraIoFdGetBindingData (fdOp);
240 22 : bindingData->enabled = elektraIoFdIsEnabled (fdOp);
241 :
242 22 : FdSource * fdSource = bindingData->source.fdSource;
243 22 : GSource * gSource = &bindingData->source.fdSource->gSource;
244 :
245 22 : if (bindingData->enabled)
246 : {
247 : // Update polling flags
248 36 : fdSource->pollFd.events = flagsToEvents (elektraIoFdGetFlags (fdOp));
249 :
250 18 : if (!fdSource->isPolling)
251 : {
252 : // Start polling file descriptor
253 18 : fdSource->pollFd.fd = elektraIoFdGetFd (fdOp);
254 18 : fdSource->pollFd.revents = 0; // clear return events
255 18 : g_source_add_poll (gSource, &fdSource->pollFd);
256 : }
257 : }
258 :
259 22 : if (!bindingData->enabled && fdSource->isPolling)
260 : {
261 : // Stop polling file descriptor
262 0 : g_source_remove_poll (gSource, &fdSource->pollFd);
263 0 : fdSource->isPolling = 0;
264 : }
265 :
266 :
267 22 : return 1;
268 : }
269 :
270 : /**
271 : * Add file descriptor to I/O binding
272 : * @see kdbio.h ::ElektraIoBindingAddFd
273 : */
274 16 : static int ioGlibBindingAddFd (ElektraIoInterface * binding, ElektraIoFdOperation * fdOp)
275 : {
276 16 : GlibBindingData * bindingData = newBindingData ();
277 16 : if (bindingData == NULL)
278 : {
279 : return 0;
280 : }
281 16 : GMainContext * context = (GMainContext *) elektraIoBindingGetData (binding);
282 :
283 16 : elektraIoFdSetBindingData (fdOp, bindingData);
284 16 : bindingData->operation.fd = fdOp;
285 :
286 : // Populate functions for glib source
287 16 : GSourceFuncs * funcs = elektraMalloc (sizeof *funcs);
288 16 : if (!funcs)
289 : {
290 : ELEKTRA_LOG_WARNING ("not enough memory");
291 : return 0;
292 : }
293 16 : funcs->prepare = ioGlibBindingFdPrepare;
294 16 : funcs->check = ioGlibBindingFdCheck;
295 16 : funcs->dispatch = ioGlibBindingFdDispatch;
296 16 : funcs->finalize = ioGlibBindingFdCleanup;
297 16 : bindingData->fdFuncs = funcs;
298 16 : FdSource * fdSource = (FdSource *) g_source_new (funcs, sizeof *fdSource);
299 16 : GSource * gSource = &fdSource->gSource;
300 :
301 : // Start polling the file descriptor
302 16 : fdSource->pollFd.fd = elektraIoFdGetFd (fdOp);
303 32 : fdSource->pollFd.events = flagsToEvents (elektraIoFdGetFlags (fdOp));
304 16 : fdSource->pollFd.revents = 0; // clear return events
305 16 : g_source_add_poll (gSource, &fdSource->pollFd);
306 :
307 16 : g_source_attach (gSource, context);
308 :
309 16 : bindingData->source.fdSource = fdSource;
310 16 : fdSource->bindingData = bindingData;
311 :
312 16 : ioGlibBindingUpdateFd (fdOp);
313 :
314 16 : return 1;
315 : }
316 :
317 : /**
318 : * Remove file descriptor from I/O binding.
319 : * @see kdbio.h ::ElektraIoBindingRemoveFd
320 : */
321 16 : static int ioGlibBindingRemoveFd (ElektraIoFdOperation * fdOp)
322 : {
323 16 : ELEKTRA_NOT_NULL (elektraIoFdGetBindingData (fdOp));
324 16 : GlibBindingData * bindingData = (GlibBindingData *) elektraIoFdGetBindingData (fdOp);
325 16 : FdSource * fdSource = bindingData->source.fdSource;
326 16 : GSource * gSource = &bindingData->source.fdSource->gSource;
327 :
328 16 : if (fdSource->isPolling)
329 : {
330 0 : g_source_remove_poll (gSource, &fdSource->pollFd);
331 : }
332 16 : g_source_destroy (gSource);
333 16 : g_source_unref (gSource);
334 :
335 16 : return 1;
336 : }
337 :
338 : /**
339 : * Update timer in I/O binding.
340 : * @see kdbio.h ::ElektraIoBindingUpdateTimer
341 : */
342 40 : static int ioGlibBindingUpdateTimer (ElektraIoTimerOperation * timerOp)
343 : {
344 40 : ELEKTRA_NOT_NULL (elektraIoTimerGetBindingData (timerOp));
345 40 : GlibBindingData * bindingData = (GlibBindingData *) elektraIoTimerGetBindingData (timerOp);
346 40 : bindingData->enabled = elektraIoTimerIsEnabled (timerOp);
347 :
348 40 : if (bindingData->cache.timerInterval != elektraIoTimerGetInterval (timerOp))
349 : {
350 : // Delete old source
351 36 : if (bindingData->source.gSource)
352 : {
353 2 : g_source_destroy (bindingData->source.gSource);
354 2 : g_source_unref (bindingData->source.gSource);
355 : }
356 36 : ElektraIoInterface * binding = elektraIoTimerGetBinding (timerOp);
357 36 : GMainContext * context = (GMainContext *) elektraIoBindingGetData (binding);
358 :
359 : // (Re-)create timeout source since interval of existing source cannot be changed
360 36 : GSource * timeoutSource = g_timeout_source_new (elektraIoTimerGetInterval (timerOp));
361 36 : g_source_set_callback (timeoutSource, ioGlibBindingTimerCallback, bindingData, NULL);
362 36 : g_source_attach (timeoutSource, context);
363 36 : bindingData->source.gSource = timeoutSource;
364 :
365 36 : bindingData->cache.timerInterval = elektraIoTimerGetInterval (timerOp);
366 : }
367 :
368 40 : return 1;
369 : }
370 :
371 : /**
372 : * Add timer for I/O binding.
373 : * @see kdbio.h ::ElektraIoBindingAddTimer
374 : */
375 34 : static int ioGlibBindingAddTimer (ElektraIoInterface * binding ELEKTRA_UNUSED, ElektraIoTimerOperation * timerOp)
376 : {
377 34 : GlibBindingData * bindingData = newBindingData ();
378 34 : if (bindingData == NULL)
379 : {
380 : return 0;
381 : }
382 :
383 34 : elektraIoTimerSetBindingData (timerOp, bindingData);
384 34 : bindingData->operation.timer = timerOp;
385 :
386 : // Create source
387 34 : ioGlibBindingUpdateTimer (timerOp);
388 :
389 34 : return 1;
390 : }
391 :
392 : /**
393 : * Remove timer from I/O binding
394 : * @see kdbio.h ::ElektraIoBindingRemoveTimer
395 : */
396 34 : static int ioGlibBindingRemoveTimer (ElektraIoTimerOperation * timerOp)
397 : {
398 34 : ELEKTRA_NOT_NULL (elektraIoTimerGetBindingData (timerOp));
399 34 : GlibBindingData * bindingData = (GlibBindingData *) elektraIoTimerGetBindingData (timerOp);
400 :
401 34 : g_source_destroy (bindingData->source.gSource);
402 34 : g_source_unref (bindingData->source.gSource);
403 34 : elektraFree (bindingData);
404 :
405 34 : return 1;
406 : }
407 :
408 : /**
409 : * Update idle operation in I/O binding
410 : * @see kdbio.h ::ElektraIoBindingUpdateIdle
411 : */
412 22 : static int ioGlibBindingUpdateIdle (ElektraIoIdleOperation * idleOp)
413 : {
414 22 : ELEKTRA_NOT_NULL (elektraIoIdleGetBindingData (idleOp));
415 22 : GlibBindingData * bindingData = (GlibBindingData *) elektraIoIdleGetBindingData (idleOp);
416 22 : bindingData->enabled = elektraIoIdleIsEnabled (idleOp);
417 22 : return 1;
418 : }
419 :
420 : /**
421 : * Add idle operation to I/O binding
422 : * @see kdbio.h ::ElektraIoBindingAddIdle
423 : */
424 18 : static int ioGlibBindingAddIdle (ElektraIoInterface * binding, ElektraIoIdleOperation * idleOp)
425 : {
426 18 : GlibBindingData * bindingData = newBindingData ();
427 18 : if (bindingData == NULL)
428 : {
429 : return 0;
430 : }
431 18 : GMainContext * context = (GMainContext *) elektraIoBindingGetData (binding);
432 :
433 18 : elektraIoIdleSetBindingData (idleOp, bindingData);
434 18 : bindingData->operation.idle = idleOp;
435 :
436 18 : GSource * idleSource = g_idle_source_new ();
437 18 : g_source_set_callback (idleSource, ioGlibBindingIdleCallback, bindingData, NULL);
438 18 : g_source_attach (idleSource, context);
439 :
440 18 : bindingData->source.gSource = idleSource;
441 :
442 18 : ioGlibBindingUpdateIdle (idleOp);
443 :
444 18 : return 1;
445 : }
446 :
447 : /**
448 : * Remove idle operation from I/O binding
449 : * @see kdbio.h ::ElektraIoBindingRemoveIdle
450 : */
451 18 : static int ioGlibBindingRemoveIdle (ElektraIoIdleOperation * idleOp)
452 : {
453 18 : ELEKTRA_NOT_NULL (elektraIoIdleGetBindingData (idleOp));
454 18 : GlibBindingData * bindingData = (GlibBindingData *) elektraIoIdleGetBindingData (idleOp);
455 :
456 18 : g_source_destroy (bindingData->source.gSource);
457 18 : g_source_unref (bindingData->source.gSource);
458 18 : elektraFree (bindingData);
459 :
460 18 : return 1;
461 : }
462 :
463 : /**
464 : * Cleanup
465 : * @param binding I/O binding
466 : * @see kdbio.h ::ElektraIoBindingCleanup
467 : */
468 38 : static int ioGlibBindingCleanup (ElektraIoInterface * binding)
469 : {
470 38 : ELEKTRA_NOT_NULL (binding);
471 38 : elektraFree (binding);
472 38 : return 1;
473 : }
474 :
475 : /**
476 : * Create and initialize a new I/O binding.
477 : * @param loop Loop to use for I/O operations
478 : * @return Populated I/O interface
479 : */
480 38 : ElektraIoInterface * elektraIoGlibNew (GMainContext * context)
481 : {
482 : // Initialize I/O interface
483 38 : ElektraIoInterface * binding = elektraIoNewBinding (
484 : // file descriptors
485 : ioGlibBindingAddFd, ioGlibBindingUpdateFd, ioGlibBindingRemoveFd,
486 : // timers
487 : ioGlibBindingAddTimer, ioGlibBindingUpdateTimer, ioGlibBindingRemoveTimer,
488 : // idle
489 : ioGlibBindingAddIdle, ioGlibBindingUpdateIdle, ioGlibBindingRemoveIdle,
490 : // cleanup
491 : ioGlibBindingCleanup);
492 38 : if (binding == NULL)
493 : {
494 : ELEKTRA_LOG_WARNING ("elektraIoNewBinding failed");
495 : return NULL;
496 : }
497 :
498 : // Save the glib context we are using
499 38 : elektraIoBindingSetData (binding, context);
500 :
501 38 : return binding;
502 : }
|