interactive shells embedded inside non-interactive shells can become deadlocked #14762
Description
Describe the bug
When a non-interactive instance of nushell tries to run an interactive instance of nushell, both the child process and the parent can become deadlocked waiting for each other.
I noticed this with the tests added in 4884894, which have since been fixed with a workaround, but I still think this is a legitimate bug in and of itself that should be handled more gracefully.
After some digging, I think I found the source of the deadlock. When a non-interactive instance tries to write input to a child process through a byte stream, it will copy all input into the child process's pipe, and then simply wait for the child process to die.
nushell/crates/nu-protocol/src/pipeline/byte_stream.rs
Lines 614 to 631 in b60f91f
Meanwhile, the child nushell instance, in interactive mode, will attempt to acquire the terminal.
Lines 93 to 115 in b60f91f
If it so happens that another process in the same process group currently owns the terminal, it will freeze itself with SIGTTIN
, which stops the process.
I can see that the code is using the GNU documentation for implementing job control as a reference, and indeed, the code seems to follow this manual pretty closely.
However, what's not made clear is how the child process is expected to be woken up. It seems like one of the parent processes is supposed to be giving the child process the terminal, so perhaps it is supposed to wake up the child process as well?
How to reproduce
I was able to come up with a test case that triggers this bug pretty easily:
1..20 | par-each -t 10 { print $"start ($in)"; nu --no-std-lib --no-config-file -c $"nu -i -c 'print ($in)'"; print $"end ($in)" }
Expected behavior
I'm really not sure what the correct behavior is, since the parent is non-interactive itself, and therefore does not have the terminal.
Configuration
key | value |
---|---|
version | 0.101.0 |
major | 0 |
minor | 101 |
patch | 0 |
branch | |
commit_hash | |
build_os | linux-x86_64 |
build_target | aarch64-linux-android |
rust_version | rustc 1.81.0 (eeb90cda1 2024-09-04) |
rust_channel | 1.81.0-x86_64-unknown-linux-gnu |
cargo_version | cargo 1.81.0 (2dbb1af80 2024-08-20) |
build_time | 2024-12-23 00:36:54 +00:00 |
build_rust_channel | release |
allocator | mimalloc |
features | default, sqlite, trash |
installed_plugins | dns 3.0.7-alpha.1, polars 0.98.0 |