Skip to content

Commit a26322c

Browse files
committed
fixup! src: make BuiltinLoader threadsafe and non-global
1 parent 5da2608 commit a26322c

File tree

11 files changed

+216
-50
lines changed

11 files changed

+216
-50
lines changed

src/env-inl.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -436,14 +436,9 @@ inline std::vector<double>* Environment::destroy_async_id_list() {
436436
return &destroy_async_id_list_;
437437
}
438438

439-
inline std::shared_ptr<builtins::BuiltinLoader> Environment::builtin_loader() {
439+
inline builtins::BuiltinLoader* Environment::builtin_loader() {
440440
DCHECK(builtin_loader_);
441-
return builtin_loader_;
442-
}
443-
444-
inline void Environment::set_builtin_loader(
445-
std::shared_ptr<builtins::BuiltinLoader> loader) {
446-
builtin_loader_ = loader;
441+
return builtin_loader_.get();
447442
}
448443

449444
inline double Environment::new_async_id() {

src/env.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,8 @@ Environment::Environment(IsolateData* isolate_data,
674674
flags_(flags),
675675
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
676676
? AllocateEnvironmentThreadId().id
677-
: thread_id.id) {
677+
: thread_id.id),
678+
builtin_loader_(builtins::BuiltinLoader::Create()) {
678679
// We'll be creating new objects so make sure we've entered the context.
679680
HandleScope handle_scope(isolate);
680681

@@ -752,8 +753,6 @@ Environment::Environment(IsolateData* isolate_data,
752753
env_info,
753754
flags,
754755
thread_id) {
755-
// TODO(addaleax): Make this part of CreateEnvironment().
756-
set_builtin_loader(builtins::BuiltinLoader::Create());
757756
InitializeMainContext(context, env_info);
758757
}
759758

src/env.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -721,8 +721,7 @@ class Environment : public MemoryRetainer {
721721
// List of id's that have been destroyed and need the destroy() cb called.
722722
inline std::vector<double>* destroy_async_id_list();
723723

724-
std::shared_ptr<builtins::BuiltinLoader> builtin_loader();
725-
void set_builtin_loader(std::shared_ptr<builtins::BuiltinLoader> loader);
724+
builtins::BuiltinLoader* builtin_loader();
726725

727726
std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map;
728727
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
@@ -1146,7 +1145,7 @@ class Environment : public MemoryRetainer {
11461145

11471146
std::unique_ptr<Realm> principal_realm_ = nullptr;
11481147

1149-
std::shared_ptr<builtins::BuiltinLoader> builtin_loader_;
1148+
std::unique_ptr<builtins::BuiltinLoader> builtin_loader_;
11501149

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

src/node_builtins.cc

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "env-inl.h"
44
#include "node_external_reference.h"
55
#include "node_internals.h"
6+
#include "node_threadsafe_cow-inl.h"
67
#include "simdutf.h"
78
#include "util-inl.h"
89

@@ -53,24 +54,22 @@ BuiltinLoader::BuiltinLoader() : config_(GetConfig()), has_code_cache_(false) {
5354
}
5455

5556
bool BuiltinLoader::Exists(const char* id) {
56-
RwLock::ScopedReadLock lock(source_mutex_);
57-
return source_.find(id) != source_.end();
57+
auto source = source_.read();
58+
return source->find(id) != source->end();
5859
}
5960

6061
bool BuiltinLoader::Add(const char* id, const UnionBytes& source) {
61-
RwLock::ScopedLock source_lock(source_mutex_);
62-
Mutex::ScopedLock categories_lock(builtin_categories_mutex_);
6362
builtin_categories_
6463
.reset(); // The category cache is reset by adding new sources
65-
auto result = source_.emplace(id, source);
64+
auto result = source_.write()->emplace(id, source);
6665
return result.second;
6766
}
6867

6968
Local<Object> BuiltinLoader::GetSourceObject(Local<Context> context) {
70-
RwLock::ScopedReadLock lock(source_mutex_);
7169
Isolate* isolate = context->GetIsolate();
7270
Local<Object> out = Object::New(isolate);
73-
for (auto const& x : source_) {
71+
auto source = source_.read();
72+
for (auto const& x : *source) {
7473
Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size());
7574
out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust();
7675
}
@@ -82,18 +81,17 @@ Local<String> BuiltinLoader::GetConfigString(Isolate* isolate) {
8281
}
8382

8483
std::vector<std::string> BuiltinLoader::GetBuiltinIds() const {
85-
RwLock::ScopedReadLock lock(source_mutex_);
8684
std::vector<std::string> ids;
87-
ids.reserve(source_.size());
88-
for (auto const& x : source_) {
85+
auto source = source_.read();
86+
ids.reserve(source->size());
87+
for (auto const& x : *source) {
8988
ids.emplace_back(x.first);
9089
}
9190
return ids;
9291
}
9392

9493
const BuiltinLoader::BuiltinCategories&
9594
BuiltinLoader::InitializeBuiltinCategories() const {
96-
Mutex::ScopedLock lock(builtin_categories_mutex_);
9795
if (LIKELY(builtin_categories_.has_value())) {
9896
DCHECK(!builtin_categories_.value().can_be_required.empty());
9997
return builtin_categories_.value();
@@ -138,7 +136,8 @@ BuiltinLoader::InitializeBuiltinCategories() const {
138136
"internal/v8_prof_processor",
139137
};
140138

141-
for (auto const& x : source_) {
139+
auto source = source_.read();
140+
for (auto const& x : *source) {
142141
const std::string& id = x.first;
143142
for (auto const& prefix : prefixes) {
144143
if (prefix.length() > id.length()) {
@@ -151,7 +150,7 @@ BuiltinLoader::InitializeBuiltinCategories() const {
151150
}
152151
}
153152

154-
for (auto const& x : source_) {
153+
for (auto const& x : *source) {
155154
const std::string& id = x.first;
156155
if (0 == builtin_categories.cannot_be_required.count(id)) {
157156
builtin_categories.can_be_required.emplace(id);
@@ -177,7 +176,8 @@ bool BuiltinLoader::CannotBeRequired(const char* id) const {
177176
return GetCannotBeRequired().count(id) == 1;
178177
}
179178

180-
ScriptCompiler::CachedData* BuiltinLoader::GetCodeCache(const char* id) const {
179+
const ScriptCompiler::CachedData* BuiltinLoader::GetCodeCache(
180+
const char* id) const {
181181
RwLock::ScopedReadLock lock(code_cache_mutex_);
182182
const auto it = code_cache_.find(id);
183183
if (it == code_cache_.end()) {
@@ -206,12 +206,12 @@ static std::string OnDiskFileName(const char* id) {
206206

207207
MaybeLocal<String> BuiltinLoader::LoadBuiltinSource(Isolate* isolate,
208208
const char* id) const {
209+
auto source = source_.read();
209210
#ifdef NODE_BUILTIN_MODULES_PATH
210211
if (strncmp(id, "embedder_main_", strlen("embedder_main_")) == 0) {
211212
#endif // NODE_BUILTIN_MODULES_PATH
212-
RwLock::ScopedReadLock lock(source_mutex_);
213-
const auto source_it = source_.find(id);
214-
if (UNLIKELY(source_it == source_.end())) {
213+
const auto source_it = source->find(id);
214+
if (UNLIKELY(source_it == source->end())) {
215215
fprintf(stderr, "Cannot find native builtin: \"%s\".\n", id);
216216
ABORT();
217217
}
@@ -439,7 +439,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
439439
MaybeLocal<Function> maybe =
440440
LookupAndCompileInternal(context, id, &parameters, &result);
441441
if (optional_realm != nullptr) {
442-
DCHECK_EQ(this, optional_realm->env()->builtin_loader().get());
442+
DCHECK_EQ(this, optional_realm->env()->builtin_loader());
443443
RecordResult(id, result, optional_realm);
444444
}
445445
return maybe;
@@ -535,7 +535,7 @@ bool BuiltinLoader::CompileAllBuiltins(Local<Context> context) {
535535
return all_succeeded;
536536
}
537537

538-
void BuiltinLoader::CopyCodeCache(std::vector<CodeCacheInfo>* out) {
538+
void BuiltinLoader::CopyCodeCache(std::vector<CodeCacheInfo>* out) const {
539539
RwLock::ScopedReadLock lock(code_cache_mutex_);
540540
for (auto const& item : code_cache_) {
541541
out->push_back(
@@ -692,8 +692,19 @@ void BuiltinLoader::HasCachedBuiltins(const FunctionCallbackInfo<Value>& args) {
692692
v8::Boolean::New(args.GetIsolate(), instance->has_code_cache_));
693693
}
694694

695-
std::shared_ptr<BuiltinLoader> BuiltinLoader::Create() {
696-
return std::shared_ptr<BuiltinLoader>{new BuiltinLoader()};
695+
std::unique_ptr<BuiltinLoader> BuiltinLoader::Create() {
696+
return std::unique_ptr<BuiltinLoader>{new BuiltinLoader()};
697+
}
698+
699+
void BuiltinLoader::CopySourceAndCodeCacheFrom(const BuiltinLoader* other) {
700+
{
701+
std::vector<CodeCacheInfo> cache;
702+
other->CopyCodeCache(&cache);
703+
RefreshCodeCache(cache);
704+
has_code_cache_ = other->has_code_cache_;
705+
}
706+
707+
source_ = other->source_;
697708
}
698709

699710
void BuiltinLoader::CreatePerIsolateProperties(IsolateData* isolate_data,

src/node_builtins.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <string>
1212
#include <vector>
1313
#include "node_mutex.h"
14+
#include "node_threadsafe_cow.h"
1415
#include "node_union_bytes.h"
1516
#include "v8.h"
1617

@@ -74,9 +75,10 @@ class NODE_EXTERN_PRIVATE BuiltinLoader {
7475

7576
bool CompileAllBuiltins(v8::Local<v8::Context> context);
7677
void RefreshCodeCache(const std::vector<CodeCacheInfo>& in);
77-
void CopyCodeCache(std::vector<CodeCacheInfo>* out);
78+
void CopyCodeCache(std::vector<CodeCacheInfo>* out) const;
7879

79-
static std::shared_ptr<BuiltinLoader> Create();
80+
static std::unique_ptr<BuiltinLoader> Create();
81+
void CopySourceAndCodeCacheFrom(const BuiltinLoader* other);
8082

8183
private:
8284
// Only allow access from friends.
@@ -101,7 +103,7 @@ class NODE_EXTERN_PRIVATE BuiltinLoader {
101103
bool CanBeRequired(const char* id) const;
102104
bool CannotBeRequired(const char* id) const;
103105

104-
v8::ScriptCompiler::CachedData* GetCodeCache(const char* id) const;
106+
const v8::ScriptCompiler::CachedData* GetCodeCache(const char* id) const;
105107
enum class Result { kWithCache, kWithoutCache };
106108
v8::MaybeLocal<v8::String> LoadBuiltinSource(v8::Isolate* isolate,
107109
const char* id) const;
@@ -135,19 +137,14 @@ class NODE_EXTERN_PRIVATE BuiltinLoader {
135137

136138
void AddExternalizedBuiltin(const char* id, const char* filename);
137139

138-
// Used to synchronize access to builtin_categories.
139140
// builtin_categories is marked mutable because it is only initialized
140141
// as a cache, so that mutating it does not change any state.
141-
Mutex builtin_categories_mutex_;
142142
mutable std::optional<BuiltinCategories> builtin_categories_;
143143

144-
// Used to synchronize access to the code cache map
145-
RwLock source_mutex_;
146-
BuiltinSourceMap source_;
144+
ThreadsafeCopyOnWrite<BuiltinSourceMap> source_;
147145

148146
const UnionBytes config_;
149147

150-
// Used to synchronize access to the code cache map
151148
RwLock code_cache_mutex_;
152149
BuiltinCodeCacheMap code_cache_;
153150
bool has_code_cache_;

src/node_main_instance.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,11 @@ NodeMainInstance::CreateMainEnvironment(ExitCode* exit_code) {
159159
&(snapshot_data_->env_info),
160160
EnvironmentFlags::kDefaultFlags,
161161
{}));
162+
#ifdef NODE_V8_SHARED_RO_HEAP
162163
// TODO(addaleax): Do this as part of creating the Environment
163164
// once we store the SnapshotData* itself on IsolateData.
164-
auto builtin_loader = builtins::BuiltinLoader::Create();
165-
builtin_loader->RefreshCodeCache(snapshot_data_->code_cache);
166-
env->set_builtin_loader(builtin_loader);
165+
env->builtin_loader()->RefreshCodeCache(snapshot_data_->code_cache);
166+
#endif
167167
context = Context::FromSnapshot(isolate_,
168168
SnapshotData::kNodeMainContextIndex,
169169
{DeserializeNodeInternalFields, env.get()})

src/node_threadsafe_cow-inl.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#ifndef SRC_NODE_THREADSAFE_COW_INL_H_
2+
#define SRC_NODE_THREADSAFE_COW_INL_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
namespace node {
7+
8+
template <typename T>
9+
T* CopyOnWrite<T>::write() {
10+
if (!data_.unique()) {
11+
data_ = std::make_shared<T>(*data_);
12+
}
13+
return data_.get();
14+
}
15+
16+
template <typename T>
17+
ThreadsafeCopyOnWrite<T>::Read::Read(const ThreadsafeCopyOnWrite<T>* cow)
18+
: cow_(cow), lock_(cow->impl_->mutex) {}
19+
20+
template <typename T>
21+
const T& ThreadsafeCopyOnWrite<T>::Read::operator*() const {
22+
return cow_->impl_->data;
23+
}
24+
25+
template <typename T>
26+
const T* ThreadsafeCopyOnWrite<T>::Read::operator->() const {
27+
return &cow_->impl_->data;
28+
}
29+
30+
template <typename T>
31+
ThreadsafeCopyOnWrite<T>::Write::Write(ThreadsafeCopyOnWrite<T>* cow)
32+
: cow_(cow), impl_(cow->impl_.write()), lock_(impl_->mutex) {}
33+
34+
template <typename T>
35+
T& ThreadsafeCopyOnWrite<T>::Write::operator*() {
36+
return impl_->data;
37+
}
38+
39+
template <typename T>
40+
T* ThreadsafeCopyOnWrite<T>::Write::operator->() {
41+
return &impl_->data;
42+
}
43+
44+
template <typename T>
45+
ThreadsafeCopyOnWrite<T>::Impl::Impl(const Impl& other) {
46+
RwLock::ScopedReadLock lock(other.mutex);
47+
data = other.data;
48+
}
49+
50+
} // namespace node
51+
52+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
53+
54+
#endif // SRC_NODE_THREADSAFE_COW_INL_H_

0 commit comments

Comments
 (0)