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: https://github.com/nodejs/node/pull/26267
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Anna Henningsen 2019-02-22 21:01:22 +01:00
parent b8018f407b
commit f65b4afaea
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
8 changed files with 65 additions and 22 deletions

View File

@ -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
@ -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

View File

@ -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_;
}

View File

@ -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>());

View File

@ -676,6 +676,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,
@ -1060,6 +1061,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;

View File

@ -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()) {
@ -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_);
@ -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(
@ -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++) {
@ -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,
@ -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) {

View File

@ -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.
@ -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_;

View File

@ -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);
}));
}
}
}

View File

@ -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);
@ -19,4 +21,5 @@ if (isMainThread) {
}));
} else {
process.emitWarning('some warning');
assert.deepStrictEqual(process.execArgv, ['--trace-warnings']);
}