Skip to content

Commit 8cf4170

Browse files
committed
worker: provide process.execArgv
Provide `process.execArgv`. If an `execArgv` option is passed to the `Worker` constructor, that option is used as its value; if not, the parent’s `process.execArgv` is inherited (since that also goes for the actual options in that case). PR-URL: #26267 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 347dd99 commit 8cf4170

8 files changed

+65
-22
lines changed

doc/api/worker_threads.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,9 @@ if (isMainThread) {
428428
not automatically be piped through to `process.stderr` in the parent.
429429
* `execArgv` {string[]} List of node CLI options passed to the worker.
430430
V8 options (such as `--max-old-space-size`) and options that affect the
431-
process (such as `--title`) are not supported.
431+
process (such as `--title`) are not supported. If set, this will be provided
432+
as [`process.execArgv`][] inside the worker. By default, options will be
433+
inherited from the parent thread.
432434

433435
### Event: 'error'
434436
<!-- YAML
@@ -582,6 +584,7 @@ active handle in the event system. If the worker is already `unref()`ed calling
582584
[`process.abort()`]: process.html#process_process_abort
583585
[`process.chdir()`]: process.html#process_process_chdir_directory
584586
[`process.env`]: process.html#process_process_env
587+
[`process.execArgv`]: process.html#process_process_execargv
585588
[`process.exit()`]: process.html#process_process_exit_code
586589
[`process.stderr`]: process.html#process_process_stderr
587590
[`process.stdin`]: process.html#process_process_stdin

src/env-inl.h

+4
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,10 @@ inline std::shared_ptr<EnvironmentOptions> Environment::options() {
605605
return options_;
606606
}
607607

608+
inline const std::vector<std::string>& Environment::exec_argv() {
609+
return exec_argv_;
610+
}
611+
608612
inline std::shared_ptr<HostPort> Environment::inspector_host_port() {
609613
return inspector_host_port_;
610614
}

src/env.cc

+1
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ MaybeLocal<Object> Environment::ProcessCliArgs(
386386
std::move(traced_value));
387387
}
388388

389+
exec_argv_ = exec_args;
389390
Local<Object> process_object =
390391
node::CreateProcessObject(this, args, exec_args)
391392
.FromMaybe(Local<Object>());

src/env.h

+2
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ class Environment {
674674
v8::MaybeLocal<v8::Object> ProcessCliArgs(
675675
const std::vector<std::string>& args,
676676
const std::vector<std::string>& exec_args);
677+
inline const std::vector<std::string>& exec_argv();
677678

678679
typedef void (*HandleCleanupCb)(Environment* env,
679680
uv_handle_t* handle,
@@ -1064,6 +1065,7 @@ class Environment {
10641065
// the inspector_host_port_->port() will be the actual port being
10651066
// used.
10661067
std::shared_ptr<HostPort> inspector_host_port_;
1068+
std::vector<std::string> exec_argv_;
10671069

10681070
uint32_t module_id_counter_ = 0;
10691071
uint32_t script_id_counter_ = 0;

src/node_worker.cc

+12-4
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,12 @@ void AsyncRequest::MemoryInfo(MemoryTracker* tracker) const {
101101
Worker::Worker(Environment* env,
102102
Local<Object> wrap,
103103
const std::string& url,
104-
std::shared_ptr<PerIsolateOptions> per_isolate_opts)
104+
std::shared_ptr<PerIsolateOptions> per_isolate_opts,
105+
std::vector<std::string>&& exec_argv)
105106
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_WORKER),
106107
url_(url),
107108
per_isolate_opts_(per_isolate_opts),
109+
exec_argv_(exec_argv),
108110
platform_(env->isolate_data()->platform()),
109111
profiler_idle_notifier_started_(env->profiler_idle_notifier_started()),
110112
thread_id_(Environment::AllocateThreadId()) {
@@ -284,7 +286,7 @@ void Worker::Run() {
284286

285287
env_->Start(profiler_idle_notifier_started_);
286288
env_->ProcessCliArgs(std::vector<std::string>{},
287-
std::vector<std::string>{});
289+
std::move(exec_argv_));
288290
}
289291

290292
Debug(this, "Created Environment for worker with id %llu", thread_id_);
@@ -434,6 +436,9 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
434436
std::string url;
435437
std::shared_ptr<PerIsolateOptions> per_isolate_opts = nullptr;
436438

439+
std::vector<std::string> exec_argv_out;
440+
bool has_explicit_exec_argv = false;
441+
437442
// Argument might be a string or URL
438443
if (args.Length() > 0 && !args[0]->IsNullOrUndefined()) {
439444
Utf8Value value(
@@ -445,6 +450,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
445450
v8::Local<v8::Array> array = args[1].As<v8::Array>();
446451
// The first argument is reserved for program name, but we don't need it
447452
// in workers.
453+
has_explicit_exec_argv = true;
448454
std::vector<std::string> exec_argv = {""};
449455
uint32_t length = array->Length();
450456
for (uint32_t i = 0; i < length; i++) {
@@ -472,7 +478,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
472478
// options for the per isolate parser.
473479
options_parser::PerIsolateOptionsParser::instance.Parse(
474480
&exec_argv,
475-
nullptr,
481+
&exec_argv_out,
476482
&invalid_args,
477483
per_isolate_opts.get(),
478484
kDisallowedInEnvironment,
@@ -492,7 +498,9 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
492498
}
493499
}
494500
}
495-
new Worker(env, args.This(), url, per_isolate_opts);
501+
if (!has_explicit_exec_argv)
502+
exec_argv_out = env->exec_argv();
503+
new Worker(env, args.This(), url, per_isolate_opts, std::move(exec_argv_out));
496504
}
497505

498506
void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {

src/node_worker.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ class Worker : public AsyncWrap {
3838
Worker(Environment* env,
3939
v8::Local<v8::Object> wrap,
4040
const std::string& url,
41-
std::shared_ptr<PerIsolateOptions> per_isolate_opts);
41+
std::shared_ptr<PerIsolateOptions> per_isolate_opts,
42+
std::vector<std::string>&& exec_argv);
4243
~Worker() override;
4344

4445
// Run the worker. This is only called from the worker thread.
@@ -74,6 +75,7 @@ class Worker : public AsyncWrap {
7475
const std::string url_;
7576

7677
std::shared_ptr<PerIsolateOptions> per_isolate_opts_;
78+
std::vector<std::string> exec_argv_;
7779
MultiIsolatePlatform* platform_;
7880
v8::Isolate* isolate_ = nullptr;
7981
bool profiler_idle_notifier_started_;

test/parallel/test-process-exec-argv.js

+34-14
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,46 @@
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

2222
'use strict';
23-
require('../common');
23+
const common = require('../common');
2424
const assert = require('assert');
2525
const spawn = require('child_process').spawn;
26+
const { Worker, isMainThread } = require('worker_threads');
2627

27-
if (process.argv[2] === 'child') {
28-
process.stdout.write(JSON.stringify(process.execArgv));
28+
if (process.argv[2] === 'child' || !isMainThread) {
29+
if (process.argv[3] === 'cp+worker')
30+
new Worker(__filename);
31+
else
32+
process.stdout.write(JSON.stringify(process.execArgv));
2933
} else {
3034
for (const extra of [ [], [ '--' ] ]) {
31-
const execArgv = ['--stack-size=256'];
32-
const args = [__filename, 'child', 'arg0'];
33-
const child = spawn(process.execPath, [...execArgv, ...extra, ...args]);
34-
let out = '';
35+
for (const kind of [ 'cp', 'worker', 'cp+worker' ]) {
36+
const execArgv = ['--pending-deprecation'];
37+
const args = [__filename, 'child', kind];
38+
let child;
39+
switch (kind) {
40+
case 'cp':
41+
child = spawn(process.execPath, [...execArgv, ...extra, ...args]);
42+
break;
43+
case 'worker':
44+
child = new Worker(__filename, {
45+
execArgv: [...execArgv, ...extra],
46+
stdout: true
47+
});
48+
break;
49+
case 'cp+worker':
50+
child = spawn(process.execPath, [...execArgv, ...args]);
51+
break;
52+
}
3553

36-
child.stdout.setEncoding('utf8');
37-
child.stdout.on('data', function(chunk) {
38-
out += chunk;
39-
});
54+
let out = '';
55+
child.stdout.setEncoding('utf8');
56+
child.stdout.on('data', (chunk) => {
57+
out += chunk;
58+
});
4059

41-
child.on('close', function() {
42-
assert.deepStrictEqual(JSON.parse(out), execArgv);
43-
});
60+
child.stdout.on('end', common.mustCall(() => {
61+
assert.deepStrictEqual(JSON.parse(out), execArgv);
62+
}));
63+
}
4464
}
4565
}

test/parallel/test-worker-execargv.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ const assert = require('assert');
55
// This test ensures that Workers have the ability to get
66
// their own command line flags.
77

8-
const { Worker, isMainThread } = require('worker_threads');
8+
const { Worker } = require('worker_threads');
99
const { StringDecoder } = require('string_decoder');
1010
const decoder = new StringDecoder('utf8');
1111

12-
if (isMainThread) {
12+
// Do not use isMainThread so that this test itself can be run inside a Worker.
13+
if (!process.env.HAS_STARTED_WORKER) {
14+
process.env.HAS_STARTED_WORKER = 1;
1315
const w = new Worker(__filename, { execArgv: ['--trace-warnings'] });
1416
w.stderr.on('data', common.mustCall((chunk) => {
1517
const error = decoder.write(chunk);
@@ -19,4 +21,5 @@ if (isMainThread) {
1921
}));
2022
} else {
2123
process.emitWarning('some warning');
24+
assert.deepStrictEqual(process.execArgv, ['--trace-warnings']);
2225
}

0 commit comments

Comments
 (0)