aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/util.cc
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2017-01-17 18:21:02 +0100
committerEelco Dolstra <edolstra@gmail.com>2017-01-17 18:21:02 +0100
commitcc3b93c991e04aff49a44dbced53f070a06f426e (patch)
treebc88ad9093faabd477ce935aedb5bd6a09459107 /src/libutil/util.cc
parentc0d55f918379f46b87e43457745895439a85555c (diff)
Handle SIGINT etc. via a sigwait() signal handler thread
This allows other threads to install callbacks that run in a regular, non-signal context. In particular, we can use this to signal the downloader thread to quit. Closes #1183.
Diffstat (limited to 'src/libutil/util.cc')
-rw-r--r--src/libutil/util.cc70
1 files changed, 66 insertions, 4 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 961c14e3a..d79cb5c13 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -2,14 +2,16 @@
#include "util.hh"
#include "affinity.hh"
+#include "sync.hh"
-#include <iostream>
+#include <cctype>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
-#include <sstream>
#include <cstring>
-#include <cctype>
+#include <iostream>
+#include <sstream>
+#include <thread>
#include <sys/wait.h>
#include <unistd.h>
@@ -933,7 +935,7 @@ void restoreSIGPIPE()
//////////////////////////////////////////////////////////////////////
-volatile sig_atomic_t _isInterrupted = 0;
+bool _isInterrupted = false;
thread_local bool interruptThrown = false;
@@ -1200,4 +1202,64 @@ void callFailure(const std::function<void(std::exception_ptr exc)> & failure, st
}
+static Sync<std::list<std::function<void()>>> _interruptCallbacks;
+
+static void signalHandlerThread(sigset_t set)
+{
+ while (true) {
+ int signal = 0;
+ sigwait(&set, &signal);
+
+ if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP) {
+ _isInterrupted = 1;
+
+ {
+ auto interruptCallbacks(_interruptCallbacks.lock());
+ for (auto & callback : *interruptCallbacks) {
+ try {
+ callback();
+ } catch (...) {
+ ignoreException();
+ }
+ }
+ }
+ }
+ }
+}
+
+void startSignalHandlerThread()
+{
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGHUP);
+ if (pthread_sigmask(SIG_BLOCK, &set, nullptr))
+ throw SysError("blocking signals");
+
+ std::thread(signalHandlerThread, set).detach();
+}
+
+/* RAII helper to automatically deregister a callback. */
+struct InterruptCallbackImpl : InterruptCallback
+{
+ std::list<std::function<void()>>::iterator it;
+ ~InterruptCallbackImpl() override
+ {
+ _interruptCallbacks.lock()->erase(it);
+ }
+};
+
+std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback)
+{
+ auto interruptCallbacks(_interruptCallbacks.lock());
+ interruptCallbacks->push_back(callback);
+
+ auto res = std::make_unique<InterruptCallbackImpl>();
+ res->it = interruptCallbacks->end();
+ res->it--;
+
+ return res;
+}
+
}