Skip to content

Commit

Permalink
worker: provide process.execArgv
Browse files Browse the repository at this point in the history
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 <joyeec9h3@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
addaleax committed Mar 2, 2019
1 parent 347dd99 commit 8cf4170
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 22 deletions.
5 changes: 4 additions & 1 deletion doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,9 @@ if (isMainThread) {
not automatically be piped through to `process.stderr` in the parent.
* `execArgv` {string[]} List of node CLI options passed to the worker.
V8 options (such as `--max-old-space-size`) and options that affect the
process (such as `--title`) are not supported.
process (such as `--title`) are not supported. If set, this will be provided
as [`process.execArgv`][] inside the worker. By default, options will be
inherited from the parent thread.

### Event: 'error'
<!-- YAML
Expand Down Expand Up @@ -582,6 +584,7 @@ active handle in the event system. If the worker is already `unref()`ed calling
[`process.abort()`]: process.html#process_process_abort
[`process.chdir()`]: process.html#process_process_chdir_directory
[`process.env`]: process.html#process_process_env
[`process.execArgv`]: process.html#process_process_execargv
[`process.exit()`]: process.html#process_process_exit_code
[`process.stderr`]: process.html#process_process_stderr
[`process.stdin`]: process.html#process_process_stdin
Expand Down
4 changes: 4 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,10 @@ inline std::shared_ptr<EnvironmentOptions> Environment::options() {
return options_;
}

inline const std::vector<std::string>& Environment::exec_argv() {
return exec_argv_;
}

inline std::shared_ptr<HostPort> Environment::inspector_host_port() {
return inspector_host_port_;
}
Expand Down
1 change: 1 addition & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ MaybeLocal<Object> Environment::ProcessCliArgs(
std::move(traced_value));
}

exec_argv_ = exec_args;
Local<Object> process_object =
node::CreateProcessObject(this, args, exec_args)
.FromMaybe(Local<Object>());
Expand Down
2 changes: 2 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ class Environment {
v8::MaybeLocal<v8::Object> ProcessCliArgs(
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args);
inline const std::vector<std::string>& exec_argv();

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

uint32_t module_id_counter_ = 0;
uint32_t script_id_counter_ = 0;
Expand Down
16 changes: 12 additions & 4 deletions src/node_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ void AsyncRequest::MemoryInfo(MemoryTracker* tracker) const {
Worker::Worker(Environment* env,
Local<Object> wrap,
const std::string& url,
std::shared_ptr<PerIsolateOptions> per_isolate_opts)
std::shared_ptr<PerIsolateOptions> per_isolate_opts,
std::vector<std::string>&& exec_argv)
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_WORKER),
url_(url),
per_isolate_opts_(per_isolate_opts),
exec_argv_(exec_argv),
platform_(env->isolate_data()->platform()),
profiler_idle_notifier_started_(env->profiler_idle_notifier_started()),
thread_id_(Environment::AllocateThreadId()) {
Expand Down Expand Up @@ -284,7 +286,7 @@ void Worker::Run() {

env_->Start(profiler_idle_notifier_started_);
env_->ProcessCliArgs(std::vector<std::string>{},
std::vector<std::string>{});
std::move(exec_argv_));
}

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

std::vector<std::string> exec_argv_out;
bool has_explicit_exec_argv = false;

// Argument might be a string or URL
if (args.Length() > 0 && !args[0]->IsNullOrUndefined()) {
Utf8Value value(
Expand All @@ -445,6 +450,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
v8::Local<v8::Array> array = args[1].As<v8::Array>();
// The first argument is reserved for program name, but we don't need it
// in workers.
has_explicit_exec_argv = true;
std::vector<std::string> exec_argv = {""};
uint32_t length = array->Length();
for (uint32_t i = 0; i < length; i++) {
Expand Down Expand Up @@ -472,7 +478,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
// options for the per isolate parser.
options_parser::PerIsolateOptionsParser::instance.Parse(
&exec_argv,
nullptr,
&exec_argv_out,
&invalid_args,
per_isolate_opts.get(),
kDisallowedInEnvironment,
Expand All @@ -492,7 +498,9 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
}
}
}
new Worker(env, args.This(), url, per_isolate_opts);
if (!has_explicit_exec_argv)
exec_argv_out = env->exec_argv();
new Worker(env, args.This(), url, per_isolate_opts, std::move(exec_argv_out));
}

void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {
Expand Down
4 changes: 3 additions & 1 deletion src/node_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class Worker : public AsyncWrap {
Worker(Environment* env,
v8::Local<v8::Object> wrap,
const std::string& url,
std::shared_ptr<PerIsolateOptions> per_isolate_opts);
std::shared_ptr<PerIsolateOptions> per_isolate_opts,
std::vector<std::string>&& exec_argv);
~Worker() override;

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

std::shared_ptr<PerIsolateOptions> per_isolate_opts_;
std::vector<std::string> exec_argv_;
MultiIsolatePlatform* platform_;
v8::Isolate* isolate_ = nullptr;
bool profiler_idle_notifier_started_;
Expand Down
48 changes: 34 additions & 14 deletions test/parallel/test-process-exec-argv.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,46 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const spawn = require('child_process').spawn;
const { Worker, isMainThread } = require('worker_threads');

if (process.argv[2] === 'child') {
process.stdout.write(JSON.stringify(process.execArgv));
if (process.argv[2] === 'child' || !isMainThread) {
if (process.argv[3] === 'cp+worker')
new Worker(__filename);
else
process.stdout.write(JSON.stringify(process.execArgv));
} else {
for (const extra of [ [], [ '--' ] ]) {
const execArgv = ['--stack-size=256'];
const args = [__filename, 'child', 'arg0'];
const child = spawn(process.execPath, [...execArgv, ...extra, ...args]);
let out = '';
for (const kind of [ 'cp', 'worker', 'cp+worker' ]) {
const execArgv = ['--pending-deprecation'];
const args = [__filename, 'child', kind];
let child;
switch (kind) {
case 'cp':
child = spawn(process.execPath, [...execArgv, ...extra, ...args]);
break;
case 'worker':
child = new Worker(__filename, {
execArgv: [...execArgv, ...extra],
stdout: true
});
break;
case 'cp+worker':
child = spawn(process.execPath, [...execArgv, ...args]);
break;
}

child.stdout.setEncoding('utf8');
child.stdout.on('data', function(chunk) {
out += chunk;
});
let out = '';
child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => {
out += chunk;
});

child.on('close', function() {
assert.deepStrictEqual(JSON.parse(out), execArgv);
});
child.stdout.on('end', common.mustCall(() => {
assert.deepStrictEqual(JSON.parse(out), execArgv);
}));
}
}
}
7 changes: 5 additions & 2 deletions test/parallel/test-worker-execargv.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ const assert = require('assert');
// This test ensures that Workers have the ability to get
// their own command line flags.

const { Worker, isMainThread } = require('worker_threads');
const { Worker } = require('worker_threads');
const { StringDecoder } = require('string_decoder');
const decoder = new StringDecoder('utf8');

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

0 comments on commit 8cf4170

Please sign in to comment.