mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-29 17:16:15 +02:00
Better signals interface
This avoids some CPP and accidentally using Unix stuff in client code.
This commit is contained in:
parent
9d03c2b08b
commit
50f621b241
11 changed files with 134 additions and 51 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -260,9 +260,9 @@ struct curlFileTransfer : public FileTransfer
|
||||||
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,
|
||||||
|
|
|
@ -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
70
src/libutil/signals.hh
Normal 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"
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue