diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2024-04-24 17:17:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-24 16:17:28 +0000 |
commit | b424959d3e2554a83dd6a7a9c8837805a3d9ae65 (patch) | |
tree | e65881529cdcb8d51ecee52ef8706047e98cb982 | |
parent | 5294885a5a411e6b2e9674ce9d8f951c9c011988 (diff) |
fix(ext/node): worker_threads copies env object (#23536)
Most common argument to `env` option for `worker_threads.Worker` will be
`process.env`.
In Deno `process.env` is a `Proxy` which can't be cloned using
structured clone algorithm.
So to be safe, I'm creating a copy of actual object before it's sent to
the worker thread.
Ref #23522
-rw-r--r-- | ext/node/polyfills/worker_threads.ts | 12 | ||||
-rw-r--r-- | tests/unit_node/worker_threads_test.ts | 29 |
2 files changed, 39 insertions, 2 deletions
diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts index 96aa02fe1..8f531368a 100644 --- a/ext/node/polyfills/worker_threads.ts +++ b/ext/node/polyfills/worker_threads.ts @@ -29,7 +29,7 @@ import { EventEmitter } from "node:events"; import { BroadcastChannel } from "ext:deno_broadcast_channel/01_broadcast_channel.js"; import process from "node:process"; -const { ObjectPrototypeIsPrototypeOf } = primordials; +const { JSONParse, JSONStringify, ObjectPrototypeIsPrototypeOf } = primordials; const { Error, Symbol, @@ -126,10 +126,18 @@ class NodeWorker extends EventEmitter { } this.#name = name; + // One of the most common usages will be to pass `process.env` here, + // but because `process.env` is a Proxy in Deno, we need to get a plain + // object out of it - otherwise we'll run in `DataCloneError`s. + // See https://github.com/denoland/deno/issues/23522. + let env_ = undefined; + if (options?.env) { + env_ = JSONParse(JSONStringify(options?.env)); + } const serializedWorkerMetadata = serializeJsMessageData({ workerData: options?.workerData, environmentData: environmentData, - env: options?.env, + env: env_, }, options?.transferList ?? []); const id = op_create_worker( { diff --git a/tests/unit_node/worker_threads_test.ts b/tests/unit_node/worker_threads_test.ts index a96896ce5..bc2becd66 100644 --- a/tests/unit_node/worker_threads_test.ts +++ b/tests/unit_node/worker_threads_test.ts @@ -10,6 +10,7 @@ import { import { fromFileUrl, relative, SEPARATOR } from "@std/path/mod.ts"; import * as workerThreads from "node:worker_threads"; import { EventEmitter, once } from "node:events"; +import process from "node:process"; Deno.test("[node/worker_threads] BroadcastChannel is exported", () => { assertEquals<unknown>(workerThreads.BroadcastChannel, BroadcastChannel); @@ -486,3 +487,31 @@ Deno.test({ await worker.terminate(); }, }); + +Deno.test({ + name: "[node/worker_threads] Worker env using process.env", + async fn() { + const deferred = Promise.withResolvers<void>(); + const worker = new workerThreads.Worker( + ` + import { parentPort } from "node:worker_threads"; + import process from "node:process"; + parentPort.postMessage("ok"); + `, + { + eval: true, + // Make sure this doesn't throw `DataCloneError`. + // See https://github.com/denoland/deno/issues/23522. + env: process.env, + }, + ); + + worker.on("message", (data) => { + assertEquals(data, "ok"); + deferred.resolve(); + }); + + await deferred.promise; + await worker.terminate(); + }, +}); |