Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(node): Rework node:child_process IPC #24763

Merged
merged 43 commits into from
Jul 30, 2024

Conversation

nathanwhit
Copy link
Member

@nathanwhit nathanwhit commented Jul 27, 2024

Fixes #24756. Fixes #24796.

This also gets vitest working when using --pool=forks (which is the default as of vitest 2.0). Ref #23882.


This PR resolves a handful of issues with child_process IPC. In particular:

  • We didn't support sending typed array views over IPC
  • Opening an IPC channel resulted in the event loop never exiting
  • Sending a null over IPC would terminate the channel
  • There was some UB in the read implementation (transmuting an &[u8] to &mut [u8])
  • The send method wasn't returning anything, so there was no way to signal backpressure (this also resulted in the benchmark child_process_ipc.mjs being misleading, as it tried to respect backpressure. That gave node much worse results at larger message sizes, and gave us much worse results at smaller message sizes).
  • We weren't setting up the channel property on the process global (or on the ChildProcess object), and also didn't have a way to ref/unref the channel
  • Calling kill multiple times (or disconnecting the channel, then calling kill) would throw an error
  • Node couldn't spawn a deno subprocess and communicate with it over IPC

@@ -1050,10 +1050,10 @@ impl CliOptions {
}

pub fn node_ipc_fd(&self) -> Option<i64> {
let maybe_node_channel_fd = std::env::var("DENO_CHANNEL_FD").ok();
let maybe_node_channel_fd = std::env::var("NODE_CHANNEL_FD").ok();
Copy link
Member

Choose a reason for hiding this comment

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

@littledivy just to be sure, we went with DENO_CHANNEL_FD because we couldn't communicate between Node.js and Deno subprocesses, right?

Copy link
Member

Choose a reason for hiding this comment

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

yup, this PR seems to enable Deno processes spawned by Node processes to communicate. @nathanwhit is it also working the other way around?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, at least on unix (haven't tested windows). Though sending handles won't work (bc we don't support it yet).

ext/node/polyfills/child_process.ts Show resolved Hide resolved
ext/node/polyfills/internal/child_process.ts Outdated Show resolved Hide resolved
ext/node/ops/ipc.rs Outdated Show resolved Hide resolved
ext/node/ops/ipc.rs Outdated Show resolved Hide resolved
ext/node/ops/ipc.rs Show resolved Hide resolved
@@ -53,25 +163,66 @@ mod impl_ {
Some(child_pipe_fd) => child_pipe_fd.0,
None => return Ok(None),
};

let ref_tracker = IpcRefTracker::new(state.external_ops_tracker.clone());
Copy link
Member

Choose a reason for hiding this comment

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

Not for this PR, but I think we should move most of this module to runtime/ops or even create a separate deno_ipc crate. Besides the serialization code it seems abstract enough that we could expose an IPC API for Deno.Command API.

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

LGTM, really nice work!

@nathanwhit nathanwhit merged commit cd59fc5 into denoland:main Jul 30, 2024
17 checks passed
@nathanwhit nathanwhit deleted the child-process-rework branch July 30, 2024 23:13
crowlKats pushed a commit that referenced this pull request Jul 31, 2024
Fixes #24756. Fixes
#24796.

This also gets vitest working when using
[`--pool=forks`](https://vitest.dev/guide/improving-performance#pool)
(which is the default as of vitest 2.0). Ref
#23882.

---

This PR resolves a handful of issues with child_process IPC. In
particular:

- We didn't support sending typed array views over IPC
- Opening an IPC channel resulted in the event loop never exiting
- Sending a `null` over IPC would terminate the channel
- There was some UB in the read implementation (transmuting an `&[u8]`
to `&mut [u8]`)
- The `send` method wasn't returning anything, so there was no way to
signal backpressure (this also resulted in the benchmark
`child_process_ipc.mjs` being misleading, as it tried to respect
backpressure. That gave node much worse results at larger message sizes,
and gave us much worse results at smaller message sizes).
- We weren't setting up the `channel` property on the `process` global
(or on the `ChildProcess` object), and also didn't have a way to
ref/unref the channel
- Calling `kill` multiple times (or disconnecting the channel, then
calling kill) would throw an error
- Node couldn't spawn a deno subprocess and communicate with it over IPC

(cherry picked from commit cd59fc5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants