Skip to content

Commit 42f9b93

Browse files
committed
src: make BuiltinLoader threadsafe and non-global
As discussed in #45888, using a global `BuiltinLoader` instance is probably undesirable in a world in which embedders are able to create Node.js Environments with different sources and therefore mutually incompatible code caching properties. This PR makes it so that `BuiltinLoader` is no longer a global singleton and instead only shared between `Environment`s that have a direct relation to each other, and addresses a few thread safety issues along with that.
1 parent 71951a0 commit 42f9b93

13 files changed

+147
-122
lines changed

src/api/environment.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -458,9 +458,8 @@ MaybeLocal<Value> LoadEnvironment(
458458
main_script_source_utf8).ToLocalChecked();
459459
auto main_utf16 = std::make_unique<String::Value>(isolate, str);
460460

461-
// TODO(addaleax): Avoid having a global table for all scripts.
462461
std::string name = "embedder_main_" + std::to_string(env->thread_id());
463-
builtins::BuiltinLoader::Add(
462+
env->builtin_loader()->Add(
464463
name.c_str(), UnionBytes(**main_utf16, main_utf16->length()));
465464
env->set_main_utf16(std::move(main_utf16));
466465
Realm* realm = env->principal_realm();
@@ -703,9 +702,10 @@ Maybe<bool> InitializePrimordials(Local<Context> context) {
703702
"internal/per_context/messageport",
704703
nullptr};
705704

705+
auto builtin_loader = builtins::BuiltinLoader::Create();
706706
for (const char** module = context_files; *module != nullptr; module++) {
707707
Local<Value> arguments[] = {exports, primordials};
708-
if (builtins::BuiltinLoader::CompileAndCall(
708+
if (builtin_loader->CompileAndCall(
709709
context, *module, arraysize(arguments), arguments, nullptr)
710710
.IsEmpty()) {
711711
// Execution failed during context creation.

src/env-inl.h

+9
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,15 @@ inline std::vector<double>* Environment::destroy_async_id_list() {
432432
return &destroy_async_id_list_;
433433
}
434434

435+
inline std::shared_ptr<builtins::BuiltinLoader> Environment::builtin_loader() {
436+
DCHECK(builtin_loader_);
437+
return builtin_loader_;
438+
}
439+
440+
inline void Environment::set_builtin_loader(std::shared_ptr<builtins::BuiltinLoader> loader) {
441+
builtin_loader_ = loader;
442+
}
443+
435444
inline double Environment::new_async_id() {
436445
async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1;
437446
return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter];

src/env.cc

+2
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,8 @@ Environment::Environment(IsolateData* isolate_data,
747747
env_info,
748748
flags,
749749
thread_id) {
750+
// TODO(addaleax): Make this part of CreateEnvironment().
751+
set_builtin_loader(builtins::BuiltinLoader::Create());
750752
InitializeMainContext(context, env_info);
751753
}
752754

src/env.h

+5
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,8 @@ class Environment : public MemoryRetainer {
716716
inline std::vector<double>* destroy_async_id_list();
717717

718718
std::set<struct node_module*> internal_bindings;
719+
std::shared_ptr<builtins::BuiltinLoader> builtin_loader();
720+
void set_builtin_loader(std::shared_ptr<builtins::BuiltinLoader> loader);
719721
std::set<std::string> builtins_with_cache;
720722
std::set<std::string> builtins_without_cache;
721723
// This is only filled during deserialization. We use a vector since
@@ -1147,7 +1149,10 @@ class Environment : public MemoryRetainer {
11471149
// Keeps the main script source alive is one was passed to LoadEnvironment().
11481150
// We should probably find a way to just use plain `v8::String`s created from
11491151
// the source passed to LoadEnvironment() directly instead.
1152+
// TODO(addaleax): Move this to BuiltinLoader, like we do for
1153+
// BuiltinLoader::AddExternalizedBuiltin().
11501154
std::unique_ptr<v8::String::Value> main_utf16_;
1155+
std::shared_ptr<builtins::BuiltinLoader> builtin_loader_;
11511156

11521157
// Used by allocate_managed_buffer() and release_managed_buffer() to keep
11531158
// track of the BackingStore for a given pointer.

src/node.cc

-3
Original file line numberDiff line numberDiff line change
@@ -1183,9 +1183,6 @@ ExitCode LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
11831183
}
11841184
}
11851185

1186-
if ((*snapshot_data_ptr) != nullptr) {
1187-
BuiltinLoader::RefreshCodeCache((*snapshot_data_ptr)->code_cache);
1188-
}
11891186
NodeMainInstance main_instance(*snapshot_data_ptr,
11901187
uv_default_loop(),
11911188
per_process::v8_platform.Platform(),

src/node_binding.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -586,13 +586,13 @@ void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
586586
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
587587
DefineConstants(env->isolate(), exports);
588588
} else if (!strcmp(*module_v, "natives")) {
589-
exports = builtins::BuiltinLoader::GetSourceObject(env->context());
589+
exports = env->builtin_loader()->GetSourceObject(env->context());
590590
// Legacy feature: process.binding('natives').config contains stringified
591591
// config.gypi
592592
CHECK(exports
593593
->Set(env->context(),
594594
env->config_string(),
595-
builtins::BuiltinLoader::GetConfigString(env->isolate()))
595+
env->builtin_loader()->GetConfigString(env->isolate()))
596596
.FromJust());
597597
} else {
598598
return THROW_ERR_INVALID_MODULE(env, "No such binding: %s", *module_v);

0 commit comments

Comments
 (0)