diff --git a/src/nvim/event/proc.c b/src/nvim/event/proc.c index 01fe62d132..eb8f2690a2 100644 --- a/src/nvim/event/proc.c +++ b/src/nvim/event/proc.c @@ -368,20 +368,27 @@ static void flush_stream(Proc *proc, RStream *stream) return; } - // Maximal remaining data size of terminated process is system - // buffer size. - // Also helps with a child process that keeps the output streams open. If it - // keeps sending data, we only accept as much data as the system buffer size. - // Otherwise this would block cleanup/teardown. - int system_buffer_size = 0; - int err = uv_recv_buffer_size((uv_handle_t *)&stream->s.uv.pipe, - &system_buffer_size); - if (err) { - system_buffer_size = ARENA_BLOCK_SIZE; + size_t max_bytes = SIZE_MAX; +#ifdef MSWIN + if (true) { +#else + // Don't limit remaining data size of PTY master unless when tearing down, as it may + // have more remaining data than system buffer size (at least on Linux). #3030 + if (proc->type != kProcTypePty || proc_is_tearing_down) { +#endif + // Maximal remaining data size of terminated process is system buffer size. + // Also helps with a child process that keeps the output streams open. If it + // keeps sending data, we only accept as much data as the system buffer size. + // Otherwise this would block cleanup/teardown. + int system_buffer_size = 0; + // All members of the stream->s.uv union share the same address. + int err = uv_recv_buffer_size((uv_handle_t *)&stream->s.uv, &system_buffer_size); + if (err != 0) { + system_buffer_size = ARENA_BLOCK_SIZE; + } + max_bytes = stream->num_bytes + (size_t)system_buffer_size; } - size_t max_bytes = stream->num_bytes + (size_t)system_buffer_size; - // Read remaining data. while (!stream->s.closed && stream->num_bytes < max_bytes) { // Remember number of bytes before polling diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 08da133819..9eac77fc37 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -32,12 +32,14 @@ static void help(void) puts(" shell-test -t {prompt text} EXE \"prog args...\""); puts(" Prints \"{prompt text} $ progs args...\" to stderr."); puts(" shell-test REP N {text}"); - puts(" Prints \"{lnr}: {text}\\n\" to stdout N times, taking N milliseconds."); + puts(" Prints \"{lnr}: {text}\\n\" to stdout N times, pausing every 100 lines."); puts(" Example:"); puts(" shell-test REP 97 \"foo bar\""); puts(" 0: foo bar"); puts(" ..."); puts(" 96: foo bar"); + puts(" shell-test REPFAST N {text}"); + puts(" Like REP, but print as fast as possible and then exit immediately."); puts(" shell-test INTERACT"); puts(" Prints \"interact $ \" to stderr, and waits for \"exit\" input."); puts(" shell-test EXIT {code}"); @@ -71,9 +73,10 @@ int main(int argc, char **argv) } else { fprintf(stderr, "ready $ "); } - } else if (strcmp(argv[1], "REP") == 0) { + } else if (strcmp(argv[1], "REP") == 0 || strcmp(argv[1], "REPFAST") == 0) { + bool fast = strcmp(argv[1], "REPFAST") == 0; if (argc != 4) { - fprintf(stderr, "REP expects exactly 3 arguments\n"); + fprintf(stderr, "REP/REPFAST expects exactly 3 arguments\n"); return 4; } int count = 0; @@ -83,7 +86,7 @@ int main(int argc, char **argv) } for (int i = 0; i < count; i++) { printf("%d: %s\n", i, argv[3]); - if (i % 100 == 0) { + if (!fast && i % 100 == 0) { usleep(1000); // Wait 1 ms (simulate typical output). } fflush(NULL); diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index f56f7377d0..7c6b76c08f 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -758,6 +758,25 @@ describe(':terminal buffer', function() ]]) end) + it('does not drop data when job exits immediately after output #3030', function() + local screen = Screen.new(50, 7) + api.nvim_create_autocmd('TermClose', { command = 'let g:did_termclose = 1' }) + fn.jobstart({ testprg('shell-test'), 'REPFAST', '20000', 'TEST' }, { term = true }) + retry(nil, nil, function() + eq(1, api.nvim_get_var('did_termclose')) + end) + feed('i') + screen:expect([[ + 19996: TEST | + 19997: TEST | + 19998: TEST | + 19999: TEST | + | + [Process exited 0]^ | + {5:-- TERMINAL --} | + ]]) + end) + it('handles unprintable chars', function() local screen = Screen.new(50, 7) feed 'i'