fix(process): wrong exit code for SIGHUP on Windows (#38242)

Problem:
When stopping a PTY process on Windows, the exit code indicates that the
process is stopped by SIGTERM even when closing all streams is enough to
terminate the process. This is inconsistent with other platforms.

Solution:
Set exit_signal to SIGHUP instead of SIGTERM when using SIGHUP.
This commit is contained in:
zeertzjq
2026-03-12 06:45:22 +08:00
committed by GitHub
parent 8aacce90b4
commit 974bc1b044
2 changed files with 35 additions and 22 deletions

View File

@@ -242,14 +242,15 @@ void proc_stop(Proc *proc) FUNC_ATTR_NONNULL_ALL
return;
}
proc->stopped_time = os_hrtime();
proc->exit_signal = SIGTERM;
switch (proc->type) {
case kProcTypeUv:
proc->exit_signal = SIGTERM;
os_proc_tree_kill(proc->pid, SIGTERM);
break;
case kProcTypePty:
// close all streams for pty processes to send SIGHUP to the process
proc->exit_signal = SIGHUP;
proc_close_streams(proc);
pty_proc_close_master((PtyProc *)proc);
break;

View File

@@ -2828,6 +2828,18 @@ describe('API', function()
eq(info, eval('rpcrequest(3, "nvim_get_chan_info", 0)'))
end)
local function term_channel_info(id, buffer, argv)
return {
stream = 'job',
id = id,
argv = argv,
mode = 'terminal',
buffer = buffer,
pty = '?',
exitcode = -1,
}
end
it('stream=job :terminal channel', function()
local screen = Screen.new(80, 24)
@@ -2835,15 +2847,7 @@ describe('API', function()
eq(1, api.nvim_get_current_buf())
eq(3, api.nvim_get_option_value('channel', { buf = 1 }))
local info = {
stream = 'job',
id = 3,
argv = { eval('exepath(&shell)') },
mode = 'terminal',
buffer = 1,
pty = '?',
exitcode = -1,
}
local info = term_channel_info(3, 1, { eval('exepath(&shell)') })
local event = api.nvim_get_var('opened_event')
if not is_os('win') then
info.pty = event.info.pty
@@ -2854,7 +2858,7 @@ describe('API', function()
eq({ [1] = testinfo, [2] = stderr, [3] = info }, api.nvim_list_chans())
eq(info, api.nvim_get_chan_info(3))
-- :terminal with args + running process.
-- :terminal with args + running process (Nvim TUI).
-- Don't use a shell here, so that SIGHUP handling doesn't depend on the shell.
command('enew')
local argv = { n.nvim_prog, '-u', 'NONE', '-i', 'NONE' }
@@ -2863,15 +2867,7 @@ describe('API', function()
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running?
local expected2 = {
stream = 'job',
id = 4,
argv = argv,
mode = 'terminal',
buffer = 2,
pty = '?',
exitcode = -1,
}
local expected2 = term_channel_info(4, 2, argv)
local actual2 = eval('nvim_get_chan_info(&channel)')
expected2.pty = actual2.pty
eq(expected2, actual2)
@@ -2879,12 +2875,28 @@ describe('API', function()
-- Make sure Nvim TUI is started (which is after registering SIGHUP handler).
screen:expect({ any = 'Nvim is open source and freely distributable' })
-- :terminal with args + stopped process.
-- :terminal with args + stopped process (Nvim TUI).
eq(1, eval('jobstop(&channel)'))
eval('jobwait([&channel], 1000)') -- Wait.
expected2.pty = (is_os('win') and '?' or '') -- pty stream was closed.
expected2.exitcode = (is_os('win') and 143 or 1)
-- On Unix, SIGHUP is handled by Nvim TUI, so exit code is 1.
-- On Windows, even though Nvim TUI handles SIGHUP, it's not possible for the
-- parent process to know that, so exit code reflects SIGHUP.
expected2.exitcode = (is_os('win') and 129 or 1)
eq(expected2, eval('nvim_get_chan_info(&channel)'))
-- :terminal with args + stopped process (shell-test).
command('enew')
argv = { n.testprg('shell-test'), 'INTERACT' }
fn.jobstart(argv, { term = true })
screen:expect({ any = { vim.pesc('interact $') } })
eq(1, eval('jobstop(&channel)'))
eval('jobwait([&channel], 1000)') -- Wait.
local expected3 = term_channel_info(5, 3, argv)
expected3.pty = (is_os('win') and '?' or '') -- pty stream was closed.
-- Exit code should reflect SIGHUP as shell-test doesn't handle it.
expected3.exitcode = 129
eq(expected3, eval('nvim_get_chan_info(&channel)'))
end)
end)