LCOV - code coverage report
Current view: top level - src/bindings/io/glib - io_glib.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 164 168 97.6 %
Date: 2019-09-12 12:28:41 Functions: 17 17 100.0 %

          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             : }

Generated by: LCOV version 1.13