@@ -313,6 +313,9 @@ Runtime::~Runtime() {
313313 s_isolate2RuntimesCache.erase (it);
314314 }
315315 }
316+ // Same backstop for the breadcrumb slot Init took: the table is small and
317+ // fixed, so slots lost to failed bootstraps would crowd out live runtimes.
318+ CrashBreadcrumbs::UnregisterRuntime (m_id);
316319
317320 delete this ->m_objectManager ;
318321 // idempotent backstop for the matched erase WorkerWrapper does right after
@@ -712,9 +715,11 @@ void Runtime::ElectMainRuntime() {
712715 s_mainRuntimeElected = true ;
713716 s_mainRuntimeFailed = false ;
714717 m_isMainThread = true ;
715- // Once per process: V8::Initialize freezes the flag list, and setting a
716- // flag afterwards aborts.
717- InitializeV8 ();
718+ // Once per process, not once per election: a main runtime that failed
719+ // hands the election back, and V8 aborts both on a second
720+ // InitializePlatform and on a flag set after V8::Initialize froze the list.
721+ static std::once_flag v8Initialized;
722+ std::call_once (v8Initialized, InitializeV8);
718723 return ;
719724 }
720725
@@ -760,6 +765,12 @@ void Runtime::UnwindFailedInit() {
760765 DestroyRuntime ();
761766 }
762767 m_isolate->Dispose ();
768+ // The ~Runtime backstop keys on m_isolate, which is cleared below, so the
769+ // platform's loop entry has to go here. Left behind, it would hand the
770+ // stopped loop to the next isolate allocated at this address.
771+ if (m_eventLoop != nullptr ) {
772+ NativeScriptPlatform::Instance ()->IsolateDisposed (m_isolate, m_eventLoop);
773+ }
763774 m_isolate = nullptr ;
764775 }
765776
@@ -1071,7 +1082,15 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath,
10711082 // Do not build metadata (which should be static for the process) for non-main
10721083 // threads
10731084 if (m_isMainThread) {
1074- MetadataNode::BuildMetadata (filesPath);
1085+ // Once per process, like V8 itself: the tree is process-wide state that
1086+ // outlives the runtime that built it, so a main runtime elected after an
1087+ // earlier one failed past this point reads the tree already there. Only
1088+ // the elected main runtime gets here, one at a time.
1089+ static bool metadataBuilt = false ;
1090+ if (!metadataBuilt) {
1091+ MetadataNode::BuildMetadata (filesPath);
1092+ metadataBuilt = true ;
1093+ }
10751094 }
10761095
10771096 auto enableProfiler = !profilerOutputDir.empty ();
@@ -1089,6 +1108,7 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath,
10891108 s_currentRuntime = this ;
10901109
10911110 if (m_isMainThread) {
1111+ s_mainRuntime.store (this , std::memory_order_release);
10921112 // Releases any runtime waiting in ElectMainRuntime: the metadata tree and
10931113 // the main event loop they depend on are published by now.
10941114 SignalMainRuntimeReady (false /* failed */ );
@@ -1164,6 +1184,9 @@ void Runtime::DestroyRuntime() {
11641184 if (s_currentRuntime == this ) {
11651185 s_currentRuntime = nullptr ;
11661186 }
1187+ Runtime* self = this ;
1188+ s_mainRuntime.compare_exchange_strong (self, nullptr ,
1189+ std::memory_order_acq_rel);
11671190 // The events state holds v8::Global handles (backing event target, dispatch
11681191 // closures and tracked promise rejections) - reset them while the isolate
11691192 // is still alive.
@@ -1251,6 +1274,7 @@ bool Runtime::s_mainRuntimeFailed = false;
12511274v8::Platform* Runtime::platform = nullptr ;
12521275int Runtime::m_androidVersion = Runtime::GetAndroidVersion();
12531276std::shared_ptr<EventLoop> Runtime::s_mainEventLoop;
1277+ std::atomic<Runtime*> Runtime::s_mainRuntime{nullptr };
12541278
12551279thread_local Runtime* Runtime::s_currentRuntime = nullptr ;
12561280thread_local PendingIsolateSetup Runtime::s_pendingIsolateSetup;
0 commit comments