Skip to content

Commit e4b9236

Browse files
committed
perf(runtime): move this branch's hash containers to robin_hood
Maintainer feedback: std::unordered_map should not be used here at all — the repo vendors robin_hood.h (already the convention in Caches.h, MetadataBuilder.mm, SetTimeout.cpp, ModuleInternal.h) and this branch's new containers were the stragglers. Swapped: the per-isolate module registry maps and state table, the import map, the module/dynamic-import waiter maps, the re-entry guard sets, the prewarm cache, and the cache-bust key set. ModuleHandleMap moves into ModuleInternalCallbacks.h so the registry accessor's public signature names one type. robin_hood picks its node-based, reference-stable layout automatically for value types that are not nothrow-movable (v8::Global), and the waiter maps are used strictly find/drain/erase, so no usage pattern relies on stability the flat layout would lose. std::unordered_map uses that predate the branch (Runtime.mm, Interop.mm, Metadata.mm, ...) are main's to sweep separately.
1 parent ee4a8d3 commit e4b9236

3 files changed

Lines changed: 36 additions & 29 deletions

File tree

‎NativeScript/runtime/HMRSupport.mm‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
#include <atomic>
99
#include <mutex>
1010
#include <string>
11-
#include <unordered_map>
12-
#include <unordered_set>
1311
#include <vector>
1412
#include "Helpers.h"
1513
#include "ModuleInternalCallbacks.h"
1614
#include "Runtime.h"
1715
#include "RuntimeConfig.h"
1816
#include "Worker.h"
17+
#include "robin_hood.h"
1918

2019
// Use centralized dev flags helper for logging
2120

@@ -165,7 +164,8 @@ void SetDevBootComplete(v8::Isolate* isolate, v8::Local<v8::Context> context, bo
165164
size_t start = 0;
166165
while (start <= query.size()) {
167166
size_t amp = query.find('&', start);
168-
std::string pair = (amp == std::string::npos) ? query.substr(start) : query.substr(start, amp - start);
167+
std::string pair =
168+
(amp == std::string::npos) ? query.substr(start) : query.substr(start, amp - start);
169169
if (!pair.empty()) {
170170
size_t eq = pair.find('=');
171171
std::string name = (eq == std::string::npos) ? pair : pair.substr(0, eq);
@@ -200,7 +200,7 @@ void SetDevBootComplete(v8::Isolate* isolate, v8::Local<v8::Context> context, bo
200200
// enters the module registry key (identity stays the canonical URL),
201201
// and the server and the registry never see a varied URL.
202202
static std::mutex g_bustNextFetchMutex;
203-
static std::unordered_set<std::string> g_bustNextFetchKeys;
203+
static robin_hood::unordered_set<std::string> g_bustNextFetchKeys;
204204

205205
void MarkUrlsForCacheBust(const std::vector<std::string>& urls) {
206206
if (urls.empty()) return;
@@ -277,7 +277,7 @@ static bool PerformHttpFetchOnceSync(const std::string& url, std::string& out,
277277
static inline void InvokeHttpFetchYield();
278278

279279
static std::mutex g_prefetchMutex;
280-
static std::unordered_map<std::string, std::string> g_prefetchCache;
280+
static robin_hood::unordered_map<std::string, std::string> g_prefetchCache;
281281

282282
// Always-on diagnostic counters. These intentionally do NOT gate behind
283283
// IsScriptLoadingLogEnabled() — without this signal we cannot tell a
@@ -302,11 +302,12 @@ static bool PerformHttpFetchOnceSync(const std::string& url, std::string& out,
302302
static std::atomic<size_t> g_fetchSyncSlow{0}; // >=100ms
303303
static constexpr size_t kFetchSyncSummaryEvery = 100;
304304

305-
bool HttpFetchText(const std::string& url, std::string& out, std::string& contentType, int& status) {
305+
bool HttpFetchText(const std::string& url, std::string& out, std::string& contentType,
306+
int& status) {
306307
// Security gate: check if remote module loading is allowed before any HTTP fetch.
307308
// This is the single point of enforcement for all HTTP module loading.
308309
if (!IsRemoteUrlAllowed(url)) {
309-
status = 403; // Forbidden
310+
status = 403; // Forbidden
310311
if (IsScriptLoadingLogEnabled()) {
311312
Log(@"[http-esm][security][blocked] %s", url.c_str());
312313
}
@@ -478,7 +479,10 @@ static bool PerformHttpFetchOnceSync(const std::string& url, std::string& out,
478479
}
479480

480481
NSURL* u = [NSURL URLWithString:[NSString stringWithUTF8String:fetchUrl.c_str()]];
481-
if (!u) { status = 0; return false; }
482+
if (!u) {
483+
status = 0;
484+
return false;
485+
}
482486

483487
NSError* err = nil;
484488
NSInteger httpStatusLocal = 0;
@@ -771,7 +775,7 @@ void ClearHttpModulePrefetchCache() {
771775

772776
struct KickstartContext {
773777
std::mutex mutex;
774-
std::unordered_set<std::string> visited;
778+
robin_hood::unordered_set<std::string> visited;
775779
std::atomic<size_t> fetchedCount{0};
776780
std::atomic<size_t> bytes{0};
777781
dispatch_group_t group = nullptr;
@@ -1342,4 +1346,4 @@ void InitializeHmrDevGlobals(v8::Isolate* isolate, v8::Local<v8::Context> contex
13421346
MirrorGlobalOnGlobalThis(isolate, context, "__NS_DEV__");
13431347
}
13441348

1345-
} // namespace tns
1349+
} // namespace tns

‎NativeScript/runtime/ModuleInternalCallbacks.h‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,24 @@
33
#include <v8.h>
44

55
#include <string>
6-
#include <unordered_map>
76
#include <vector>
87

8+
#include "robin_hood.h"
9+
910
namespace tns {
1011

12+
// Canonical module key → compiled-module handle map used by the per-isolate
13+
// registries below.
14+
using ModuleHandleMap =
15+
robin_hood::unordered_map<std::string, v8::Global<v8::Module>>;
16+
1117
// Per-isolate module registry accessor: map canonical keys → compiled
1218
// v8::Module handles for `isolate`. Keyed by v8::Isolate* (not thread) because
1319
// v8::Global<Module> handles are isolate-bound; see the long-form comment above
1420
// the definition in ModuleInternalCallbacks.mm for the cross-isolate-handle bug
1521
// this prevents. Callers bind a local alias, e.g.
1622
// `auto& g_moduleRegistry = tns::ModuleRegistryFor(isolate);`.
17-
std::unordered_map<std::string, v8::Global<v8::Module>>& ModuleRegistryFor(
18-
v8::Isolate* isolate);
23+
ModuleHandleMap& ModuleRegistryFor(v8::Isolate* isolate);
1924

2025
// Reset + drop every module handle owned by `isolate`. Must be called while the
2126
// isolate is still alive (the Runtime destructor calls this before disposal).

‎NativeScript/runtime/ModuleInternalCallbacks.mm‎

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
#include <mutex>
1212
#include <queue>
1313
#include <string>
14-
#include <unordered_map>
15-
#include <unordered_set>
1614
#include <vector>
1715
#include "DevFlags.h"
1816
#include "HMRSupport.h"
@@ -270,8 +268,6 @@ static inline bool StartsWith(const std::string& s, const char* prefix) {
270268
}
271269

272270
namespace {
273-
using ModuleHandleMap = std::unordered_map<std::string, v8::Global<v8::Module>>;
274-
275271
// The four module-handle maps for a single isolate. v8::Global<Module> handles
276272
// are bound to the isolate that created them, so each isolate (main + every
277273
// Worker) keeps its own set; they are looked up by v8::Isolate* below.
@@ -289,9 +285,10 @@ static inline bool StartsWith(const std::string& s, const char* prefix) {
289285
return *mutex;
290286
}
291287

292-
std::unordered_map<v8::Isolate*, std::unique_ptr<PerIsolateModuleState>>& ModuleStateTable() {
288+
robin_hood::unordered_map<v8::Isolate*, std::unique_ptr<PerIsolateModuleState>>&
289+
ModuleStateTable() {
293290
static auto* table =
294-
new std::unordered_map<v8::Isolate*, std::unique_ptr<PerIsolateModuleState>>();
291+
new robin_hood::unordered_map<v8::Isolate*, std::unique_ptr<PerIsolateModuleState>>();
295292
return *table;
296293
}
297294

@@ -336,7 +333,7 @@ static inline bool StartsWith(const std::string& s, const char* prefix) {
336333
// Each access site binds a local reference (e.g.
337334
// `auto& g_moduleRegistry = ModuleRegistryFor(isolate);`) so existing bodies
338335
// stay byte-for-byte the same while becoming isolate-correct.
339-
std::unordered_map<std::string, v8::Global<v8::Module>>& ModuleRegistryFor(v8::Isolate* isolate) {
336+
ModuleHandleMap& ModuleRegistryFor(v8::Isolate* isolate) {
340337
return ModuleStateFor(isolate).registry;
341338
}
342339

@@ -378,7 +375,7 @@ void DestroyModuleStateForIsolate(v8::Isolate* isolate) {
378375
// Instead of rewriting import statements in source code on the Vite side, the runtime
379376
// resolves bare specifiers through this map to either vendor URLs (ns-vendor://)
380377
// or HTTP module URLs. Source code is served as Vite transformed it.
381-
static std::unordered_map<std::string, std::string> g_importMap;
378+
static robin_hood::unordered_map<std::string, std::string> g_importMap;
382379

383380
// Volatile URL patterns: URLs matching these substrings are always re-fetched
384381
// (cache is evicted before loading). Configured by Vite at boot instead of
@@ -1100,7 +1097,7 @@ void InvalidateModules(v8::Isolate* isolate, v8::Local<v8::Context> context,
11001097
auto& g_moduleRegistry = ModuleRegistryFor(isolate);
11011098
if (urls.empty()) return;
11021099

1103-
std::unordered_set<std::string> seen;
1100+
robin_hood::unordered_set<std::string> seen;
11041101
std::vector<std::string> uniqueUrls;
11051102
uniqueUrls.reserve(urls.size());
11061103

@@ -1216,12 +1213,12 @@ void UpdateModuleFallback(v8::Isolate* isolate, const std::string& canonicalPath
12161213

12171214
// Track active resolution stack to detect and short-circuit self-recursive module loads
12181215
static thread_local std::vector<std::string> g_moduleResolutionStack;
1219-
static thread_local std::unordered_map<std::string, size_t> g_moduleReentryCounts;
1220-
static thread_local std::unordered_map<std::string, std::unordered_set<std::string>>
1216+
static thread_local robin_hood::unordered_map<std::string, size_t> g_moduleReentryCounts;
1217+
static thread_local robin_hood::unordered_map<std::string, robin_hood::unordered_set<std::string>>
12211218
g_moduleReentryParents;
1222-
static thread_local std::unordered_map<std::string, std::string> g_modulePrimaryImporters;
1223-
static thread_local std::unordered_set<std::string> g_modulesInFlight;
1224-
static thread_local std::unordered_set<std::string> g_modulesPendingReset;
1219+
static thread_local robin_hood::unordered_map<std::string, std::string> g_modulePrimaryImporters;
1220+
static thread_local robin_hood::unordered_set<std::string> g_modulesInFlight;
1221+
static thread_local robin_hood::unordered_set<std::string> g_modulesPendingReset;
12251222
// The threshold for detecting circular dependencies during module resolution.
12261223
// 256 was chosen as a high enough value to allow deep but legitimate module graphs,
12271224
// but low enough to catch runaway recursion or infinite circular imports.
@@ -1230,10 +1227,11 @@ void UpdateModuleFallback(v8::Isolate* isolate, const std::string& canonicalPath
12301227
static constexpr size_t kMaxModuleReentryCount = 256;
12311228
// Waiters: module path -> list of Promise resolvers waiting for completion (instantiated/evaluated
12321229
// or errored)
1233-
static std::unordered_map<std::string, std::vector<v8::Global<v8::Promise::Resolver>>>
1230+
static robin_hood::unordered_map<std::string, std::vector<v8::Global<v8::Promise::Resolver>>>
12341231
g_moduleWaiters;
12351232
// Dynamic HTTP import waiters: resolve to module namespace when available.
1236-
static thread_local std::unordered_map<std::string, std::vector<v8::Global<v8::Promise::Resolver>>>
1233+
static thread_local robin_hood::unordered_map<std::string,
1234+
std::vector<v8::Global<v8::Promise::Resolver>>>
12371235
g_httpDynamicWaiters;
12381236

12391237
static bool IsModuleEvaluationInProgress(v8::Module::Status status) {

0 commit comments

Comments
 (0)