Better signals interface

This avoids some CPP and accidentally using Unix stuff in client code.
This commit is contained in:
John Ericson 2024-04-04 12:25:01 -04:00
parent 9d03c2b08b
commit 50f621b241
11 changed files with 134 additions and 51 deletions

View file

@ -357,7 +357,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
if (line.empty()) if (line.empty())
return ProcessLineResult::PromptAgain; return ProcessLineResult::PromptAgain;
_isInterrupted = false; setInterrupted(false);
std::string command, arg; std::string command, arg;

View file

@ -348,7 +348,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
{ {
auto act = (Activity *) payload; auto act = (Activity *) payload;
act->result(resFetchStatus, trim(std::string_view(str, len))); act->result(resFetchStatus, trim(std::string_view(str, len)));
return _isInterrupted ? -1 : 0; return getInterrupted() ? -1 : 0;
} }
static int transferProgressCallback(const git_indexer_progress * stats, void * payload) static int transferProgressCallback(const git_indexer_progress * stats, void * payload)
@ -361,7 +361,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
stats->indexed_deltas, stats->indexed_deltas,
stats->total_deltas, stats->total_deltas,
stats->received_bytes / (1024.0 * 1024.0))); stats->received_bytes / (1024.0 * 1024.0)));
return _isInterrupted ? -1 : 0; return getInterrupted() ? -1 : 0;
} }
void fetch( void fetch(

View file

@ -121,7 +121,7 @@ void initNix()
initLibStore(); initLibStore();
startSignalHandlerThread(); unix::startSignalHandlerThread();
/* Reset SIGCHLD to its default. */ /* Reset SIGCHLD to its default. */
struct sigaction act; struct sigaction act;

View file

@ -1,5 +1,6 @@
#include "daemon.hh" #include "daemon.hh"
#include "monitor-fd.hh" #include "monitor-fd.hh"
#include "signals.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "worker-protocol-impl.hh" #include "worker-protocol-impl.hh"
#include "build-result.hh" #include "build-result.hh"
@ -1038,7 +1039,7 @@ void processConnection(
unsigned int opCount = 0; unsigned int opCount = 0;
Finally finally([&]() { Finally finally([&]() {
_isInterrupted = false; setInterrupted(false);
printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount);
}); });

View file

@ -258,11 +258,11 @@ struct curlFileTransfer : public FileTransfer
int progressCallback(double dltotal, double dlnow) int progressCallback(double dltotal, double dlnow)
{ {
try { try {
act.progress(dlnow, dltotal); act.progress(dlnow, dltotal);
} catch (nix::Interrupted &) { } catch (nix::Interrupted &) {
assert(_isInterrupted); assert(getInterrupted());
} }
return _isInterrupted; return getInterrupted();
} }
static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow) static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow)
@ -466,7 +466,7 @@ struct curlFileTransfer : public FileTransfer
if (errorSink) if (errorSink)
response = std::move(errorSink->s); response = std::move(errorSink->s);
auto exc = auto exc =
code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted code == CURLE_ABORTED_BY_CALLBACK && getInterrupted()
? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri) ? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri)
: httpStatus != 0 : httpStatus != 0
? FileTransferError(err, ? FileTransferError(err,

View file

@ -82,7 +82,7 @@ void setStackSize(rlim_t stackSize)
void restoreProcessContext(bool restoreMounts) void restoreProcessContext(bool restoreMounts)
{ {
restoreSignals(); unix::restoreSignals();
if (restoreMounts) { if (restoreMounts) {
#if __linux__ #if __linux__
restoreMountNamespace(); restoreMountNamespace();

70
src/libutil/signals.hh Normal file
View file

@ -0,0 +1,70 @@
#pragma once
///@file
#include "types.hh"
#include "error.hh"
#include "logging.hh"
#include <functional>
namespace nix {
/* User interruption. */
/**
* @note Does nothing on Windows
*/
static inline void setInterrupted(bool isInterrupted);
/**
* @note Does nothing on Windows
*/
static inline bool getInterrupted();
/**
* @note Does nothing on Windows
*/
static inline void setInterruptCheck(std::function<bool()> interruptCheck);
/**
* @note Does nothing on Windows
*/
void setInterruptThrown();
/**
* @note Does nothing on Windows
*/
inline void checkInterrupt();
/**
* @note Never will happen on Windows
*/
MakeError(Interrupted, BaseError);
struct InterruptCallback
{
virtual ~InterruptCallback() { };
};
/**
* Register a function that gets called on SIGINT (in a non-signal
* context).
*
* @note Does nothing on Windows
*/
std::unique_ptr<InterruptCallback> createInterruptCallback(
std::function<void()> callback);
/**
* A RAII class that causes the current thread to receive SIGUSR1 when
* the signal handler thread receives SIGINT. That is, this allows
* SIGINT to be multiplexed to multiple threads.
*
* @note Does nothing on Windows
*/
struct ReceiveInterrupts;
}
#include "signals-impl.hh"

View file

@ -82,7 +82,7 @@ void ThreadPool::doWork(bool mainThread)
ReceiveInterrupts receiveInterrupts; ReceiveInterrupts receiveInterrupts;
if (!mainThread) if (!mainThread)
interruptCheck = [&]() { return (bool) quit; }; unix::interruptCheck = [&]() { return (bool) quit; };
bool didWork = false; bool didWork = false;
std::exception_ptr exc; std::exception_ptr exc;

View file

@ -50,7 +50,7 @@ public:
*/ */
if (count == 0) continue; if (count == 0) continue;
if (fds[0].revents & POLLHUP) { if (fds[0].revents & POLLHUP) {
triggerInterrupt(); unix::triggerInterrupt();
break; break;
} }
/* This will only happen on macOS. We sleep a bit to /* This will only happen on macOS. We sleep a bit to

View file

@ -1,5 +1,14 @@
#pragma once #pragma once
///@file /**
* @file
*
* Implementation of some inline definitions for Unix signals, and also
* some extra Unix-only interfaces.
*
* (The only reason everything about signals isn't Unix-only is some
* no-op definitions are provided on Windows to avoid excess CPP in
* downstream code.)
*/
#include "types.hh" #include "types.hh"
#include "error.hh" #include "error.hh"
@ -24,22 +33,20 @@ namespace nix {
/* User interruption. */ /* User interruption. */
namespace unix {
extern std::atomic<bool> _isInterrupted; extern std::atomic<bool> _isInterrupted;
extern thread_local std::function<bool()> interruptCheck; extern thread_local std::function<bool()> interruptCheck;
void setInterruptThrown();
void _interrupted(); void _interrupted();
void inline checkInterrupt() /**
{ * Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't
if (_isInterrupted || (interruptCheck && interruptCheck())) * necessarily match the current thread's mask.
_interrupted(); * See saveSignalMask() to set the saved mask to the current mask.
} */
void setChildSignalMask(sigset_t *sigs);
MakeError(Interrupted, BaseError);
/** /**
* Start a thread that handles various signals. Also block those signals * Start a thread that handles various signals. Also block those signals
@ -63,27 +70,27 @@ void saveSignalMask();
*/ */
void restoreSignals(); void restoreSignals();
/**
* Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't
* necessarily match the current thread's mask.
* See saveSignalMask() to set the saved mask to the current mask.
*/
void setChildSignalMask(sigset_t *sigs);
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);
void triggerInterrupt(); void triggerInterrupt();
}
static inline void setInterrupted(bool isInterrupted)
{
unix::_isInterrupted = isInterrupted;
}
static inline bool getInterrupted()
{
return unix::_isInterrupted;
}
void inline checkInterrupt()
{
using namespace unix;
if (_isInterrupted || (interruptCheck && interruptCheck()))
_interrupted();
}
/** /**
* A RAII class that causes the current thread to receive SIGUSR1 when * A RAII class that causes the current thread to receive SIGUSR1 when
* the signal handler thread receives SIGINT. That is, this allows * the signal handler thread receives SIGINT. That is, this allows

View file

@ -8,17 +8,22 @@
namespace nix { namespace nix {
std::atomic<bool> _isInterrupted = false; using namespace unix;
std::atomic<bool> unix::_isInterrupted = false;
namespace unix {
static thread_local bool interruptThrown = false; static thread_local bool interruptThrown = false;
thread_local std::function<bool()> interruptCheck; }
thread_local std::function<bool()> unix::interruptCheck;
void setInterruptThrown() void setInterruptThrown()
{ {
interruptThrown = true; unix::interruptThrown = true;
} }
void _interrupted() void unix::_interrupted()
{ {
/* Block user interrupts while an exception is being handled. /* Block user interrupts while an exception is being handled.
Throwing an exception while another exception is being handled Throwing an exception while another exception is being handled
@ -65,7 +70,7 @@ static void signalHandlerThread(sigset_t set)
} }
} }
void triggerInterrupt() void unix::triggerInterrupt()
{ {
_isInterrupted = true; _isInterrupted = true;
@ -96,7 +101,7 @@ void triggerInterrupt()
static sigset_t savedSignalMask; static sigset_t savedSignalMask;
static bool savedSignalMaskIsSet = false; static bool savedSignalMaskIsSet = false;
void setChildSignalMask(sigset_t * sigs) void unix::setChildSignalMask(sigset_t * sigs)
{ {
assert(sigs); // C style function, but think of sigs as a reference assert(sigs); // C style function, but think of sigs as a reference
@ -115,14 +120,14 @@ void setChildSignalMask(sigset_t * sigs)
savedSignalMaskIsSet = true; savedSignalMaskIsSet = true;
} }
void saveSignalMask() { void unix::saveSignalMask() {
if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask))
throw SysError("querying signal mask"); throw SysError("querying signal mask");
savedSignalMaskIsSet = true; savedSignalMaskIsSet = true;
} }
void startSignalHandlerThread() void unix::startSignalHandlerThread()
{ {
updateWindowSize(); updateWindowSize();
@ -141,7 +146,7 @@ void startSignalHandlerThread()
std::thread(signalHandlerThread, set).detach(); std::thread(signalHandlerThread, set).detach();
} }
void restoreSignals() void unix::restoreSignals()
{ {
// If startSignalHandlerThread wasn't called, that means we're not running // If startSignalHandlerThread wasn't called, that means we're not running
// in a proper libmain process, but a process that presumably manages its // in a proper libmain process, but a process that presumably manages its