input: set input stream to blocking on exit

If stdin is non-blocking, many tools (e.g. cat(1), read(1)) which assume
that stdin is blocking, will break in odd ways:

  read: read error: 0: Resource temporarily unavailable
  cat: -: Resource temporarily unavailable
  rm: error closing file

libuv puts stdin in nonblocking mode, and leaves it that way at exit
(this is apparently by design). So, before this commit, this always
works (because the shell clobbers O_NONBLOCK):

  $ nvim --cmd q
  $ read

...but these forms do _not_ work:

  $ nvim --cmd q && read
  $ echo foo | nvim --cmd q && read
  $ nvim && read

After this commit, all of the above forms work.

Background:

437b4397b9 (diff-41f4d294430cd8c36538999d62681ae2)
https://github.com/fish-shell/fish-shell/issues/176#issuecomment-15800155

- bash (and other shells: zsh, tcsh, fish), upon returning to the
  foreground, always sets fd 0 back to blocking mode. This practice only
  applies to stdin, _not_ stdout or stderr (in practice these fds may be
  affected anyways).
- bash/zsh/tcsh/fish do _not_ restore the non-blocking status of stdin
  when _resuming a job_.
- We do _not_ save/restore the original flags visible to
  fcntl(F_[SG]ETFL), because (counterintuitively) that isn't expected.

Helped-by: oni-link <knil.ino@gmail.com>

Closes #2086
Closes #2377

---

Note: The following implementation of stream_set_blocking() was
discarded, because it resulted in a failed libuv assertion[1]:

  int stream_set_blocking(int fd, bool blocking)
  {
    uv_pipe_t stream;
    uv_pipe_init(uv_default_loop(), &stream, 0);
    uv_pipe_open(&stream, fd);
    int retval = uv_stream_set_blocking((uv_stream_t *)&stream, blocking);
    uv_close((uv_handle_t *)&stream, NULL);
    return retval;
  }

[1] .deps/build/src/libuv/src/unix/core.c:833: uv__io_stop: Assertion `loop->watchers[w->fd] == w' failed.
This commit is contained in:
Justin M. Keyes
2015-05-21 09:45:46 -04:00
parent 5a9ad68b25
commit 8a782f1699
8 changed files with 50 additions and 9 deletions

View File

@@ -8,6 +8,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/os/input.h"
#include "nvim/os/event.h"
#include "nvim/os/os.h"
#include "nvim/os/rstream_defs.h"
#include "nvim/os/rstream.h"
#include "nvim/ascii.h"
@@ -61,9 +62,14 @@ void input_start_stdin(int fd)
void input_stop_stdin(void)
{
if (!read_stream) {
// In some cases (i.e. "nvim && read") where we do not explicitly play with
// std{in,out,err}, some other module/library nevertheless sets the stream
// to non-blocking; we still must "fix" the stream (#2598) in those cases.
stream_set_blocking(global_input_fd, true); // normalize stream (#2598)
return;
}
rstream_set_blocking(read_stream, true); // normalize stream (#2598)
rstream_stop(read_stream);
rstream_free(read_stream);
read_stream = NULL;