Skip to content

fix(process): allow unref'd child processes to outlive parent#32563

Merged
bartlomieju merged 3 commits intomainfrom
fix/child-process-unref-kill-on-drop
Mar 17, 2026
Merged

fix(process): allow unref'd child processes to outlive parent#32563
bartlomieju merged 3 commits intomainfrom
fix/child-process-unref-kill-on-drop

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

Summary

  • Fixes child_process.unref() killing the child process when the parent exits, instead of letting it continue running independently (Node.js behavior)
  • Replaces tokio's kill_on_drop(true) with a custom Drop on ChildResource that respects a togglable kill_on_drop flag
  • Adds op_spawn_child_ref / op_spawn_child_unref ops to control the flag from JS ref()/unref()
  • The Node.js child_process polyfill delegates to Deno.ChildProcess, so it gets the fix automatically

Test plan

  • New spec test child_process_unref_survives — parent spawns child, calls unref(), exits; verifies child wrote a marker file
  • All existing child_process spec tests pass (18/18)
  • cargo check, format, and lint pass

Closes #21446

🤖 Generated with Claude Code

Previously, calling `unref()` on a child process spawned via
`node:child_process` or `Deno.ChildProcess` would still kill the child
when the parent exited. This happened because tokio's `kill_on_drop(true)`
was set at spawn time with no way to disable it later.

This commit replaces tokio/deno_subprocess_windows's `kill_on_drop` with
a custom `Drop` implementation on `ChildResource` that kills the child
by PID only when a `kill_on_drop` flag is true. New ops
`op_spawn_child_ref` and `op_spawn_child_unref` toggle this flag, and
are called from `ChildProcess.ref()` and `ChildProcess.unref()` in JS.

Closes #21446

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@kajukitli kajukitli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm — good fix for Node.js unref() semantics

the approach is correct:

  • custom Drop impl on ChildResource instead of tokio's kill_on_drop(true)
  • kill_on_drop: Cell<bool> toggled by op_spawn_child_ref/unref
  • sends SIGKILL on drop only if kill_on_drop is true

test coverage is solid — spawns child, calls unref, parent exits, verifies child wrote marker file

bartlomieju and others added 2 commits March 12, 2026 09:39
On Windows, non-detached children are assigned to a global job object
with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, so they are always terminated
when the parent exits — regardless of unref(). This is a Windows
OS-level behaviour that cannot be changed after spawn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `multi_test` schema does not support `if` at the top level — it
must be inside the individual test definition (`single_or_multi_step_test`).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bartlomieju bartlomieju merged commit 21a8152 into main Mar 17, 2026
220 of 222 checks passed
@bartlomieju bartlomieju deleted the fix/child-process-unref-kill-on-drop branch March 17, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Calling unref() on a child process instance created by Node.js' spawn API kills the sub process

3 participants