summaryrefslogtreecommitdiff
path: root/src/basic/async.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/async.c')
-rw-r--r--src/basic/async.c69
1 files changed, 51 insertions, 18 deletions
diff --git a/src/basic/async.c b/src/basic/async.c
index d368b92522..b6c6d6a80b 100644
--- a/src/basic/async.c
+++ b/src/basic/async.c
@@ -27,49 +27,82 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "process-util.h"
+#include "signal-util.h"
#include "util.h"
int asynchronous_job(void* (*func)(void *p), void *arg) {
+ sigset_t ss, saved_ss;
pthread_attr_t a;
pthread_t t;
- int r;
+ int r, k;
- /* It kinda sucks that we have to resort to threads to
- * implement an asynchronous sync(), but well, such is
- * life.
- *
- * Note that issuing this command right before exiting a
- * process will cause the process to wait for the sync() to
- * complete. This function hence is nicely asynchronous really
- * only in long running processes. */
+ /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is
+ * life. */
r = pthread_attr_init(&a);
if (r > 0)
return -r;
r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
- if (r > 0)
+ if (r > 0) {
+ r = -r;
+ goto finish;
+ }
+
+ if (sigfillset(&ss) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* Block all signals before forking off the thread, so that the new thread is started with all signals
+ * blocked. This way the existence of the new thread won't affect signal handling in other threads. */
+
+ r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
+ if (r > 0) {
+ r = -r;
goto finish;
+ }
r = pthread_create(&t, &a, func, arg);
+ k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
+
+ if (r > 0)
+ r = -r;
+ else if (k > 0)
+ r = -k;
+ else
+ r = 0;
+
finish:
pthread_attr_destroy(&a);
- return -r;
+ return r;
}
-static void *sync_thread(void *p) {
- sync();
- return NULL;
-}
+int asynchronous_sync(pid_t *ret_pid) {
+ int r;
-int asynchronous_sync(void) {
- log_debug("Spawning new thread for sync");
+ /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to
+ * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our
+ * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
+ * syscalls. */
+
+ r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child process */
+ (void) sync();
+ _exit(EXIT_SUCCESS);
+ }
- return asynchronous_job(sync_thread, NULL);
+ return 0;
}
static void *close_thread(void *p) {
+ (void) pthread_setname_np(pthread_self(), "close");
+
assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF);
return NULL;
}