Skip to content

Commit

Permalink
worker: add setEnvironmentData/getEnvironmentData
Browse files Browse the repository at this point in the history
These APIs allow arbitrary, cloneable JavaScript values to be set and
passed to all new Worker instances spawned from the current context.
It is similar to `workerData` except that environment data is set
independently of the `new Worker()` constructor, and the the value is
passed automatically to all new Workers.

This is a *partial* fix of #30992
but does not implement a complete fix.

Signed-off-by: James M Snell <[email protected]>

PR-URL: #37486
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Gireesh Punathil <[email protected]>
Reviewed-By: Darshan Sen <[email protected]>
  • Loading branch information
jasnell authored and targos committed Sep 1, 2021
1 parent fe12cc0 commit 9f6bc58
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 0 deletions.
49 changes: 49 additions & 0 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,38 @@ Worker threads inherit non-process-specific options by default. Refer to
[`Worker constructor options`][] to know how to customize worker thread options,
specifically `argv` and `execArgv` options.

## `worker.getEnvironmentData(key)`
<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
* `key` {any} Any arbitrary, cloneable JavaScript value that can be used as a
{Map} key.
* Returns: {any}

Within a worker thread, `worker.getEnvironmentData()` returns a clone
of data passed to the spawning thread's `worker.setEnvironmentData()`.
Every new `Worker` receives its own copy of the environment data
automatically.

```js
const {
Worker,
isMainThread,
setEnvironmentData,
getEnvironmentData,
} = require('worker_threads');

if (isMainThread) {
setEnvironmentData('Hello', 'World!');
const worker = new Worker(__filename);
} else {
console.log(getEnvironmentData('Hello')); // Prints 'World!'.
}
```

## `worker.isMainThread`
<!-- YAML
added: v10.5.0
Expand Down Expand Up @@ -240,6 +272,23 @@ new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
});
```

## `worker.setEnvironmentData(key[, value])`
<!--YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
* `key` {any} Any arbitrary, cloneable JavaScript value that can be used as a
{Map} key.
* `value` {any} Any arbitrary, cloneable JavaScript value that will be cloned
and passed automatically to all new `Worker` instances. If `value` is passed
as `undefined`, any previously set value for the `key` will be deleted.

The `worker.setEnvironmentData()` API sets the content of
`worker.getEnvironmentData()` in the current thread and all new `Worker`
instances spawned from the current context.

## `worker.threadId`
<!-- YAML
added: v10.5.0
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/main/worker_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ port.on('message', (message) => {
filename,
doEval,
workerData,
environmentData,
publicPort,
manifestSrc,
manifestURL,
Expand All @@ -130,6 +131,8 @@ port.on('message', (message) => {
publicWorker.parentPort = publicPort;
publicWorker.workerData = workerData;

require('internal/worker').assignEnvironmentData(environmentData);

// The counter is only passed to the workers created by the main thread, not
// to workers created by other workers.
let cachedCwd = '';
Expand Down
25 changes: 25 additions & 0 deletions lib/internal/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
Promise,
PromiseResolve,
RegExpPrototypeTest,
SafeMap,
String,
Symbol,
SymbolFor,
Expand Down Expand Up @@ -85,6 +86,8 @@ let debug = require('internal/util/debuglog').debuglog('worker', (fn) => {

let cwdCounter;

const environmentData = new SafeMap();

if (isMainThread) {
cwdCounter = new Uint32Array(new SharedArrayBuffer(4));
const originalChdir = process.chdir;
Expand All @@ -94,6 +97,24 @@ if (isMainThread) {
};
}

function setEnvironmentData(key, value) {
if (value === undefined)
environmentData.delete(key);
else
environmentData.set(key, value);
}

function getEnvironmentData(key) {
return environmentData.get(key);
}

function assignEnvironmentData(data) {
if (data === undefined) return;
data.forEach((value, key) => {
environmentData.set(key, value);
});
}

class Worker extends EventEmitter {
constructor(filename, options = {}) {
super();
Expand Down Expand Up @@ -222,6 +243,7 @@ class Worker extends EventEmitter {
doEval,
cwdCounter: cwdCounter || workerIo.sharedCwdCounter,
workerData: options.workerData,
environmentData,
publicPort: port2,
manifestURL: getOptionValue('--experimental-policy') ?
require('internal/process/policy').url :
Expand Down Expand Up @@ -482,6 +504,9 @@ module.exports = {
SHARE_ENV,
resourceLimits:
!isMainThread ? makeResourceLimits(resourceLimitsRaw) : {},
setEnvironmentData,
getEnvironmentData,
assignEnvironmentData,
threadId,
Worker,
};
4 changes: 4 additions & 0 deletions lib/worker_threads.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const {
isMainThread,
SHARE_ENV,
resourceLimits,
setEnvironmentData,
getEnvironmentData,
threadId,
Worker
} = require('internal/worker');
Expand Down Expand Up @@ -32,4 +34,6 @@ module.exports = {
Worker,
parentPort: null,
workerData: null,
setEnvironmentData,
getEnvironmentData,
};
33 changes: 33 additions & 0 deletions test/parallel/test-worker-environmentdata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

require('../common');
const {
Worker,
getEnvironmentData,
setEnvironmentData,
threadId,
} = require('worker_threads');

const {
deepStrictEqual,
strictEqual,
} = require('assert');

if (!process.env.HAS_STARTED_WORKER) {
process.env.HAS_STARTED_WORKER = 1;
setEnvironmentData('foo', 'bar');
setEnvironmentData('hello', { value: 'world' });
setEnvironmentData(1, 2);
strictEqual(getEnvironmentData(1), 2);
setEnvironmentData(1); // Delete it, key won't show up in the worker.
new Worker(__filename);
setEnvironmentData('hello'); // Delete it. Has no impact on the worker.
} else {
strictEqual(getEnvironmentData('foo'), 'bar');
deepStrictEqual(getEnvironmentData('hello'), { value: 'world' });
strictEqual(getEnvironmentData(1), undefined);

// Recurse to make sure the environment data is inherited
if (threadId <= 2)
new Worker(__filename);
}

0 comments on commit 9f6bc58

Please sign in to comment.