Repository: xonsh/xonsh

Issue:
`pv /dev/urandom | head` kills terminal
## xonfig

```
+------------------+---------------------+
| xonsh            | 0.8.4               |
| Git SHA          | 8405b8dd            |
| Commit Date      | Dec 7 10:39:36 2018 |
| Python           | 3.6.7               |
| PLY              | 3.11                |
| have readline    | True                |
| prompt toolkit   | 2.0.7               |
| shell type       | prompt_toolkit2     |
| pygments         | 2.2.0               |
| on posix         | True                |
| on linux         | True                |
| distro           | unknown             |
| on darwin        | False               |
| on windows       | False               |
| on cygwin        | False               |
| on msys2         | False               |
| is superuser     | False               |
| default encoding | utf-8               |
| xonsh encoding   | utf-8               |
| encoding errors  | surrogateescape     |
+------------------+---------------------+
```

## Expected Behavior

`pv /dev/urandom | head` doesn't crash the terminal

## Current Behavior

`pv /dev/urandom | head` crashes the terminal, leading to the window getting closed

## Steps to Reproduce

```
sudo apt install pv -y
pv /dev/urandom | head
```


Discussion:
Well, that's a weird one.  Thanks for reporting @pt12lol !
For the moment, you can work around this by adding `pv` to the `threadable_predictors` in your `xonshrc` 

```
__xonsh_commands_cache__.threadable_predictors['pv'] = lambda args: False
```

If you want to tackle fixing this properly you can add it to `xonsh/commands_cache.py` and mark `pv` explicitly as a `predict_false`.

Yup definitively weird. Can confirm!
I think this might be throwing recursive exceptions:

```
scopatz@artemis ~ $ cat /dev/urandom | pv | less                                                                                                                                   
^C64.0KiB 0:00:07 [8.66KiB/s] [  <=>                                                                                                                                                ]
xonsh: To log full traceback to a file set: $XONSH_TRACEBACK_LOGFILE = <filename>
Traceback (most recent call last):
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9447, in wait_for_active_job
    _, wcode = os.waitpid(obj.pid, os.WUNTRACED)
ChildProcessError: [Errno 10] No child processes

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9447, in wait_for_active_job
    _, wcode = os.waitpid(obj.pid, os.WUNTRACED)
ChildProcessError: [Errno 10] No child processes

During handling of the above exception, another exception occurred:

....
 
Traceback (most recent call last):
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 16550, in default
    run_compiled_code(code, self.ctx, None, "single")
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 2456, in run_compiled_code
    func(code, glb, loc)
  File "<xonsh-code>", line 1, in <module>
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 21594, in subproc_captured_hiddenobject
    return run_subproc(cmds, captured="hiddenobject")
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 21557, in run_subproc
    command.end()
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 14086, in end
    self._end(tee_output=tee_output)
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 14094, in _end
    for _ in self.tee_stdout():
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 14007, in tee_stdout
    for line in self.iterraw():
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 13893, in iterraw
    task = wait_for_active_job()
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9449, in wait_for_active_job
    return wait_for_active_job(last_task=active_task, backgrounded=backgrounded)
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9449, in wait_for_active_job
    return wait_for_active_job(last_task=active_task, backgrounded=backgrounded)
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9449, in wait_for_active_job
    return wait_for_active_job(last_task=active_task, backgrounded=backgrounded)
  [Previous line repeated 981 more times]
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9439, in wait_for_active_job
    _clear_dead_jobs()
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 9487, in _clear_dead_jobs
    if obj is None or obj.poll() is not None:
  File "/home/scopatz/miniconda/lib/python3.6/subprocess.py", line 855, in poll
    return self._internal_poll()
RecursionError: maximum recursion depth exceeded

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 23285, in main
    return main_xonsh(args)
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 23314, in main_xonsh
    shell.shell.cmdloop()
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/ptk2/shell.py", line 194, in cmdloop
    self.default(line)
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 16559, in default
    print_exception()
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 4814, in print_exception
    traceback.print_exc()
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 163, in print_exc
    print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 104, in print_exception
    type(value), value, tb, limit=limit).format(chain=chain):
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 498, in __init__
    _seen=_seen)
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 498, in __init__
    _seen=_seen)
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 498, in __init__
    _seen=_seen)
  [Previous line repeated 244 more times]
RecursionError: maximum recursion depth exceeded

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/scopatz/.local/bin/xonsh", line 3, in <module>
    main()
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 23287, in main
    _failback_to_other_shells(args, err)
  File "/home/scopatz/.local/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 23273, in _failback_to_other_shells
    traceback.print_exc()
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 163, in print_exc
    print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 104, in print_exception
    type(value), value, tb, limit=limit).format(chain=chain):
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 498, in __init__
    _seen=_seen)
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 498, in __init__
    _seen=_seen)
  File "/home/scopatz/miniconda/lib/python3.6/traceback.py", line 498, in __init__
    _seen=_seen)
  [Previous line repeated 245 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object

```
I am not sure about what is going on here.  I think the previous traceback is a red herring.  It seems that this only really happens with `/dev/urandom` and `head`. Other commands to pipe into (`less`, `tail`) work. Other files, even `/dev/random` work.  

Maybe EOFs are being generated which close the file prematurely and then head is passing that on to the terminal.  I say this because `pv /dev/urandom | head | less` seems to more or less work too.
Also, `pv /dev/urandom | head o>out.txt` works just fine.
Also `$[pv /dev/urandom | head]` works.

Resolving patch:
diff --git a/xonsh/jobs.py b/xonsh/jobs.py
--- a/xonsh/jobs.py
+++ b/xonsh/jobs.py
@@ -180,7 +180,9 @@ def give_terminal_to(pgid):
             else:
                 raise
         finally:
-            _pthread_sigmask(signal.SIG_SETMASK, oldmask)
+            if oldmask:
+                # only reset the mask if it is non-empty! See #2989
+                _pthread_sigmask(signal.SIG_SETMASK, oldmask)
 
     def wait_for_active_job(last_task=None, backgrounded=False):
         """


Test patch: