-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hermes reference tracking #563
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #563 +/- ##
==========================================
- Coverage 89.70% 89.70% -0.01%
==========================================
Files 331 331
Lines 19794 19794
Branches 1949 1949
==========================================
- Hits 17757 17756 -1
- Misses 2023 2024 +1
Partials 14 14 ☔ View full report in Codecov by Sentry. |
/canary |
auto ref = newObjectCxxArgs(); | ||
return ref; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: unnecessary local var (applies many places)
for (auto& ptr : *runtimeScope_->valueScope) { | ||
ptr.second.reset(); | ||
} | ||
for (auto& ptr : *runtimeScope_->objectScope) { | ||
ptr.second.reset(); | ||
} | ||
for (auto& ptr : *runtimeScope_->functionScope) { | ||
ptr.second.reset(); | ||
} | ||
for (auto& ptr : *runtimeScope_->arrayScope) { | ||
ptr.second.reset(); | ||
} | ||
for (auto& ptr : *runtimeScope_->symbolScope) { | ||
ptr.second.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should all be private implementation details of RuntimeScope::release
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Secondarily, this should also probably clear the keys too - as we discovered earlier.
@@ -107,12 +106,12 @@ class JHermesRuntime : public HybridClass<JHermesRuntime, JJSIRuntime> { | |||
private: | |||
friend HybridBase; | |||
std::unique_ptr<HermesRuntime> runtime_; | |||
global_ref<JHermesConfig::jhybridobject> jConfig_; | |||
std::vector<global_ref<JHybridClass::jhybridobject>> scope_; | |||
::global_ref<JHermesConfig::jhybridobject> jConfig_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
::
?
// TODO: From my current understanding, holding a global prevents the class from being | ||
// GC'd when it goes out of scope in Java land, which would prevent the JSI Value reference | ||
// from being reset. I originally wanted to keep a weak_ref, but was getting intermittent | ||
// segfaults from untracked (or incorrectly tracked) references that were then not being | ||
// released because the weak_ref was out of scope. The assumption there was that if the | ||
// weak_ref was out of scope, it was already released. But this doesn't seem to be true, | ||
// otherwise we wouldn't be getting segfaults. Making them global does prevent the segfaults, | ||
// reaffirming the above. Since all the wrapper classes are holding are pointers | ||
// this probably isn't a huge deal, but is certainly an inefficiency we should look | ||
// to remove. I'd like to just create weak_ptrs on the JSI values themselves, | ||
// which requires the holders to use shared_ptrs and expose a getter for accessing. | ||
// Then we'd also need a way to hold arbitrary weak_ptrs. We could also maybe try | ||
// to get weak refs to _just_ the CXX parts of the hybrid class to avoid leaking things | ||
// on the JVM side? | ||
// TODO: Maybe we could do a periodic pruning of the vector to remove obsolete refs? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful - removing longstanding paragraphs of TODOs are always great to see.
@@ -39,8 +39,7 @@ void JJSIPreparedJavaScript::registerNatives() { | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't put a comment on a diff that isn't modified - comment at the top of this file can now be removed as well:
player/jvm/hermes/src/main/jni/JJSIValue.cpp
Lines 6 to 15 in dc3c3ab
/** | |
* IMPORTANT: Our current scoping strategy is to ensure all JHybridClass constructs (JSI Value wrappers) | |
* are tracked as refs with the runtime that they're created in. This allows the runtime to explicitly | |
* release any lingering runtime pointer values before releasing the runtime itself. This prevents dangling | |
* pointers that could cause undefined behavior (likely segfault - or assert if in debug mode). | |
* | |
* Currently, this requires careful consideration for every newObjectCxxArgs call. If it creates a wrapper | |
* around a runtime PointerValue, and we don't have a guarantee it won't go out of scope, then we need to | |
* make sure it's tracked. TODO: Make it more foolproof, maybe inside the ctors for our wrappers | |
*/ |
jvm/hermes/src/main/jni/JJSIValue.h
Outdated
explicit JJSIValue(std::shared_ptr<RuntimeScope> scope, Value&& value) : HybridClass(), scope_(scope) { | ||
// internally creates unique ptr | ||
scope->trackValue(this, std::move(value)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If/when there is a singular API to track arbitrary runtime types, we should try to capture this as a common super constructor for JJSIValueHybridClass
to enforce for any subclass (there are some new ones that may exist in the future).
~JJSIValue() override { | ||
release(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still necessary?
if (!tracked && value_) value_.reset(); | ||
if (scope_) scope_->clearRef(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I get why this is necessary - but maybe the !tracked && value_
case could just make it's own RuntimeScope
so that all the APIs still go through a scope and you don't need explicit logic to handle both cases.
jvm/hermes/src/main/jni/JJSIValue.h
Outdated
if (auto ref = scope_->getValue((void *)this)) { | ||
return *ref; | ||
} else { | ||
throw std::runtime_error("Value reference cleaned up and is no longer in RuntimeScope"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- This should probably just go through
throwNativeHandleReleasedException("Value");
from below - If you don't go through the helper function, you need to tell the JNI to expect an exception, like
throwNativeHandleReleasedException
does (and should also probably wrap in aPlayerException
- likethrowNativeHandleReleasedException
does)
if (it != valueScope->end()) { | ||
return valueScope->at(ptr).get(); | ||
} | ||
throw std::runtime_error("Value was not found in the RuntimeScope"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why throw here? The return type is nullable, it should just return nullptr
.
Otherwise, change return type to Value&
to be more accurate (although, my preference would be keep nullable return type to match standard map APIs and let the caller handle nullptr
explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would apply to all the pointers, right?
No longer overloading the global JNI table, tracking references in C++ instead to avoid over-usage crash
Change Type (required)
Indicate the type of change your pull request is:
patch
minor
major
N/A
Does your PR have any documentation updates?
📦 Published PR as canary version:
0.10.1--canary.563.18922
Try this version out locally by upgrading relevant packages to 0.10.1--canary.563.18922