nodejs relies on pid of dead process to use in hash maps. that is not
entirely safe, but also still fairly reasonable to do. So instead keep
pid, but just set a flag to record that it is dead.
This commit is contained in:
Jameson Nash 2026-03-16 13:18:23 -04:00
parent 95c2ba84d1
commit b15b448e52
5 changed files with 22 additions and 18 deletions

View File

@ -172,12 +172,10 @@ Public members
.. c:member:: int uv_process_t.pid
The PID of the spawned process if it is alive. It is set after calling
:c:func:`uv_spawn` and cleared before calling :c:func:`exit_cb`. The value
is unique only while this is non-zero, otherwise, another process handle
may have already been reassigned the same number before the callback ran.
If you call :c:func:`uv_spawn`
The PID of the spawned process. It is set after calling :c:func:`uv_spawn`
and retains the value even after the process exits. The value is only
unique while the process is alive; after exit, another process may be
reassigned the same PID.
.. note::
The :c:type:`uv_handle_t` members also apply.
@ -272,13 +270,13 @@ API
setgid specified, or not having enough memory to allocate for the new
process.
Whether the call succeeds of fails, you must call :c:func:`uv_close` before
Whether the call succeeds or fails, you must call :c:func:`uv_close` before
freeing the memory of handle (unlike other init function in libuv).
.. warning::
On unix, if `handle->pid != 0` when you call `uv_close`, you will create a
zombie that libuv cannot reap. You are responsible for calling
`waitpid` later. This is not relevant on Windows.
On unix, if the process has not yet exited when you call `uv_close`,
you will create a zombie that libuv cannot reap. You are responsible
for calling `waitpid` later. This is not relevant on Windows.
.. versionchanged:: 1.24.0 Added `UV_PROCESS_WINDOWS_HIDE_CONSOLE` and
`UV_PROCESS_WINDOWS_HIDE_GUI` flags.

View File

@ -145,7 +145,7 @@ void uv__wait_children(uv_loop_t* loop) {
}
assert(pid == process->pid);
process->pid = 0; // pid is no longer valid (or unique)
process->flags |= UV_HANDLE_ESRCH; /* pid is no longer valid (or unique) */
process->status = status;
uv__queue_remove(&process->queue);
uv__queue_insert_tail(&pending, &process->queue);
@ -1101,7 +1101,7 @@ error:
int uv_process_kill(uv_process_t* process, int signum) {
if (process->pid == 0)
if (process->flags & UV_HANDLE_ESRCH)
return UV_ESRCH;
return uv_kill(process->pid, signum);
}
@ -1123,9 +1123,9 @@ int uv_kill(int pid, int signum) {
void uv__process_close(uv_process_t* handle) {
/* Warning: if handle->pid != 0, the caller is creating a zombie that we
* cannot reap. We assume here that it is intentional, and that the user will
* be wise and cleanup later. */
/* Warning: if UV_HANDLE_ESRCH is not set, the caller is creating a zombie
* that we cannot reap. We assume here that it is intentional, and that the
* user will be wise and cleanup later. */
uv__queue_remove(&handle->queue);
uv__handle_stop(handle);
#ifdef UV_USE_SIGCHLD

View File

@ -137,6 +137,7 @@ enum {
UV_HANDLE_POLL_SLOW = 0x01000000,
/* Only used by uv_process_t handles. */
UV_HANDLE_ESRCH = 0x01000000,
UV_HANDLE_REAP = 0x10000000
};

View File

@ -846,7 +846,7 @@ void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
/* Clean-up the process handle eagerly. */
CloseHandle(handle->process_handle);
handle->process_handle = INVALID_HANDLE_VALUE;
handle->pid = 0;
handle->flags |= UV_HANDLE_ESRCH;
/* Set the handle to inactive: no callbacks will be made after the exit
* callback. */
@ -1370,7 +1370,7 @@ static int uv__kill(HANDLE process_handle, int signum) {
int uv_process_kill(uv_process_t* process, int signum) {
int err;
if (process->process_handle == INVALID_HANDLE_VALUE) {
if (process->flags & UV_HANDLE_ESRCH) {
return UV_ESRCH;
}

View File

@ -84,6 +84,8 @@ static void fail_cb(uv_process_t* process,
static void kill_cb(uv_process_t* process,
int64_t exit_status,
int term_signal) {
int err;
printf("exit_cb\n");
exit_cb_called++;
#ifdef _WIN32
@ -104,6 +106,8 @@ static void kill_cb(uv_process_t* process,
uv_close((uv_handle_t*) process, close_cb);
/*
* Sending signum == 0 should check if the
* child process is still alive, not kill it.
* This process should be dead.
*/
err = uv_kill(process->pid, 0);
@ -200,12 +204,13 @@ TEST_IMPL(spawn_fails) {
#ifndef _WIN32
TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) {
int r;
int status;
int err;
init_process_options("", fail_cb);
options.file = options.args[0] = "program-that-had-better-not-exist";
r = uv_spawn(uv_default_loop(), &process, &options);
ASSERT(process.pid == 0);
ASSERT(r == UV_ENOENT || r == UV_EACCES);
ASSERT_OK(uv_is_active((uv_handle_t*) &process));
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));