1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-27 00:11:17 +02:00

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.
This commit is contained in:
Eelco Dolstra 2017-01-17 18:21:02 +01:00
parent c0d55f9183
commit cc3b93c991
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
4 changed files with 101 additions and 28 deletions

View file

@ -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;
}
}