about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-03-29T13·08+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-03-29T14·37+0200
commitab3ce1cc13153b2053302cdb710cb411b0b9d84e (patch)
tree1548ea7b5316f7793cdb44d0da2642dafa1d4441 /src
parent4f34c403980e7f5ae3d5257b742af6fd6452c6cf (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/libmain/shared.cc11
-rw-r--r--src/libutil/thread-pool.cc7
-rw-r--r--src/libutil/util.cc6
-rw-r--r--src/libutil/util.hh2
4 files changed, 12 insertions, 14 deletions
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index e264832113..ed997052be 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<void()> 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 819aed7483..743038b588 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<Interrupted*>(&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 d4ac3fb603..55d4909921 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 0a72cc5922..20bd62a0e7 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()