Improve SIGINT handling in multi-threaded programs

The flag remembering whether an Interrupted exception was thrown is
now thread-local. Thus, all threads will (eventually) throw
Interrupted. Previously, one thread would throw Interrupted, and then
the other threads wouldn't see that they were supposed to quit.
This commit is contained in:
Eelco Dolstra 2016-03-29 15:08:24 +02:00
parent 4f34c40398
commit ab3ce1cc13
4 changed files with 12 additions and 14 deletions

View file

@ -24,15 +24,9 @@
namespace nix { namespace nix {
volatile sig_atomic_t blockInt = 0;
static void sigintHandler(int signo) static void sigintHandler(int signo)
{ {
if (!blockInt) {
_isInterrupted = 1; _isInterrupted = 1;
blockInt = 1;
}
} }
@ -287,8 +281,7 @@ int handleExceptions(const string & programName, std::function<void()> fun)
condition is discharged before we reach printMsg() condition is discharged before we reach printMsg()
below, since otherwise it will throw an (uncaught) below, since otherwise it will throw an (uncaught)
exception. */ exception. */
blockInt = 1; /* ignore further SIGINTs */ interruptThrown = true;
_isInterrupted = 0;
throw; throw;
} }
} catch (Exit & e) { } catch (Exit & e) {

View file

@ -55,9 +55,10 @@ void ThreadPool::process()
work(); work();
} catch (std::exception & e) { } catch (std::exception & e) {
auto state_(state.lock()); auto state_(state.lock());
if (state_->exception) if (state_->exception) {
if (!dynamic_cast<Interrupted*>(&e))
printMsg(lvlError, format("error: %s") % e.what()); printMsg(lvlError, format("error: %s") % e.what());
else { } else {
state_->exception = std::current_exception(); state_->exception = std::current_exception();
wakeup.notify_all(); wakeup.notify_all();
} }

View file

@ -1062,13 +1062,15 @@ void restoreSIGPIPE()
volatile sig_atomic_t _isInterrupted = 0; volatile sig_atomic_t _isInterrupted = 0;
thread_local bool interruptThrown = false;
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_exception()) { if (!interruptThrown && !std::uncaught_exception()) {
_isInterrupted = 0; interruptThrown = true;
throw Interrupted("interrupted by the user"); throw Interrupted("interrupted by the user");
} }
} }

View file

@ -316,6 +316,8 @@ void restoreSIGPIPE();
extern volatile sig_atomic_t _isInterrupted; extern volatile sig_atomic_t _isInterrupted;
extern thread_local bool interruptThrown;
void _interrupted(); void _interrupted();
void inline checkInterrupt() void inline checkInterrupt()