Visitar URL original
fix: runtime lifetime — deferred leaks, cross-isolate sharing, startup robustness by edusperoni · Pull Request #2013 · NativeScript/android · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: move isolate-bound objects into RuntimeState
`isolateBoundObjects_` was a process-wide Isolate*-keyed map behind a mutex --
the same shape RuntimeState exists to remove -- and it held exactly one object
per runtime: Timers.

Timers now lives in RuntimeState like every other per-runtime subsystem, so the
map, its mutex and the unique_void_ptr machinery are deleted rather than made
inline, which resolves the ODR item by removing the definitions. disposeIsolate
stays for the two builtin-layer hooks.

Timers is consequently destroyed at m_state->Clear() instead of inside
disposeIsolate. ~Timers -> Destroy() touches only its own task map, the event
loop and the tasks' Java token peers -- nothing torn down in between -- and both
the isolate and JNI are still alive at Clear(), which is what resetting the task
handles and deleting the token global refs require.
  • Loading branch information
edusperoni committed Aug 15, 2026
commit 1903726f88cfafe2db7ab13f4e486c49466ef368
7 changes: 0 additions & 7 deletions test-app/runtime/src/main/cpp/IsolateDisposer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,5 @@ namespace tns {
void disposeIsolate(v8::Isolate *isolate) {
tns::NsBuiltinModules::onDisposeIsolate(isolate);
tns::BuiltinLoader::onDisposeIsolate(isolate);
// clear all isolate bound objects
std::lock_guard<std::mutex> lock(isolateBoundObjectsMutex_);
auto it = isolateBoundObjects_.find(isolate);
if (it != isolateBoundObjects_.end()) {
it->second->clear();
isolateBoundObjects_.erase(it);
}
}
}
31 changes: 2 additions & 29 deletions test-app/runtime/src/main/cpp/IsolateDisposer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,11 @@
#ifndef TEST_APP_ISOLATEDISPOSER_H
#define TEST_APP_ISOLATEDISPOSER_H
#include "v8.h"
#include "robin_hood.h"
#include <vector>
#include <mutex>
#include <memory>

namespace tns {
// Runs the builtin layers' isolate-teardown hooks. Subsystem state that
// belongs to one runtime lives in RuntimeState instead; see RuntimeState.h.
void disposeIsolate(v8::Isolate* isolate);
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
delete p;
});
}
// inline: this header is included by three TUs, and a namespace-scope
// definition would be a strong symbol in each of them.
inline robin_hood::unordered_map<v8::Isolate*, std::shared_ptr<std::vector<unique_void_ptr>>> isolateBoundObjects_;
inline std::mutex isolateBoundObjectsMutex_;
template<typename T>
void registerIsolateBoundObject(v8::Isolate* isolate, T *ptr) {
std::lock_guard<std::mutex> lock(isolateBoundObjectsMutex_);
auto it = isolateBoundObjects_.find(isolate);
if (it == isolateBoundObjects_.end()) {
auto vec = std::make_shared<std::vector<unique_void_ptr>>();
vec->push_back(unique_void(ptr));
isolateBoundObjects_.emplace(isolate, vec);
} else {
it->second->push_back(unique_void(ptr));
}
}
}

#endif //TEST_APP_ISOLATEDISPOSER_H
10 changes: 7 additions & 3 deletions test-app/runtime/src/main/cpp/Timers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "Runtime.h"
#include "NativeScriptException.h"
#include "ModuleBinding.h"
#include "IsolateDisposer.h"
#include "RuntimeState.h"
#include "NativeScriptPlatform.h"
#include "Util.h"
#include <cmath>
Expand Down Expand Up @@ -378,9 +378,13 @@ bool Timers::RunIfEarliest(double now, double otherDue) {
}

void Timers::InitStatic(v8::Isolate* isolate, v8::Local<v8::ObjectTemplate> globalObjectTemplate) {
auto timers = new Timers();
// Owned by the runtime, which destroys it while the isolate is still alive
// -- what releasing the tasks' handles and their Java token peers requires.
auto* timers = RuntimeState::For<Timers>(isolate);
if (timers == nullptr) {
return;
}
timers->Init(isolate, globalObjectTemplate);
registerIsolateBoundObject(isolate, timers);
}

};
Expand Down