Skip to content

Commit a394219

Browse files
legendecastargos
authored andcommittedAug 14, 2024
src: skip inspector wait in internal workers
Internal workers are essential to load user scripts and bootstrapped with internal entrypoints. They should not be waiting for inspectors even when `--inspect-brk` and `--inspect-wait` were specified, and avoid blocking main thread to bootstrap. IsolateData can be created with a specified PerIsolateOptions instead of creating a copy from the per_process namespace. This also avoids creating a copy bypassing the parent env's modified options, like creating a worker thread from a worker thread. PR-URL: #54219 Fixes: #53681 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent b93c6d9 commit a394219

10 files changed

+148
-28
lines changed
 

‎src/api/environment.cc

+2-3
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,8 @@ IsolateData* CreateIsolateData(
371371
MultiIsolatePlatform* platform,
372372
ArrayBufferAllocator* allocator,
373373
const EmbedderSnapshotData* embedder_snapshot_data) {
374-
const SnapshotData* snapshot_data =
375-
SnapshotData::FromEmbedderWrapper(embedder_snapshot_data);
376-
return new IsolateData(isolate, loop, platform, allocator, snapshot_data);
374+
return IsolateData::CreateIsolateData(
375+
isolate, loop, platform, allocator, embedder_snapshot_data);
377376
}
378377

379378
void FreeIsolateData(IsolateData* isolate_data) {

‎src/env-inl.h

-5
Original file line numberDiff line numberDiff line change
@@ -590,11 +590,6 @@ inline std::shared_ptr<PerIsolateOptions> IsolateData::options() {
590590
return options_;
591591
}
592592

593-
inline void IsolateData::set_options(
594-
std::shared_ptr<PerIsolateOptions> options) {
595-
options_ = std::move(options);
596-
}
597-
598593
template <typename Fn>
599594
void Environment::SetImmediate(Fn&& cb, CallbackFlags::Flags flags) {
600595
auto callback = native_immediates_.CreateCallback(std::move(cb), flags);

‎src/env.cc

+20-4
Original file line numberDiff line numberDiff line change
@@ -538,19 +538,35 @@ Mutex IsolateData::isolate_data_mutex_;
538538
std::unordered_map<uint16_t, std::unique_ptr<PerIsolateWrapperData>>
539539
IsolateData::wrapper_data_map_;
540540

541+
IsolateData* IsolateData::CreateIsolateData(
542+
Isolate* isolate,
543+
uv_loop_t* loop,
544+
MultiIsolatePlatform* platform,
545+
ArrayBufferAllocator* allocator,
546+
const EmbedderSnapshotData* embedder_snapshot_data,
547+
std::shared_ptr<PerIsolateOptions> options) {
548+
const SnapshotData* snapshot_data =
549+
SnapshotData::FromEmbedderWrapper(embedder_snapshot_data);
550+
if (options == nullptr) {
551+
options = per_process::cli_options->per_isolate->Clone();
552+
}
553+
return new IsolateData(
554+
isolate, loop, platform, allocator, snapshot_data, options);
555+
}
556+
541557
IsolateData::IsolateData(Isolate* isolate,
542558
uv_loop_t* event_loop,
543559
MultiIsolatePlatform* platform,
544560
ArrayBufferAllocator* node_allocator,
545-
const SnapshotData* snapshot_data)
561+
const SnapshotData* snapshot_data,
562+
std::shared_ptr<PerIsolateOptions> options)
546563
: isolate_(isolate),
547564
event_loop_(event_loop),
548565
node_allocator_(node_allocator == nullptr ? nullptr
549566
: node_allocator->GetImpl()),
550567
platform_(platform),
551-
snapshot_data_(snapshot_data) {
552-
options_.reset(
553-
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
568+
snapshot_data_(snapshot_data),
569+
options_(std::move(options)) {
554570
v8::CppHeap* cpp_heap = isolate->GetCppHeap();
555571

556572
uint16_t cppgc_id = kDefaultCppGCEmbedderID;

‎src/env.h

+14-5
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,22 @@ struct PerIsolateWrapperData {
139139
};
140140

141141
class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
142-
public:
142+
private:
143143
IsolateData(v8::Isolate* isolate,
144144
uv_loop_t* event_loop,
145-
MultiIsolatePlatform* platform = nullptr,
146-
ArrayBufferAllocator* node_allocator = nullptr,
147-
const SnapshotData* snapshot_data = nullptr);
145+
MultiIsolatePlatform* platform,
146+
ArrayBufferAllocator* node_allocator,
147+
const SnapshotData* snapshot_data,
148+
std::shared_ptr<PerIsolateOptions> options);
149+
150+
public:
151+
static IsolateData* CreateIsolateData(
152+
v8::Isolate* isolate,
153+
uv_loop_t* event_loop,
154+
MultiIsolatePlatform* platform = nullptr,
155+
ArrayBufferAllocator* node_allocator = nullptr,
156+
const EmbedderSnapshotData* embedder_snapshot_data = nullptr,
157+
std::shared_ptr<PerIsolateOptions> options = nullptr);
148158
~IsolateData();
149159

150160
SET_MEMORY_INFO_NAME(IsolateData)
@@ -173,7 +183,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
173183
inline MultiIsolatePlatform* platform() const;
174184
inline const SnapshotData* snapshot_data() const;
175185
inline std::shared_ptr<PerIsolateOptions> options();
176-
inline void set_options(std::shared_ptr<PerIsolateOptions> options);
177186

178187
inline NodeArrayBufferAllocator* node_allocator() const;
179188

‎src/node_options-inl.h

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ EnvironmentOptions* PerIsolateOptions::get_per_env_options() {
1717
return per_env.get();
1818
}
1919

20+
std::shared_ptr<PerIsolateOptions> PerIsolateOptions::Clone() const {
21+
auto options =
22+
std::shared_ptr<PerIsolateOptions>(new PerIsolateOptions(*this));
23+
options->per_env = std::make_shared<EnvironmentOptions>(*per_env);
24+
return options;
25+
}
26+
2027
namespace options_parser {
2128

2229
template <typename Options>

‎src/node_options.h

+13
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ class DebugOptions : public Options {
9494
break_first_line = true;
9595
}
9696

97+
void DisableWaitOrBreakFirstLine() {
98+
inspect_wait = false;
99+
break_first_line = false;
100+
}
101+
97102
bool wait_for_connect() const {
98103
return break_first_line || break_node_first_line || inspect_wait;
99104
}
@@ -253,6 +258,9 @@ class EnvironmentOptions : public Options {
253258

254259
class PerIsolateOptions : public Options {
255260
public:
261+
PerIsolateOptions() = default;
262+
PerIsolateOptions(PerIsolateOptions&&) = default;
263+
256264
std::shared_ptr<EnvironmentOptions> per_env { new EnvironmentOptions() };
257265
bool track_heap_objects = false;
258266
bool report_uncaught_exception = false;
@@ -264,6 +272,11 @@ class PerIsolateOptions : public Options {
264272
inline EnvironmentOptions* get_per_env_options();
265273
void CheckOptions(std::vector<std::string>* errors,
266274
std::vector<std::string>* argv) override;
275+
276+
inline std::shared_ptr<PerIsolateOptions> Clone() const;
277+
278+
private:
279+
PerIsolateOptions(const PerIsolateOptions&) = default;
267280
};
268281

269282
class PerProcessOptions : public Options {

‎src/node_worker.cc

+21-11
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,15 @@ class WorkerThreadData {
185185
isolate->SetStackLimit(w->stack_base_);
186186

187187
HandleScope handle_scope(isolate);
188-
isolate_data_.reset(
189-
CreateIsolateData(isolate,
190-
&loop_,
191-
w_->platform_,
192-
allocator.get(),
193-
w->snapshot_data()->AsEmbedderWrapper().get()));
188+
isolate_data_.reset(IsolateData::CreateIsolateData(
189+
isolate,
190+
&loop_,
191+
w_->platform_,
192+
allocator.get(),
193+
w->snapshot_data()->AsEmbedderWrapper().get(),
194+
std::move(w_->per_isolate_opts_)));
194195
CHECK(isolate_data_);
195196
CHECK(!isolate_data_->is_building_snapshot());
196-
if (w_->per_isolate_opts_)
197-
isolate_data_->set_options(std::move(w_->per_isolate_opts_));
198197
isolate_data_->set_worker_context(w_);
199198
isolate_data_->max_young_gen_size =
200199
params.constraints.max_young_generation_size_in_bytes();
@@ -491,9 +490,8 @@ Worker::~Worker() {
491490

492491
void Worker::New(const FunctionCallbackInfo<Value>& args) {
493492
Environment* env = Environment::GetCurrent(args);
494-
auto is_internal = args[5];
495-
CHECK(is_internal->IsBoolean());
496-
if (is_internal->IsFalse()) {
493+
bool is_internal = args[5]->IsTrue();
494+
if (!is_internal) {
497495
THROW_IF_INSUFFICIENT_PERMISSIONS(
498496
env, permission::PermissionScope::kWorkerThreads, "");
499497
}
@@ -658,7 +656,19 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
658656
return;
659657
}
660658
} else {
659+
// Copy the parent's execArgv.
661660
exec_argv_out = env->exec_argv();
661+
per_isolate_opts = env->isolate_data()->options()->Clone();
662+
}
663+
664+
// Internal workers should not wait for inspector frontend to connect or
665+
// break on the first line of internal scripts. Module loader threads are
666+
// essential to load user codes and must not be blocked by the inspector
667+
// for internal scripts.
668+
// Still, `--inspect-node` can break on the first line of internal scripts.
669+
if (is_internal) {
670+
per_isolate_opts->per_env->get_debug_options()
671+
->DisableWaitOrBreakFirstLine();
662672
}
663673

664674
const SnapshotData* snapshot_data = env->isolate_data()->snapshot_data();

‎test/common/inspector-helper.js

+4
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ class InspectorSession {
309309
return notification.method === 'Runtime.executionContextDestroyed' &&
310310
notification.params.executionContextId === 1;
311311
});
312+
await this.waitForDisconnect();
313+
}
314+
315+
async waitForDisconnect() {
312316
while ((await this._instance.nextStderrString()) !==
313317
'Waiting for the debugger to disconnect...');
314318
await this.disconnect();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This tests esm loader's internal worker will not be blocked by --inspect-brk.
2+
// Regression: https://github.com/nodejs/node/issues/53681
3+
4+
'use strict';
5+
const common = require('../common');
6+
7+
common.skipIfInspectorDisabled();
8+
9+
const assert = require('assert');
10+
const fixtures = require('../common/fixtures');
11+
const { NodeInstance } = require('../common/inspector-helper.js');
12+
13+
async function runIfWaitingForDebugger(session) {
14+
const commands = [
15+
{ 'method': 'Runtime.enable' },
16+
{ 'method': 'Debugger.enable' },
17+
{ 'method': 'Runtime.runIfWaitingForDebugger' },
18+
];
19+
20+
await session.send(commands);
21+
await session.waitForNotification('Debugger.paused');
22+
}
23+
24+
async function runTest() {
25+
const main = fixtures.path('es-module-loaders', 'register-loader.mjs');
26+
const child = new NodeInstance(['--inspect-brk=0'], '', main);
27+
28+
const session = await child.connectInspectorSession();
29+
await runIfWaitingForDebugger(session);
30+
await session.runToCompletion();
31+
assert.strictEqual((await child.expectShutdown()).exitCode, 0);
32+
}
33+
34+
runTest();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// This tests esm loader's internal worker will not be blocked by --inspect-wait.
2+
// Regression: https://github.com/nodejs/node/issues/53681
3+
4+
'use strict';
5+
const common = require('../common');
6+
7+
common.skipIfInspectorDisabled();
8+
9+
const assert = require('assert');
10+
const fixtures = require('../common/fixtures');
11+
const { NodeInstance } = require('../common/inspector-helper.js');
12+
13+
async function runIfWaitingForDebugger(session) {
14+
const commands = [
15+
{ 'method': 'Runtime.enable' },
16+
{ 'method': 'Debugger.enable' },
17+
{ 'method': 'Runtime.runIfWaitingForDebugger' },
18+
];
19+
20+
await session.send(commands);
21+
}
22+
23+
async function runTest() {
24+
const main = fixtures.path('es-module-loaders', 'register-loader.mjs');
25+
const child = new NodeInstance(['--inspect-wait=0'], '', main);
26+
27+
const session = await child.connectInspectorSession();
28+
await runIfWaitingForDebugger(session);
29+
await session.waitForDisconnect();
30+
assert.strictEqual((await child.expectShutdown()).exitCode, 0);
31+
}
32+
33+
runTest();

0 commit comments

Comments
 (0)