Repository: pantsbuild/pants

Issue:
[pantsd] sending ctrl-\ in a `./pants repl` can lead to a hung pantsd-runner
repro:

```
[omerta pants-release (master)]$ ps -ef |grep pantsd-runner |grep -v grep
[omerta pants-release (master)]$ ./pants -q repl 3rdparty/python:psutil

Python 2.7.10 (default, Dec 16 2015, 14:09:45) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> ^\Quit: 3
[omerta pants-release (master)]$ ps -ef |grep pantsd-runner |grep -v grep
  501 67669     1   0 10:37PM ??         0:01.14 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67670 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67671 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67672 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67673 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67674 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67675 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67676 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67677 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
[omerta pants-release (master)]$ 
```

we'll want to better handle `SIGQUIT` in the thin client side of the runner to avoid this.


Discussion:

Resolving patch:
diff --git a/src/python/pants/bin/remote_pants_runner.py b/src/python/pants/bin/remote_pants_runner.py
--- a/src/python/pants/bin/remote_pants_runner.py
+++ b/src/python/pants/bin/remote_pants_runner.py
@@ -55,17 +55,25 @@ def pantsd(self):
     return PantsDaemon.Factory.create(bootstrap_options=self._bootstrap_options)
 
   @contextmanager
-  def _trapped_control_c(self, client):
-    """A contextmanager that overrides the SIGINT (control-c) handler and handles it remotely."""
+  def _trapped_signals(self, client):
+    """A contextmanager that overrides the SIGINT (control-c) and SIGQUIT (control-\) handlers
+    and handles them remotely."""
     def handle_control_c(signum, frame):
       client.send_control_c()
 
     existing_sigint_handler = signal.signal(signal.SIGINT, handle_control_c)
-    signal.siginterrupt(signal.SIGINT, False)  # Retry interrupted system calls.
+    # N.B. SIGQUIT will abruptly kill the pantsd-runner, which will shut down the other end
+    # of the Pailgun connection - so we send a gentler SIGINT here instead.
+    existing_sigquit_handler = signal.signal(signal.SIGQUIT, handle_control_c)
+
+    # Retry interrupted system calls.
+    signal.siginterrupt(signal.SIGINT, False)
+    signal.siginterrupt(signal.SIGQUIT, False)
     try:
       yield
     finally:
       signal.signal(signal.SIGINT, existing_sigint_handler)
+      signal.signal(signal.SIGQUIT, existing_sigquit_handler)
 
   def _setup_logging(self):
     """Sets up basic stdio logging for the thin client."""
@@ -94,7 +102,7 @@ def _connect_and_execute(self, port):
                            err=self._stderr,
                            exit_on_broken_pipe=True)
 
-    with self._trapped_control_c(client):
+    with self._trapped_signals(client):
       # Execute the command on the pailgun.
       result = client.execute(self.PANTS_COMMAND, *self._args, **modified_env)
 


Test patch: