Branch data 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 : 16 : 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 : 16 : r = pthread_attr_init(&a); 27 [ - + ]: 16 : if (r > 0) 28 : 0 : return -r; 29 : : 30 : 16 : r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); 31 [ - + ]: 16 : if (r > 0) { 32 : 0 : r = -r; 33 : 0 : goto finish; 34 : : } 35 : : 36 [ - + ]: 16 : 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 : 16 : r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); 42 [ - + ]: 16 : if (r > 0) { 43 : 0 : r = -r; 44 : 0 : goto finish; 45 : : } 46 : : 47 : 16 : r = pthread_create(&t, &a, func, arg); 48 : : 49 : 16 : k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); 50 : : 51 [ - + ]: 16 : if (r > 0) 52 : 0 : r = -r; 53 [ - + ]: 16 : else if (k > 0) 54 : 0 : r = -k; 55 : : else 56 : 16 : r = 0; 57 : : 58 : 16 : finish: 59 : 16 : pthread_attr_destroy(&a); 60 : 16 : return r; 61 : : } 62 : : 63 : 4 : 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 : 4 : r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid); 72 [ - + ]: 4 : if (r < 0) 73 : 0 : return r; 74 [ - + ]: 4 : if (r == 0) { 75 : : /* Child process */ 76 : 0 : (void) sync(); 77 : 0 : _exit(EXIT_SUCCESS); 78 : : } 79 : : 80 : 4 : return 0; 81 : : } 82 : : 83 : 12 : static void *close_thread(void *p) { 84 : 12 : (void) pthread_setname_np(pthread_self(), "close"); 85 : : 86 [ - + ]: 12 : assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); 87 : 12 : return NULL; 88 : : } 89 : : 90 : 220 : 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 [ + + ]: 220 : if (fd >= 0) { 100 : 12 : PROTECT_ERRNO; 101 : : 102 : 12 : r = asynchronous_job(close_thread, FD_TO_PTR(fd)); 103 [ - + ]: 12 : if (r < 0) 104 [ # # ]: 0 : assert_se(close_nointr(fd) != -EBADF); 105 : : } 106 : : 107 : 220 : return -1; 108 : : }