diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index d9b360a570..1c3772d4c0 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -35,6 +35,7 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" +#include "nvim/os/time.h" #include "nvim/strings.h" #include "nvim/tui/input.h" #include "nvim/tui/termdef_field_defs.h" @@ -765,9 +766,10 @@ void tui_stop(TUIData *tui) terminfo_disable(tui); // Wait until DA1 response is received, or stdin is closed (#35744). + uint64_t wait_start = os_hrtime(); LOOP_PROCESS_EVENTS_UNTIL(tui->loop, tui->loop->events, EXIT_TIMEOUT_MS, tui->stopped || tui->input.read_stream.did_eof); - if (!tui->stopped && !tui->input.read_stream.did_eof) { + if (!tui->stopped && (os_hrtime() - wait_start) / 1000000 >= EXIT_TIMEOUT_MS) { WLOG("TUI: timed out waiting for DA1 response"); } tui->stopped = true; diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 1e216fb7c5..25e2475caa 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -3047,17 +3047,6 @@ describe('TUI', function() screen:expect({ any = vim.pesc('[Process exited 1]') }) end) - it('exits immediately when stdin is closed #35744', function() - local chan = api.nvim_get_option_value('channel', { buf = 0 }) - local pid = fn.jobpid(chan) - fn.chanclose(chan) - retry(nil, 50, function() - eq(vim.NIL, api.nvim_get_proc(pid)) - end) - -- FIXME: SIGHUP sometimes isn't caught with ASAN. - screen:expect({ any = t.is_asan() and '%[Process exited %d+%]' or '%[Process exited 1%]' }) - end) - it('exits properly when :quit non-last window in event handler #14379', function() local code = [[ vim.defer_fn(function() @@ -3663,6 +3652,28 @@ describe('TUI', function() end end) end) + + it('exits immediately when stdin is closed #35744', function() + local screen = tt.setup_child_nvim( + { '--clean', '--cmd', 'set laststatus=2' }, + { env = vim.tbl_extend('force', env_notermguicolors, { NVIM_LOG_FILE = testlog }) } + ) + finally(function() + os.remove(testlog) + end) + screen:expect({ any = '%[No Name%]' }) + + local chan = api.nvim_get_option_value('channel', { buf = 0 }) + local pid = fn.jobpid(chan) + fn.chanclose(chan) + retry(nil, 2000, function() + eq(vim.NIL, api.nvim_get_proc(pid)) + end) + -- FIXME: SIGHUP sometimes isn't caught with ASAN. + screen:expect({ any = t.is_asan() and '%[Process exited %d+%]' or '%[Process exited 1%]' }) + -- Closing stdin must skip the DA1 wait. + t.assert_nolog('timed out waiting for DA1 response', testlog, 100) + end) end) describe('TUI UIEnter/UILeave', function()