diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2017-01-17T17·21+0100 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2017-01-17T17·21+0100 |
commit | cc3b93c991e04aff49a44dbced53f070a06f426e (patch) | |
tree | bc88ad9093faabd477ce935aedb5bd6a09459107 /src/libutil | |
parent | c0d55f918379f46b87e43457745895439a85555c (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')
-rw-r--r-- | src/libutil/util.cc | 70 | ||||
-rw-r--r-- | src/libutil/util.hh | 17 |
2 files changed, 82 insertions, 5 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 961c14e3a47f..d79cb5c1333e 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; +} + } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 679c3a1b6df4..052173ff9976 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -263,7 +263,7 @@ void restoreSIGPIPE(); /* User interruption. */ -extern volatile sig_atomic_t _isInterrupted; +extern bool _isInterrupted; extern thread_local bool interruptThrown; @@ -416,4 +416,19 @@ void callSuccess( } +/* Start a thread that handles various signals. Also block those signals + on the current thread (and thus any threads created by it). */ +void startSignalHandlerThread(); + +struct InterruptCallback +{ + virtual ~InterruptCallback() { }; +}; + +/* Register a function that gets called on SIGINT (in a non-signal + context). */ +std::unique_ptr<InterruptCallback> createInterruptCallback( + std::function<void()> callback); + + } |