LCOV - code coverage report
Current view: top level - src/bindings/io/ev - io_ev.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 126 127 99.2 %
Date: 2023-05-29 20:12:37 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.13