From b2351bbb4e6a205237fc58c9d5b089103deba028 Mon Sep 17 00:00:00 2001 From: robobun Date: Fri, 22 Aug 2025 19:59:15 -0700 Subject: [PATCH] Add Symbol.asyncDispose to Worker in worker_threads (#22064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Implement `Symbol.asyncDispose` for the `Worker` class in `worker_threads` module - Enables automatic resource cleanup with `await using` syntax - Calls `await this.terminate()` to properly shut down workers when they go out of scope ## Implementation Details The implementation adds a simple async method to the Worker class: ```typescript async [Symbol.asyncDispose]() { await this.terminate(); } ``` This allows workers to be used with the new `await using` syntax for automatic cleanup: ```javascript { await using worker = new Worker('./worker.js'); // worker automatically terminates when leaving this scope } ``` ## Test Plan - [x] Added comprehensive tests for `Symbol.asyncDispose` functionality - [x] Tests verify the method exists and returns undefined - [x] Tests verify `await using` syntax works correctly for automatic worker cleanup - [x] All new tests pass - [x] Existing worker_threads functionality remains intact 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot Co-authored-by: Claude Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/js/node/worker_threads.ts | 4 ++ .../worker-async-dispose.test.ts | 58 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/js/node/worker_threads/worker-async-dispose.test.ts diff --git a/src/js/node/worker_threads.ts b/src/js/node/worker_threads.ts index a4bdc6b65b..bb8bddb61f 100644 --- a/src/js/node/worker_threads.ts +++ b/src/js/node/worker_threads.ts @@ -388,6 +388,10 @@ class Worker extends EventEmitter { #onOpen() { this.emit("online"); } + + async [Symbol.asyncDispose]() { + await this.terminate(); + } } class HeapSnapshotStream extends Readable { diff --git a/test/js/node/worker_threads/worker-async-dispose.test.ts b/test/js/node/worker_threads/worker-async-dispose.test.ts new file mode 100644 index 0000000000..a4cf40087b --- /dev/null +++ b/test/js/node/worker_threads/worker-async-dispose.test.ts @@ -0,0 +1,58 @@ +import { expect, test } from "bun:test"; +import { Worker } from "worker_threads"; + +test("Worker implements Symbol.asyncDispose", async () => { + const worker = new Worker( + ` + const { parentPort } = require("worker_threads"); + parentPort?.postMessage("ready"); + `, + { eval: true }, + ); + + // Wait for the worker to be ready + await new Promise(resolve => { + worker.on("message", msg => { + if (msg === "ready") { + resolve(msg); + } + }); + }); + + // Test that Symbol.asyncDispose exists and is a function + expect(typeof worker[Symbol.asyncDispose]).toBe("function"); + + // Test that calling Symbol.asyncDispose terminates the worker + const disposeResult = await worker[Symbol.asyncDispose](); + expect(disposeResult).toBeUndefined(); +}); + +test("Worker can be used with await using", async () => { + let workerTerminated = false; + + { + await using worker = new Worker( + ` + const { parentPort } = require("worker_threads"); + parentPort?.postMessage("hello from worker"); + `, + { eval: true }, + ); + + // Listen for worker exit to confirm termination + worker.on("exit", () => { + workerTerminated = true; + }); + + // Wait for the worker message to ensure it's running + await new Promise(resolve => { + worker.on("message", resolve); + }); + + // Worker should automatically terminate when leaving this block via Symbol.asyncDispose + } + + // Give a moment for the exit event to be emitted + await new Promise(resolve => setTimeout(resolve, 100)); + expect(workerTerminated).toBe(true); +});