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