libstore: check for interrupts in parallel promise

this simplifies the worker loop, and lets us remove it entirely later.
note that ideally only one promise waiting for interrupts should exist
in the entire system. not one per event loop, one per *process*. extra
interrupt waiters make interrupt response nondeterministic and as such
aren't great for user experience. if anything wants to react to aborts
caused by explicit interruptions, or anything else, those things would
be much better served using RAII guards such as Finally (or KJ_DEFER).

Change-Id: I41d035ff40172d536e098153c7375b0972110d51
This commit is contained in:
eldritch horrors 2024-10-05 00:38:35 +02:00
parent 896a123605
commit 99edc2ae38
3 changed files with 16 additions and 5 deletions

View file

@ -294,7 +294,13 @@ std::vector<GoalPtr> Worker::run(std::function<Targets (GoalFactory &)> req)
topGoals.insert(goal); topGoals.insert(goal);
} }
auto promise = runImpl().exclusiveJoin(updateStatistics()); auto onInterrupt = kj::newPromiseAndCrossThreadFulfiller<Result<void>>();
auto interruptCallback = createInterruptCallback([&] {
return result::failure(std::make_exception_ptr(makeInterrupted()));
});
auto promise =
runImpl().exclusiveJoin(updateStatistics()).exclusiveJoin(std::move(onInterrupt.promise));
// TODO GC interface? // TODO GC interface?
if (auto localStore = dynamic_cast<LocalStore *>(&store); localStore && settings.minFree != 0) { if (auto localStore = dynamic_cast<LocalStore *>(&store); localStore && settings.minFree != 0) {
@ -316,9 +322,6 @@ try {
debug("entered goal loop"); debug("entered goal loop");
while (1) { while (1) {
checkInterrupt();
if (topGoals.empty()) break; if (topGoals.empty()) break;
/* Wait for input. */ /* Wait for input. */

View file

@ -12,13 +12,18 @@ std::atomic<bool> _isInterrupted = false;
thread_local std::function<bool()> interruptCheck; thread_local std::function<bool()> interruptCheck;
Interrupted makeInterrupted()
{
return Interrupted("interrupted by the user");
}
void _interrupted() void _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
kills the program! */ kills the program! */
if (!std::uncaught_exceptions()) { if (!std::uncaught_exceptions()) {
throw Interrupted("interrupted by the user"); throw makeInterrupted();
} }
} }

View file

@ -16,10 +16,13 @@ namespace nix {
/* User interruption. */ /* User interruption. */
class Interrupted;
extern std::atomic<bool> _isInterrupted; extern std::atomic<bool> _isInterrupted;
extern thread_local std::function<bool()> interruptCheck; extern thread_local std::function<bool()> interruptCheck;
Interrupted makeInterrupted();
void _interrupted(); void _interrupted();
void inline checkInterrupt() void inline checkInterrupt()