Skip to content

Commit 71d43a5

Browse files
legendecastargos
authored andcommitted
worker: add argv constructor option
A convenience option to populate `process.argv` in worker threads. PR-URL: #30559 Fixes: #30531 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Denys Otrishko <[email protected]>
1 parent 255cd7e commit 71d43a5

File tree

4 files changed

+81
-3
lines changed

4 files changed

+81
-3
lines changed

doc/api/worker_threads.md

+7
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ changes:
510510
- version: v13.2.0
511511
pr-url: https://github.com/nodejs/node/pull/26628
512512
description: The `resourceLimits` option was introduced.
513+
- version: REPLACEME
514+
pr-url: https://github.com/nodejs/node/pull/30559
515+
description: The `argv` option was introduced.
513516
-->
514517

515518
* `filename` {string} The path to the Worker’s main script. Must be
@@ -518,6 +521,10 @@ changes:
518521
If `options.eval` is `true`, this is a string containing JavaScript code
519522
rather than a path.
520523
* `options` {Object}
524+
* `argv` {any[]} List of arguments which would be stringified and appended to
525+
`process.argv` in the worker. This is mostly similar to the `workerData`
526+
but the values will be available on the global `process.argv` as if they
527+
were passed as CLI options to the script.
521528
* `env` {Object} If set, specifies the initial value of `process.env` inside
522529
the Worker thread. As a special value, [`worker.SHARE_ENV`][] may be used
523530
to specify that the parent thread and the child thread should share their

lib/internal/main/worker_thread.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ if (process.env.NODE_CHANNEL_FD) {
9292
port.on('message', (message) => {
9393
if (message.type === LOAD_SCRIPT) {
9494
const {
95+
argv,
9596
cwdCounter,
9697
filename,
9798
doEval,
@@ -115,6 +116,9 @@ port.on('message', (message) => {
115116
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
116117
loadPreloadModules();
117118
initializeFrozenIntrinsics();
119+
if (argv !== undefined) {
120+
process.argv = process.argv.concat(argv);
121+
}
118122
publicWorker.parentPort = publicPort;
119123
publicWorker.workerData = workerData;
120124

@@ -143,12 +147,22 @@ port.on('message', (message) => {
143147
port.postMessage({ type: UP_AND_RUNNING });
144148
if (doEval) {
145149
const { evalScript } = require('internal/process/execution');
146-
evalScript('[worker eval]', filename);
150+
const name = '[worker eval]';
151+
// This is necessary for CJS module compilation.
152+
// TODO: pass this with something really internal.
153+
ObjectDefineProperty(process, '_eval', {
154+
configurable: true,
155+
enumerable: true,
156+
value: filename,
157+
});
158+
process.argv.splice(1, 0, name);
159+
evalScript(name, filename);
147160
} else {
148161
// script filename
149162
// runMain here might be monkey-patched by users in --require.
150163
// XXX: the monkey-patchability here should probably be deprecated.
151-
CJSLoader.Module.runMain(process.argv[1] = filename);
164+
process.argv.splice(1, 0, filename);
165+
CJSLoader.Module.runMain(filename);
152166
}
153167
} else if (message.type === STDIO_PAYLOAD) {
154168
const { stream, chunk, encoding } = message;

lib/internal/worker.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,16 @@ class Worker extends EventEmitter {
8282
validateString(filename, 'filename');
8383
if (options.execArgv && !ArrayIsArray(options.execArgv)) {
8484
throw new ERR_INVALID_ARG_TYPE('options.execArgv',
85-
'array',
85+
'Array',
8686
options.execArgv);
8787
}
88+
let argv;
89+
if (options.argv) {
90+
if (!ArrayIsArray(options.argv)) {
91+
throw new ERR_INVALID_ARG_TYPE('options.argv', 'Array', options.argv);
92+
}
93+
argv = options.argv.map(String);
94+
}
8895
if (!options.eval) {
8996
if (!path.isAbsolute(filename) && !/^\.\.?[\\/]/.test(filename)) {
9097
throw new ERR_WORKER_PATH(filename);
@@ -154,6 +161,7 @@ class Worker extends EventEmitter {
154161
this[kPublicPort].on('message', (message) => this.emit('message', message));
155162
setupPortReferencing(this[kPublicPort], this, 'message');
156163
this[kPort].postMessage({
164+
argv,
157165
type: messageTypes.LOAD_SCRIPT,
158166
filename,
159167
doEval: !!options.eval,
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const { Worker, isMainThread, workerData } = require('worker_threads');
5+
6+
if (isMainThread) {
7+
assert.throws(() => {
8+
new Worker(__filename, { argv: 'foo' });
9+
}, {
10+
code: 'ERR_INVALID_ARG_TYPE'
11+
});
12+
13+
[
14+
new Worker(__filename, {
15+
argv: [null, 'foo', 123, Symbol('bar')],
16+
// Asserts only if the worker is started by the test.
17+
workerData: 'assert-argv'
18+
}),
19+
new Worker(`
20+
const assert = require('assert');
21+
assert.deepStrictEqual(
22+
process.argv,
23+
[process.execPath, '[worker eval]']
24+
);
25+
`, {
26+
eval: true
27+
}),
28+
new Worker(`
29+
const assert = require('assert');
30+
assert.deepStrictEqual(
31+
process.argv,
32+
[process.execPath, '[worker eval]', 'null', 'foo', '123',
33+
String(Symbol('bar'))]
34+
);
35+
`, {
36+
argv: [null, 'foo', 123, Symbol('bar')],
37+
eval: true
38+
})
39+
].forEach((worker) => {
40+
worker.on('exit', common.mustCall((code) => {
41+
assert.strictEqual(code, 0);
42+
}));
43+
});
44+
} else if (workerData === 'assert-argv') {
45+
assert.deepStrictEqual(
46+
process.argv,
47+
[process.execPath, __filename, 'null', 'foo', '123', String(Symbol('bar'))]
48+
);
49+
}

0 commit comments

Comments
 (0)