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