]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
c65eb836 | 2 | |
11c3a366 | 3 | #include <errno.h> |
c65eb836 | 4 | #include <pthread.h> |
11c3a366 | 5 | #include <stddef.h> |
c65eb836 LP |
6 | #include <unistd.h> |
7 | ||
f485606b | 8 | #include "async.h" |
3ffd4af2 | 9 | #include "fd-util.h" |
f485606b | 10 | #include "log.h" |
11c3a366 | 11 | #include "macro.h" |
41bd3379 LP |
12 | #include "process-util.h" |
13 | #include "signal-util.h" | |
8a474b0c | 14 | #include "util.h" |
c65eb836 | 15 | |
f485606b | 16 | int asynchronous_job(void* (*func)(void *p), void *arg) { |
5e9f01e8 | 17 | sigset_t ss, saved_ss; |
c65eb836 LP |
18 | pthread_attr_t a; |
19 | pthread_t t; | |
5e9f01e8 | 20 | int r, k; |
c65eb836 | 21 | |
5e9f01e8 LP |
22 | /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is |
23 | * life. */ | |
c65eb836 LP |
24 | |
25 | r = pthread_attr_init(&a); | |
c1d630d5 | 26 | if (r > 0) |
c65eb836 LP |
27 | return -r; |
28 | ||
29 | r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); | |
5e9f01e8 LP |
30 | if (r > 0) { |
31 | r = -r; | |
32 | goto finish; | |
33 | } | |
34 | ||
cd2a429e | 35 | assert_se(sigfillset(&ss) >= 0); |
5e9f01e8 LP |
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; | |
c65eb836 | 43 | goto finish; |
5e9f01e8 | 44 | } |
c65eb836 | 45 | |
f485606b | 46 | r = pthread_create(&t, &a, func, arg); |
c65eb836 | 47 | |
5e9f01e8 LP |
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 | ||
c65eb836 LP |
57 | finish: |
58 | pthread_attr_destroy(&a); | |
5e9f01e8 | 59 | return r; |
c65eb836 | 60 | } |
f485606b | 61 | |
d00c2631 | 62 | int asynchronous_sync(pid_t *ret_pid) { |
4c253ed1 | 63 | int r; |
41bd3379 LP |
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 | ||
d00c2631 | 70 | r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid); |
4c253ed1 LP |
71 | if (r < 0) |
72 | return r; | |
73 | if (r == 0) { | |
41bd3379 | 74 | /* Child process */ |
41bd3379 LP |
75 | (void) sync(); |
76 | _exit(EXIT_SUCCESS); | |
77 | } | |
f485606b | 78 | |
41bd3379 | 79 | return 0; |
f485606b | 80 | } |
8a474b0c LP |
81 | |
82 | static void *close_thread(void *p) { | |
fa7ff4cf LP |
83 | (void) pthread_setname_np(pthread_self(), "close"); |
84 | ||
23e096cc | 85 | assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); |
8a474b0c LP |
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 | ||
5ed12272 LP |
98 | if (fd >= 0) { |
99 | PROTECT_ERRNO; | |
100 | ||
23e096cc | 101 | r = asynchronous_job(close_thread, FD_TO_PTR(fd)); |
5ed12272 LP |
102 | if (r < 0) |
103 | assert_se(close_nointr(fd) != -EBADF); | |
104 | } | |
8a474b0c LP |
105 | |
106 | return -1; | |
107 | } |