diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2016-10-12 00:04:07 +0200 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2016-10-12 04:33:53 +0200 |
commit | b182f247ecf5d49558aae165ba248e2240d3a5d7 (patch) | |
tree | 7f5cf775fed6d67a9e263d71e8e7dbd190305216 | |
parent | ef4676ed5bd18fc2d5fb52b52304d59d94e9e806 (diff) | |
download | rneovim-b182f247ecf5d49558aae165ba248e2240d3a5d7.tar.gz rneovim-b182f247ecf5d49558aae165ba248e2240d3a5d7.tar.bz2 rneovim-b182f247ecf5d49558aae165ba248e2240d3a5d7.zip |
eval/term_write(): Skip writes if stream was closed.
If the backing stream for a :terminal was closed (e.g. if the shell exits
unexpectedly) there may be pending input on the loop which will be processed
before the terminal close event (which is queued on the same loop).
terminal_send checks term->closed but this does not reflect the state of the
underlying streams. The terminal.c module in fact has no knowledge of the
streams (this seems intentional: it is abstracted as TerminalOption.write_cb).
The SIGCHLD handler (pty_process_unix.c) is executed immediately, and it
triggers a stream teardown so Stream.closed=false (TerminalJobData.in.closed).
When the pending writes are handled by eval.c:term_write, wstream_write() aborts
because it sees the closed Stream.
To avoid that, this commit checks Stream.closed in eval:term_write() before writing
to the WStream. (As hinted above, we cannot do this in terminal:terminal_send()
because that module cannot inspect the underlying streams.)
References #5445
https://github.com/neovim/neovim/pull/5445#issuecomment-252529766
-rw-r--r-- | src/nvim/eval.c | 16 |
1 files changed, 11 insertions, 5 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0b1fd6670e..ac8b675834 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -405,7 +405,7 @@ typedef struct { LibuvProcess uv; PtyProcess pty; } proc; - Stream in, out, err; + Stream in, out, err; // Initialized in common_job_start(). Terminal *term; bool stopped; bool exited; @@ -21739,7 +21739,7 @@ static inline TerminalJobData *common_job_init(char **argv, if (!pty) { proc->err = &data->err; } - proc->cb = on_process_exit; + proc->cb = eval_job_process_exit_cb; proc->events = data->events; proc->detach = detach; proc->cwd = cwd; @@ -21923,7 +21923,7 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, rbuffer_consumed(buf, count); } -static void on_process_exit(Process *proc, int status, void *d) +static void eval_job_process_exit_cb(Process *proc, int status, void *d) { TerminalJobData *data = d; if (data->term && !data->exited) { @@ -21947,9 +21947,15 @@ static void on_process_exit(Process *proc, int status, void *d) static void term_write(char *buf, size_t size, void *d) { - TerminalJobData *data = d; + TerminalJobData *job = d; + if (job->in.closed) { + // If the backing stream was closed abruptly, there may be write events + // ahead of the terminal close event. Just ignore the writes. + ILOG("write failed: stream is closed"); + return; + } WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); - wstream_write(&data->in, wbuf); + wstream_write(&job->in, wbuf); } static void term_resize(uint16_t width, uint16_t height, void *d) |