LCOV - code coverage report
Current view: top level - src - npth.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 62 210 29.5 %
Date: 2015-11-05 17:05:43 Functions: 10 34 29.4 %

          Line data    Source code
       1             : /* npth.c - a lightweight implementation of pth over pthread.
       2             :    Copyright (C) 2011 g10 Code GmbH
       3             : 
       4             :    This file is part of NPTH.
       5             : 
       6             :    NPTH is free software; you can redistribute it and/or modify it
       7             :    under the terms of either
       8             : 
       9             :    - the GNU Lesser General Public License as published by the Free
      10             :    Software Foundation; either version 3 of the License, or (at
      11             :    your option) any later version.
      12             : 
      13             :    or
      14             : 
      15             :    - the GNU General Public License as published by the Free
      16             :    Software Foundation; either version 2 of the License, or (at
      17             :    your option) any later version.
      18             : 
      19             :    or both in parallel, as here.
      20             : 
      21             :    NPTH is distributed in the hope that it will be useful, but WITHOUT
      22             :    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
      23             :    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
      24             :    License for more details.
      25             : 
      26             :    You should have received a copies of the GNU General Public License
      27             :    and the GNU Lesser General Public License along with this program;
      28             :    if not, see <http://www.gnu.org/licenses/>.  */
      29             : 
      30             : #ifdef HAVE_CONFIG_H
      31             : #include <config.h>
      32             : #endif
      33             : 
      34             : #include <stdlib.h>
      35             : #include <stdio.h>
      36             : #include <string.h>
      37             : #include <assert.h>
      38             : #include <errno.h>
      39             : #include <pthread.h>
      40             : #include <fcntl.h>
      41             : #include <sys/stat.h>
      42             : #include <semaphore.h>
      43             : #ifdef HAVE_UNISTD_H
      44             : # include <unistd.h>
      45             : #endif
      46             : #ifndef HAVE_PSELECT
      47             : # include <signal.h>
      48             : #endif
      49             : 
      50             : #include "npth.h"
      51             : 
      52             : 
      53             : /* The global lock that excludes all threads but one.  This is a
      54             :    semaphore, because these can be safely used in a library even if
      55             :    the application or other libraries call fork(), including from a
      56             :    signal handler.  sem_post is async-signal-safe.  (The reason a
      57             :    semaphore is safe and a mutex is not safe is that a mutex has an
      58             :    owner, while a semaphore does not.)  We init sceptre to a static
      59             :    buffer for use by sem_init; in case sem_open is used instead
      60             :    SCEPTRE will changed to the value returned by sem_open.  */
      61             : static sem_t sceptre_buffer;
      62             : static sem_t *sceptre = &sceptre_buffer;
      63             : 
      64             : /* The main thread is the active thread at the time pth_init was
      65             :    called.  As of now it is only useful for debugging.  The volatile
      66             :    make sure the compiler does not eliminate this set but not used
      67             :    variable.  */
      68             : static volatile pthread_t main_thread;
      69             : 
      70             : /* Systems that don't have pthread_mutex_timedlock get a busy wait
      71             :    implementation that probes the lock every BUSY_WAIT_INTERVAL
      72             :    milliseconds.  */
      73             : #define BUSY_WAIT_INTERVAL 200
      74             : 
      75             : typedef int (*trylock_func_t) (void *);
      76             : 
      77             : static int
      78           0 : busy_wait_for (trylock_func_t trylock, void *lock,
      79             :                const struct timespec *abstime)
      80             : {
      81             :   int err;
      82             : 
      83             :   /* This is not great, but better than nothing.  Only works for locks
      84             :      which are mostly uncontested.  Provides absolutely no fairness at
      85             :      all.  Creates many wake-ups.  */
      86             :   while (1)
      87             :     {
      88             :       struct timespec ts;
      89           0 :       err = npth_clock_gettime (&ts);
      90           0 :       if (err < 0)
      91             :         {
      92             :           /* Just for safety make sure we return some error.  */
      93           0 :           err = errno ? errno : EINVAL;
      94           0 :           break;
      95             :         }
      96             : 
      97           0 :       if (! npth_timercmp (abstime, &ts, <))
      98             :         {
      99           0 :           err = ETIMEDOUT;
     100           0 :           break;
     101             :         }
     102             : 
     103           0 :       err = (*trylock) (lock);
     104           0 :       if (err != EBUSY)
     105           0 :         break;
     106             : 
     107             :       /* Try again after waiting a bit.  We could calculate the
     108             :          maximum wait time from ts and abstime, but we don't
     109             :          bother, as our granularity is pretty fine.  */
     110           0 :       usleep (BUSY_WAIT_INTERVAL * 1000);
     111           0 :     }
     112             : 
     113           0 :   return err;
     114             : }
     115             : 
     116             : 
     117             : static void
     118     2932706 : enter_npth (void)
     119             : {
     120             :   int res;
     121             : 
     122     2932706 :   res = sem_post (sceptre);
     123     2932702 :   assert (res == 0);
     124     2932702 : }
     125             : 
     126             : 
     127             : static void
     128     2932654 : leave_npth (void)
     129             : {
     130             :   int res;
     131     2932654 :   int save_errno = errno;
     132             : 
     133             :   do {
     134     2932632 :     res = sem_wait (sceptre);
     135     2932708 :   } while (res < 0 && errno == EINTR);
     136             : 
     137     2932708 :   assert (!res);
     138     2932708 :   errno = save_errno;
     139     2932708 : }
     140             : 
     141             : #define ENTER() enter_npth ()
     142             : #define LEAVE() leave_npth ()
     143             : 
     144             : 
     145             : static int
     146           0 : try_sem_open (sem_t **r_sem)
     147             : {
     148             :   sem_t *sem;
     149             :   char name [256];
     150           0 :   int counter = 0;
     151             : 
     152             :   do
     153             :     {
     154           0 :       snprintf (name, sizeof name - 1, "/npth-sceptre-%lu-%u",
     155           0 :                 (unsigned long)getpid (), counter);
     156           0 :       name[sizeof name -1] = 0;
     157           0 :       counter++;
     158             : 
     159           0 :       sem = sem_open (name, (O_CREAT | O_EXCL), (S_IRUSR | S_IWUSR), 1);
     160           0 :       if (sem != SEM_FAILED)
     161             :         {
     162           0 :           *r_sem = sem;
     163           0 :           return 0;
     164             :         }
     165           0 :       fprintf (stderr, " semOpen(%s): %s\n", name, strerror (errno));
     166             :     }
     167           0 :   while (errno == EEXIST);
     168             : 
     169           0 :   return -1;
     170             : }
     171             : 
     172             : 
     173             : int
     174           2 : npth_init (void)
     175             : {
     176             :   int res;
     177             : 
     178           2 :   main_thread = pthread_self();
     179             : 
     180             :   /* Better reset ERRNO so that we know that it has been set by
     181             :      sem_init.  */
     182           2 :   errno = 0;
     183             : 
     184             :   /* The semaphore is not shared and binary.  */
     185           2 :   res = sem_init (sceptre, 0, 1);
     186           2 :   if (res < 0)
     187             :     {
     188             :       /* Mac OSX and some AIX versions have sem_init but return
     189             :          ENOSYS.  This is allowed according to some POSIX versions but
     190             :          the informative section is quite fuzzy about it.  We resort
     191             :          to sem_open in this case.  */
     192           0 :       if (errno == ENOSYS)
     193             :         {
     194           0 :           if (try_sem_open (&sceptre))
     195           0 :             return errno;
     196             :         }
     197             :       else
     198             :         {
     199             :           /* POSIX.1-2001 defines the semaphore interface but does not
     200             :              specify the return value for success.  Thus we better
     201             :              bail out on error only on a POSIX.1-2008 system.  */
     202             : #if _POSIX_C_SOURCE >= 200809L
     203           0 :           return errno;
     204             : #endif
     205             :         }
     206             :     }
     207             : 
     208           2 :   LEAVE();
     209           2 :   return 0;
     210             : }
     211             : 
     212             : 
     213             : int
     214           0 : npth_getname_np (npth_t target_thread, char *buf, size_t buflen)
     215             : {
     216             : #ifdef HAVE_PTHREAD_GETNAME_NP
     217           0 :   return pthread_getname_np (target_thread, buf, buflen);
     218             : #else
     219             :   (void)target_thread;
     220             :   (void)buf;
     221             :   (void)buflen;
     222             :   return ENOSYS;
     223             : #endif
     224             : }
     225             : 
     226             : 
     227             : int
     228           3 : npth_setname_np (npth_t target_thread, const char *name)
     229             : {
     230             : #ifdef HAVE_PTHREAD_SETNAME_NP
     231             : #ifdef __NetBSD__
     232             :   return pthread_setname_np (target_thread, "%s", (void*) name);
     233             : #else
     234           3 :   return pthread_setname_np (target_thread, name);
     235             : #endif
     236             : #else
     237             :   (void)target_thread;
     238             :   (void)name;
     239             :   return ENOSYS;
     240             : #endif
     241             : }
     242             : 
     243             : 
     244             : 
     245             : struct startup_s
     246             : {
     247             :   void *(*start_routine) (void *);
     248             :   void *arg;
     249             : };
     250             : 
     251             : 
     252             : static void *
     253           3 : thread_start (void *startup_arg)
     254             : {
     255           3 :   struct startup_s *startup = startup_arg;
     256             :   void *(*start_routine) (void *);
     257             :   void *arg;
     258             :   void *result;
     259             : 
     260           3 :   start_routine = startup->start_routine;
     261           3 :   arg = startup->arg;
     262           3 :   free (startup);
     263             : 
     264           3 :   LEAVE();
     265           3 :   result = (*start_routine) (arg);
     266             :   /* Note: instead of returning here, we might end up in
     267             :      npth_exit() instead.  */
     268           3 :   ENTER();
     269             : 
     270           3 :   return result;
     271             : }
     272             : 
     273             : 
     274             : int
     275           3 : npth_create (npth_t *thread, const npth_attr_t *attr,
     276             :              void *(*start_routine) (void *), void *arg)
     277             : {
     278             :   int err;
     279             :   struct startup_s *startup;
     280             : 
     281           3 :   startup = malloc (sizeof (*startup));
     282           3 :   if (!startup)
     283           0 :     return errno;
     284             : 
     285           3 :   startup->start_routine = start_routine;
     286           3 :   startup->arg = arg;
     287           3 :   err = pthread_create (thread, attr, thread_start, startup);
     288           3 :   if (err)
     289             :     {
     290           0 :       free (startup);
     291           0 :       return err;
     292             :     }
     293             : 
     294             :   /* Memory is released in thread_start.  */
     295           3 :   return 0;
     296             : }
     297             : 
     298             : 
     299             : int
     300           2 : npth_join (npth_t thread, void **retval)
     301             : {
     302             :   int err;
     303             : 
     304             : #ifdef HAVE_PTHREAD_TRYJOIN_NP
     305             :   /* No need to allow competing threads to enter when we can get the
     306             :      lock immediately.  pthread_tryjoin_np is a GNU extension.  */
     307           2 :   err = pthread_tryjoin_np (thread, retval);
     308           2 :   if (err != EBUSY)
     309           0 :     return err;
     310             : #endif /*HAVE_PTHREAD_TRYJOIN_NP*/
     311             : 
     312           2 :   ENTER();
     313           2 :   err = pthread_join (thread, retval);
     314           2 :   LEAVE();
     315           2 :   return err;
     316             : }
     317             : 
     318             : 
     319             : void
     320           0 : npth_exit (void *retval)
     321             : {
     322           0 :   ENTER();
     323           0 :   pthread_exit (retval);
     324             :   /* Never reached.  But just in case pthread_exit does return... */
     325             :   LEAVE();
     326             : }
     327             : 
     328             : 
     329             : int
     330          21 : npth_mutex_lock (npth_mutex_t *mutex)
     331             : {
     332             :   int err;
     333             : 
     334             :   /* No need to allow competing threads to enter when we can get the
     335             :      lock immediately.  */
     336          21 :   err = pthread_mutex_trylock (mutex);
     337          21 :   if (err != EBUSY)
     338          20 :     return err;
     339             : 
     340           1 :   ENTER();
     341           1 :   err = pthread_mutex_lock (mutex);
     342           1 :   LEAVE();
     343           1 :   return err;
     344             : }
     345             : 
     346             : 
     347             : int
     348           0 : npth_mutex_timedlock (npth_mutex_t *mutex, const struct timespec *abstime)
     349             : {
     350             :   int err;
     351             : 
     352             :   /* No need to allow competing threads to enter when we can get the
     353             :      lock immediately.  */
     354           0 :   err = pthread_mutex_trylock (mutex);
     355           0 :   if (err != EBUSY)
     356           0 :     return err;
     357             : 
     358           0 :   ENTER();
     359             : #if HAVE_PTHREAD_MUTEX_TIMEDLOCK
     360           0 :   err = pthread_mutex_timedlock (mutex, abstime);
     361             : #else
     362             :   err = busy_wait_for ((trylock_func_t) pthread_mutex_trylock, mutex, abstime);
     363             : #endif
     364           0 :   LEAVE();
     365           0 :   return err;
     366             : }
     367             : 
     368             : 
     369             : #ifndef _NPTH_NO_RWLOCK
     370             : int
     371           0 : npth_rwlock_rdlock (npth_rwlock_t *rwlock)
     372             : {
     373             :   int err;
     374             : 
     375             : #ifdef HAVE_PTHREAD_RWLOCK_TRYRDLOCK
     376             :   /* No need to allow competing threads to enter when we can get the
     377             :      lock immediately.  */
     378           0 :   err = pthread_rwlock_tryrdlock (rwlock);
     379           0 :   if (err != EBUSY)
     380           0 :     return err;
     381             : #endif
     382             : 
     383           0 :   ENTER();
     384           0 :   err = pthread_rwlock_rdlock (rwlock);
     385           0 :   LEAVE();
     386           0 :   return err;
     387             : }
     388             : 
     389             : 
     390             : int
     391           0 : npth_rwlock_timedrdlock (npth_rwlock_t *rwlock, const struct timespec *abstime)
     392             : {
     393             :   int err;
     394             : 
     395             : #ifdef HAVE_PTHREAD_RWLOCK_TRYRDLOCK
     396             :   /* No need to allow competing threads to enter when we can get the
     397             :      lock immediately.  */
     398           0 :   err = pthread_rwlock_tryrdlock (rwlock);
     399           0 :   if (err != EBUSY)
     400           0 :     return err;
     401             : #endif
     402             : 
     403           0 :   ENTER();
     404             : #if HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK
     405           0 :   err = pthread_rwlock_timedrdlock (rwlock, abstime);
     406             : #else
     407             :   err = busy_wait_for ((trylock_func_t) pthread_rwlock_tryrdlock, rwlock,
     408             :                        abstime);
     409             : #endif
     410           0 :   LEAVE();
     411           0 :   return err;
     412             : }
     413             : 
     414             : 
     415             : int
     416           0 : npth_rwlock_wrlock (npth_rwlock_t *rwlock)
     417             : {
     418             :   int err;
     419             : 
     420             : #ifdef HAVE_PTHREAD_RWLOCK_TRYWRLOCK
     421             :   /* No need to allow competing threads to enter when we can get the
     422             :      lock immediately.  */
     423           0 :   err = pthread_rwlock_trywrlock (rwlock);
     424           0 :   if (err != EBUSY)
     425           0 :     return err;
     426             : #endif
     427             : 
     428           0 :   ENTER();
     429           0 :   err = pthread_rwlock_wrlock (rwlock);
     430           0 :   LEAVE();
     431           0 :   return err;
     432             : }
     433             : 
     434             : 
     435             : int
     436           0 : npth_rwlock_timedwrlock (npth_rwlock_t *rwlock, const struct timespec *abstime)
     437             : {
     438             :   int err;
     439             : 
     440             : #ifdef HAVE_PTHREAD_RWLOCK_TRYWRLOCK
     441             :   /* No need to allow competing threads to enter when we can get the
     442             :      lock immediately.  */
     443           0 :   err = pthread_rwlock_trywrlock (rwlock);
     444           0 :   if (err != EBUSY)
     445           0 :     return err;
     446             : #endif
     447             : 
     448           0 :   ENTER();
     449             : #if HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK
     450           0 :   err = pthread_rwlock_timedwrlock (rwlock, abstime);
     451             : #elif HAVE_PTHREAD_RWLOCK_TRYRDLOCK
     452             :   err = busy_wait_for ((trylock_func_t) pthread_rwlock_trywrlock, rwlock,
     453             :                        abstime);
     454             : #else
     455             :   err = ENOSYS;
     456             : #endif
     457           0 :   LEAVE();
     458           0 :   return err;
     459             : }
     460             : #endif
     461             : 
     462             : 
     463             : int
     464           0 : npth_cond_wait (npth_cond_t *cond, npth_mutex_t *mutex)
     465             : {
     466             :   int err;
     467             : 
     468           0 :   ENTER();
     469           0 :   err = pthread_cond_wait (cond, mutex);
     470           0 :   LEAVE();
     471           0 :   return err;
     472             : }
     473             : 
     474             : 
     475             : int
     476           0 : npth_cond_timedwait (npth_cond_t *cond, npth_mutex_t *mutex,
     477             :                      const struct timespec *abstime)
     478             : {
     479             :   int err;
     480             : 
     481           0 :   ENTER();
     482           0 :   err = pthread_cond_timedwait (cond, mutex, abstime);
     483           0 :   LEAVE();
     484           0 :   return err;
     485             : }
     486             : 
     487             : 
     488             : /* Standard POSIX Replacement API */
     489             : 
     490             : int
     491         102 : npth_usleep(unsigned int usec)
     492             : {
     493             :   int res;
     494             : 
     495         102 :   ENTER();
     496         102 :   res = usleep(usec);
     497         102 :   LEAVE();
     498         102 :   return res;
     499             : }
     500             : 
     501             : 
     502             : unsigned int
     503     2932598 : npth_sleep(unsigned int sec)
     504             : {
     505             :   unsigned res;
     506             : 
     507     2932598 :   ENTER();
     508     2932598 :   res = sleep(sec);
     509     2932598 :   LEAVE();
     510     2932598 :   return res;
     511             : }
     512             : 
     513             : 
     514             : int
     515           0 : npth_system(const char *cmd)
     516             : {
     517             :   int res;
     518             : 
     519           0 :   ENTER();
     520           0 :   res = system(cmd);
     521           0 :   LEAVE();
     522           0 :   return res;
     523             : }
     524             : 
     525             : 
     526             : pid_t
     527           0 : npth_waitpid(pid_t pid, int *status, int options)
     528             : {
     529             :   pid_t res;
     530             : 
     531           0 :   ENTER();
     532           0 :   res = waitpid(pid,status, options);
     533           0 :   LEAVE();
     534           0 :   return res;
     535             : }
     536             : 
     537             : 
     538             : int
     539           0 : npth_connect(int s, const struct sockaddr *addr, socklen_t addrlen)
     540             : {
     541             :   int res;
     542             : 
     543           0 :   ENTER();
     544           0 :   res = connect(s, addr, addrlen);
     545           0 :   LEAVE();
     546           0 :   return res;
     547             : }
     548             : 
     549             : 
     550             : int
     551           0 : npth_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
     552             : {
     553             :   int res;
     554             : 
     555           0 :   ENTER();
     556           0 :   res = accept(s, addr, addrlen);
     557           0 :   LEAVE();
     558           0 :   return res;
     559             : }
     560             : 
     561             : 
     562             : int
     563           0 : npth_select(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
     564             :             struct timeval *timeout)
     565             : {
     566             :   int res;
     567             : 
     568           0 :   ENTER();
     569           0 :   res = select(nfd, rfds, wfds, efds, timeout);
     570           0 :   LEAVE();
     571           0 :   return res;
     572             : }
     573             : 
     574             : 
     575             : int
     576           0 : npth_pselect(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
     577             :              const struct timespec *timeout, const sigset_t *sigmask)
     578             : {
     579             :   int res;
     580             : 
     581           0 :   ENTER();
     582             : #ifdef HAVE_PSELECT
     583           0 :   res = pselect (nfd, rfds, wfds, efds, timeout, sigmask);
     584             : #else /*!HAVE_PSELECT*/
     585             :   {
     586             :     /* A better emulation of pselect would be to create a pipe, wait
     587             :        in the select for one end and have a signal handler write to
     588             :        the other end.  However, this is non-trivial to implement and
     589             :        thus we only print a compile time warning.  */
     590             : #   ifdef __GNUC__
     591             : #     warning Using a non race free pselect emulation.
     592             : #   endif
     593             : 
     594             :     struct timeval t, *tp;
     595             : 
     596             :     tp = NULL;
     597             :     if (!timeout)
     598             :       ;
     599             :     else if (timeout->tv_nsec >= 0 && timeout->tv_nsec < 1000000000)
     600             :       {
     601             :         t.tv_sec = timeout->tv_sec;
     602             :         t.tv_usec = (timeout->tv_nsec + 999) / 1000;
     603             :         tp = &t;
     604             :       }
     605             :     else
     606             :       {
     607             :         errno = EINVAL;
     608             :         res = -1;
     609             :         goto leave;
     610             :       }
     611             : 
     612             :     if (sigmask)
     613             :       {
     614             :         int save_errno;
     615             :         sigset_t savemask;
     616             : 
     617             :         pthread_sigmask (SIG_SETMASK, sigmask, &savemask);
     618             :         res = select (nfd, rfds, wfds, efds, tp);
     619             :         save_errno = errno;
     620             :         pthread_sigmask (SIG_SETMASK, &savemask, NULL);
     621             :         errno = save_errno;
     622             :       }
     623             :     else
     624             :       res = select (nfd, rfds, wfds, efds, tp);
     625             : 
     626             :   leave:
     627             :     ;
     628             :   }
     629             : #endif /*!HAVE_PSELECT*/
     630           0 :   LEAVE();
     631           0 :   return res;
     632             : }
     633             : 
     634             : 
     635             : ssize_t
     636           0 : npth_read(int fd, void *buf, size_t nbytes)
     637             : {
     638             :   ssize_t res;
     639             : 
     640           0 :   ENTER();
     641           0 :   res = read(fd, buf, nbytes);
     642           0 :   LEAVE();
     643           0 :   return res;
     644             : }
     645             : 
     646             : 
     647             : ssize_t
     648           0 : npth_write(int fd, const void *buf, size_t nbytes)
     649             : {
     650             :   ssize_t res;
     651             : 
     652           0 :   ENTER();
     653           0 :   res = write(fd, buf, nbytes);
     654           0 :   LEAVE();
     655           0 :   return res;
     656             : }
     657             : 
     658             : 
     659             : int
     660           0 : npth_recvmsg (int fd, struct msghdr *msg, int flags)
     661             : {
     662             :   int res;
     663             : 
     664           0 :   ENTER();
     665           0 :   res = recvmsg (fd, msg, flags);
     666           0 :   LEAVE();
     667           0 :   return res;
     668             : }
     669             : 
     670             : 
     671             : int
     672           0 : npth_sendmsg (int fd, const struct msghdr *msg, int flags)
     673             : {
     674             :   int res;
     675             : 
     676           0 :   ENTER();
     677           0 :   res = sendmsg (fd, msg, flags);
     678           0 :   LEAVE();
     679           0 :   return res;
     680             : }
     681             : 
     682             : 
     683             : void
     684           0 : npth_unprotect (void)
     685             : {
     686           0 :   ENTER();
     687           0 : }
     688             : 
     689             : 
     690             : void
     691           0 : npth_protect (void)
     692             : {
     693           0 :   LEAVE();
     694           0 : }
     695             : 
     696             : 
     697             : int
     698           0 : npth_clock_gettime (struct timespec *ts)
     699             : {
     700             : #if defined(CLOCK_REALTIME) && HAVE_CLOCK_GETTIME
     701           0 :   return clock_gettime (CLOCK_REALTIME, ts);
     702             : #elif HAVE_GETTIMEOFDAY
     703             :   {
     704             :     struct timeval tv;
     705             : 
     706             :     if (gettimeofday (&tv, NULL))
     707             :       return -1;
     708             :     ts->tv_sec = tv.tv_sec;
     709             :     ts->tv_nsec = tv.tv_usec * 1000;
     710             :     return 0;
     711             :   }
     712             : #else
     713             :   /* FIXME: fall back on time() with seconds resolution.  */
     714             : # error clock_gettime not available - please provide a fallback.
     715             : #endif
     716             : }

Generated by: LCOV version 1.11