From 3bc5724ff4f6804b7253eda8016640b4bcdfdb20 Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Thu, 29 May 2025 11:45:18 -0700 Subject: [PATCH] better stub child_process channel --- src/js/node/child_process.ts | 56 +++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/js/node/child_process.ts b/src/js/node/child_process.ts index 35354dfdda..a327245673 100644 --- a/src/js/node/child_process.ts +++ b/src/js/node/child_process.ts @@ -1,3 +1,5 @@ +import type { Pipe as NodeStreamPipe } from "node:stream"; + // Hardcoded module "node:child_process" const EventEmitter = require("node:events"); const OsModule = require("node:os"); @@ -1041,13 +1043,15 @@ class ChildProcess extends EventEmitter { #handle; #closesNeeded = 1; #closesGot = 0; + disconnect: undefined | (() => void); signalCode = null; exitCode = null; spawnfile; spawnargs; pid; - channel; + + channel: NodeStreamPipe | undefined; killed = false; [Symbol.dispose]() { @@ -1371,7 +1375,7 @@ class ChildProcess extends EventEmitter { if (has_ipc) { this.send = this.#send; this.disconnect = this.#disconnect; - this.channel = new Control(); + this.channel = new SubprocessChannel(this); Object.defineProperty(this, "_channel", { get() { return this.channel; @@ -1730,9 +1734,53 @@ function abortChildProcess(child, killSignal, reason) { } } -class Control extends EventEmitter { - constructor() { +class SubprocessChannel extends EventEmitter implements NodeStreamPipe { + #hasRef: boolean = true; + #setRef: (enabled: boolean) => void; + #closed: boolean = false; + #childProcess: ChildProcess | undefined; + + public constructor(childProcess?: ChildProcess) { super(); + this.#setRef = $newZigFunction("node_cluster_binding.zig", "setRef", 1); + this.#childProcess = childProcess; + } + + public close(): void { + if (this.#closed) return; + + this.#closed = true; + + if (this.#hasRef) { + this.#hasRef = false; + this.#setRef(false); + } + + if (this.#childProcess) { + this.#childProcess.disconnect?.(); + } + + process.nextTick(() => { + this.emit("close"); + }); + } + + public hasRef(): boolean { + return this.#hasRef && !this.#closed; + } + + public ref(): void { + if (!this.#hasRef && !this.#closed) { + this.#hasRef = true; + this.#setRef(true); + } + } + + public unref(): void { + if (this.#hasRef) { + this.#hasRef = false; + this.#setRef(false); + } } }