From ab3ce1cc13153b2053302cdb710cb411b0b9d84e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 29 Mar 2016 15:08:24 +0200 Subject: [PATCH] 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. --- src/libmain/shared.cc | 11 ++--------- src/libutil/thread-pool.cc | 7 ++++--- src/libutil/util.cc | 6 ++++-- src/libutil/util.hh | 2 ++ 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index e26483211..ed997052b 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -24,15 +24,9 @@ namespace nix { -volatile sig_atomic_t blockInt = 0; - - static void sigintHandler(int signo) { - if (!blockInt) { - _isInterrupted = 1; - blockInt = 1; - } + _isInterrupted = 1; } @@ -287,8 +281,7 @@ int handleExceptions(const string & programName, std::function fun) condition is discharged before we reach printMsg() below, since otherwise it will throw an (uncaught) exception. */ - blockInt = 1; /* ignore further SIGINTs */ - _isInterrupted = 0; + interruptThrown = true; throw; } } catch (Exit & e) { diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 819aed748..743038b58 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -55,9 +55,10 @@ void ThreadPool::process() work(); } catch (std::exception & e) { auto state_(state.lock()); - if (state_->exception) - printMsg(lvlError, format("error: %s") % e.what()); - else { + if (state_->exception) { + if (!dynamic_cast(&e)) + printMsg(lvlError, format("error: %s") % e.what()); + } else { state_->exception = std::current_exception(); wakeup.notify_all(); } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index d4ac3fb60..55d490992 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1062,13 +1062,15 @@ void restoreSIGPIPE() volatile sig_atomic_t _isInterrupted = 0; +thread_local bool interruptThrown = false; + void _interrupted() { /* Block user interrupts while an exception is being handled. Throwing an exception while another exception is being handled kills the program! */ - if (!std::uncaught_exception()) { - _isInterrupted = 0; + if (!interruptThrown && !std::uncaught_exception()) { + interruptThrown = true; throw Interrupted("interrupted by the user"); } } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 0a72cc592..20bd62a0e 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -316,6 +316,8 @@ void restoreSIGPIPE(); extern volatile sig_atomic_t _isInterrupted; +extern thread_local bool interruptThrown; + void _interrupted(); void inline checkInterrupt()