LCOV - code coverage report
Current view: top level - basic - async.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 31 42 73.8 %
Date: 2019-08-22 15:41:25 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <pthread.h>
       5             : #include <stddef.h>
       6             : #include <unistd.h>
       7             : 
       8             : #include "async.h"
       9             : #include "errno-util.h"
      10             : #include "fd-util.h"
      11             : #include "log.h"
      12             : #include "macro.h"
      13             : #include "process-util.h"
      14             : #include "signal-util.h"
      15             : #include "util.h"
      16             : 
      17           4 : int asynchronous_job(void* (*func)(void *p), void *arg) {
      18             :         sigset_t ss, saved_ss;
      19             :         pthread_attr_t a;
      20             :         pthread_t t;
      21             :         int r, k;
      22             : 
      23             :         /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is
      24             :          * life. */
      25             : 
      26           4 :         r = pthread_attr_init(&a);
      27           4 :         if (r > 0)
      28           0 :                 return -r;
      29             : 
      30           4 :         r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
      31           4 :         if (r > 0) {
      32           0 :                 r = -r;
      33           0 :                 goto finish;
      34             :         }
      35             : 
      36           4 :         assert_se(sigfillset(&ss) >= 0);
      37             : 
      38             :         /* Block all signals before forking off the thread, so that the new thread is started with all signals
      39             :          * blocked. This way the existence of the new thread won't affect signal handling in other threads. */
      40             : 
      41           4 :         r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
      42           4 :         if (r > 0) {
      43           0 :                 r = -r;
      44           0 :                 goto finish;
      45             :         }
      46             : 
      47           4 :         r = pthread_create(&t, &a, func, arg);
      48             : 
      49           4 :         k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
      50             : 
      51           4 :         if (r > 0)
      52           0 :                 r = -r;
      53           4 :         else if (k > 0)
      54           0 :                 r = -k;
      55             :         else
      56           4 :                 r = 0;
      57             : 
      58           4 : finish:
      59           4 :         pthread_attr_destroy(&a);
      60           4 :         return r;
      61             : }
      62             : 
      63           1 : int asynchronous_sync(pid_t *ret_pid) {
      64             :         int r;
      65             : 
      66             :         /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to
      67             :          * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our
      68             :          * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
      69             :          * syscalls. */
      70             : 
      71           1 :         r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid);
      72           1 :         if (r < 0)
      73           0 :                 return r;
      74           1 :         if (r == 0) {
      75             :                 /* Child process */
      76           0 :                 (void) sync();
      77           0 :                 _exit(EXIT_SUCCESS);
      78             :         }
      79             : 
      80           1 :         return 0;
      81             : }
      82             : 
      83           3 : static void *close_thread(void *p) {
      84           3 :         (void) pthread_setname_np(pthread_self(), "close");
      85             : 
      86           3 :         assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF);
      87           3 :         return NULL;
      88             : }
      89             : 
      90          55 : int asynchronous_close(int fd) {
      91             :         int r;
      92             : 
      93             :         /* This is supposed to behave similar to safe_close(), but
      94             :          * actually invoke close() asynchronously, so that it will
      95             :          * never block. Ideally the kernel would have an API for this,
      96             :          * but it doesn't, so we work around it, and hide this as a
      97             :          * far away as we can. */
      98             : 
      99          55 :         if (fd >= 0) {
     100           3 :                 PROTECT_ERRNO;
     101             : 
     102           3 :                 r = asynchronous_job(close_thread, FD_TO_PTR(fd));
     103           3 :                 if (r < 0)
     104           0 :                          assert_se(close_nointr(fd) != -EBADF);
     105             :         }
     106             : 
     107          55 :         return -1;
     108             : }

Generated by: LCOV version 1.14