LCOV - code coverage report
Current view: top level - src/bindings/io/uv - io_uv.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 123 124 99.2 %
Date: 2019-09-12 12:28:41 Functions: 15 15 100.0 %

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

Generated by: LCOV version 1.13