worker: add setEnvironmentData/getEnvironmentData

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 https://github.com/nodejs/node/issues/30992
but does not implement a complete fix.

Signed-off-by: James M Snell <jasnell@gmail.com>

PR-URL: https://github.com/nodejs/node/pull/37486
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
This commit is contained in:
James M Snell committed 2021-03-15 07:40:26 -07:00
1 parent 7abdc3ecb4
commit 802b3e7cf9
5 files changed
+114

No files matched your search

+49
View File
@@ -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
@@ -246,6 +278,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
+3
View File
@@ -105,6 +105,7 @@ port.on('message', (message) => {
filename,
doEval,
workerData,
environmentData,
publicPort,
manifestSrc,
manifestURL,
@@ -127,6 +128,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 = '';
+25
View File
@@ -18,6 +18,7 @@ const {
ReflectApply,
RegExpPrototypeTest,
SafeArrayIterator,
SafeMap,
String,
Symbol,
SymbolFor,
@@ -90,6 +91,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;
@@ -99,6 +102,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();
@@ -228,6 +249,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 :
@@ -493,6 +515,9 @@ module.exports = {
SHARE_ENV,
resourceLimits:
!isMainThread ? makeResourceLimits(resourceLimitsRaw) : {},
setEnvironmentData,
getEnvironmentData,
assignEnvironmentData,
threadId,
Worker,
};
+4
View File
@@ -4,6 +4,8 @@ const {
isMainThread,
SHARE_ENV,
resourceLimits,
setEnvironmentData,
getEnvironmentData,
threadId,
Worker
} = require('internal/worker');
@@ -34,4 +36,6 @@ module.exports = {
parentPort: null,
workerData: null,
BroadcastChannel,
setEnvironmentData,
getEnvironmentData,
};
@@ -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);
}