diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 7cecc60b7..4f1bf4516 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -357,7 +357,7 @@ ProcessLineResult NixRepl::processLine(std::string line) if (line.empty()) return ProcessLineResult::PromptAgain; - _isInterrupted = false; + setInterrupted(false); std::string command, arg; diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index b723554cc..5e560f5f3 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -348,7 +348,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto act = (Activity *) payload; 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) @@ -361,7 +361,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this stats->indexed_deltas, stats->total_deltas, stats->received_bytes / (1024.0 * 1024.0))); - return _isInterrupted ? -1 : 0; + return getInterrupted() ? -1 : 0; } void fetch( diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index f3dd73101..4c9051d3b 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -121,7 +121,7 @@ void initNix() initLibStore(); - startSignalHandlerThread(); + unix::startSignalHandlerThread(); /* Reset SIGCHLD to its default. */ struct sigaction act; diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 2c808015d..def2c80b2 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,5 +1,6 @@ #include "daemon.hh" #include "monitor-fd.hh" +#include "signals.hh" #include "worker-protocol.hh" #include "worker-protocol-impl.hh" #include "build-result.hh" @@ -1038,7 +1039,7 @@ void processConnection( unsigned int opCount = 0; Finally finally([&]() { - _isInterrupted = false; + setInterrupted(false); printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 0d5379d25..df89b5bd1 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -258,11 +258,11 @@ struct curlFileTransfer : public FileTransfer int progressCallback(double dltotal, double dlnow) { try { - act.progress(dlnow, dltotal); + act.progress(dlnow, dltotal); } catch (nix::Interrupted &) { - assert(_isInterrupted); + assert(getInterrupted()); } - return _isInterrupted; + return getInterrupted(); } static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow) @@ -466,7 +466,7 @@ struct curlFileTransfer : public FileTransfer if (errorSink) response = std::move(errorSink->s); 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) : httpStatus != 0 ? FileTransferError(err, diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index c13e6d567..d33f7163a 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -82,7 +82,7 @@ void setStackSize(rlim_t stackSize) void restoreProcessContext(bool restoreMounts) { - restoreSignals(); + unix::restoreSignals(); if (restoreMounts) { #if __linux__ restoreMountNamespace(); diff --git a/src/libutil/signals.hh b/src/libutil/signals.hh new file mode 100644 index 000000000..0473128db --- /dev/null +++ b/src/libutil/signals.hh @@ -0,0 +1,70 @@ +#pragma once +///@file + +#include "types.hh" +#include "error.hh" +#include "logging.hh" + +#include + +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 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 createInterruptCallback( + std::function 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" diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 9a7dfee56..805f31d80 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -82,7 +82,7 @@ void ThreadPool::doWork(bool mainThread) ReceiveInterrupts receiveInterrupts; if (!mainThread) - interruptCheck = [&]() { return (bool) quit; }; + unix::interruptCheck = [&]() { return (bool) quit; }; bool didWork = false; std::exception_ptr exc; diff --git a/src/libutil/unix/monitor-fd.hh b/src/libutil/unix/monitor-fd.hh index 228fb13f8..103894de9 100644 --- a/src/libutil/unix/monitor-fd.hh +++ b/src/libutil/unix/monitor-fd.hh @@ -50,7 +50,7 @@ public: */ if (count == 0) continue; if (fds[0].revents & POLLHUP) { - triggerInterrupt(); + unix::triggerInterrupt(); break; } /* This will only happen on macOS. We sleep a bit to diff --git a/src/libutil/unix/signals.hh b/src/libutil/unix/signals-impl.hh similarity index 80% rename from src/libutil/unix/signals.hh rename to src/libutil/unix/signals-impl.hh index 7e8beff33..7ac8c914d 100644 --- a/src/libutil/unix/signals.hh +++ b/src/libutil/unix/signals-impl.hh @@ -1,5 +1,14 @@ #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 "error.hh" @@ -24,22 +33,20 @@ namespace nix { /* User interruption. */ +namespace unix { + extern std::atomic _isInterrupted; extern thread_local std::function interruptCheck; -void setInterruptThrown(); - void _interrupted(); -void inline checkInterrupt() -{ - if (_isInterrupted || (interruptCheck && interruptCheck())) - _interrupted(); -} - -MakeError(Interrupted, BaseError); - +/** + * 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); /** * Start a thread that handles various signals. Also block those signals @@ -63,27 +70,27 @@ void saveSignalMask(); */ 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 createInterruptCallback( - std::function callback); - 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 * the signal handler thread receives SIGINT. That is, this allows diff --git a/src/libutil/unix/signals.cc b/src/libutil/unix/signals.cc index eaa4ea30e..7e30687d8 100644 --- a/src/libutil/unix/signals.cc +++ b/src/libutil/unix/signals.cc @@ -8,17 +8,22 @@ namespace nix { -std::atomic _isInterrupted = false; +using namespace unix; +std::atomic unix::_isInterrupted = false; + +namespace unix { static thread_local bool interruptThrown = false; -thread_local std::function interruptCheck; +} + +thread_local std::function unix::interruptCheck; void setInterruptThrown() { - interruptThrown = true; + unix::interruptThrown = true; } -void _interrupted() +void unix::_interrupted() { /* Block user interrupts while an 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; @@ -96,7 +101,7 @@ void triggerInterrupt() static sigset_t savedSignalMask; 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 @@ -115,14 +120,14 @@ void setChildSignalMask(sigset_t * sigs) savedSignalMaskIsSet = true; } -void saveSignalMask() { +void unix::saveSignalMask() { if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) throw SysError("querying signal mask"); savedSignalMaskIsSet = true; } -void startSignalHandlerThread() +void unix::startSignalHandlerThread() { updateWindowSize(); @@ -141,7 +146,7 @@ void startSignalHandlerThread() std::thread(signalHandlerThread, set).detach(); } -void restoreSignals() +void unix::restoreSignals() { // If startSignalHandlerThread wasn't called, that means we're not running // in a proper libmain process, but a process that presumably manages its