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
Use LongLivedObjects for TurboModule callbacks #10436
Conversation
Hello @vmoroz! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
@vmoroz Is this important enough to get backported into 0.70? |
Yes, it is important. Without this fix we have a risk of crashing every time when a RN instance is unloaded. |
* Use LongLivedObjects for TurboModule callbacks (#10436) * Use "prerelease" for the change type Co-authored-by: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Co-authored-by: dannyvv <dannyvv@microsoft.com>
We're seeing crashes when the React instance is destroyed if there is an active ImageLoader::getSizeWithHeaders request in flight (related to JSI trying to create an object on a destroyed runtime). This is similar to an issue that was resolved and picked into React Native Windows v0.68 (microsoft#10436), but even with that fix, the crash still occurs. I suspect it may be related to using the ReactPromise copy constructor, but even if not, it's not so bad to avoid copying the ReactPromise.
We're seeing crashes when the React instance is destroyed if there is an active ImageLoader::getSizeWithHeaders request in flight (related to JSI trying to create an object on a destroyed runtime). This is similar to an issue that was resolved and picked into React Native Windows v0.68 (microsoft#10436), but even with that fix, the crash still occurs. I suspect it may be related to using the ReactPromise copy constructor, but even if not, it's not so bad to avoid copying the ReactPromise.
We're seeing crashes when the React instance is destroyed if there is an active ImageLoader::getSizeWithHeaders request in flight (related to JSI trying to create an object on a destroyed runtime). This is similar to an issue that was resolved and picked into React Native Windows v0.68 (microsoft#10436), but even with that fix, the crash still occurs. I suspect it may be related to using the ReactPromise copy constructor, but even if not, it's not so bad to avoid copying the ReactPromise.
Description
Type of Change
Why
@acoates-ms has reported an issue with RNW Turbo Module implementation where we see a crash after a RN instance is unloaded. The crash is caused by a JSI
Function
that captured in a lambda used for asynchronous callbacks or in aPromise
.We destroy JS engine instance when unloading the RN instance, and the JSI
Function
cannot be safely released anymore.What
RN Turbo Modules that wrap up CxxModules use
LongLivedObject
andLongLivedObjectCollection
as a mechanism to address similar issues. The callbacks are wrapped up intoCallbackWrapper
class inherited from theLongLivedObject
. TheLongLivedObjectCollection
is cleared when JS engine instance is shut down and it releases all associatedstd::shared_ptr<LongLivedObject>
instances, which in turn release all associated JSI values. The deferred callback and Promise lambdas capturestd::weak_ptr<LongLivedObject>
, and do nothing if the weak pointer cannot be "locked" anymore.The PR uses the same
LongLivedObjectCollection
for RNW Turbo Modules, but it introduces newLongLivedJsiRuntime
andLongLivedJsiValue
classes inherited fromLongLivedObject
. They have smaller size and do not use globalLongLivedObjectCollection
instance.The solution consists of the following parts:
JSDispatcherWriter
keepsstd::weak_ptr<LongLivedJsiRuntime>
instead of JSIRuntime
and does nothing while handling results if the JSIRuntime
is not available anymore.TurboModulesProvider
is wrapping up JSIFunction
instances intostd::weak_ptr<LongLivedJsiFunction>
. (TheLongLivedJsiFunction
is an alias forLongLivedJsiValue<jsi::Function>
.) It helps to release all JSIFunction
instances while the JS engine instance is shutting down.ReactNativeHost
creates a new instance ofLongLivedObjectCollection
and passes it to theTurboModulesProvider
.InstanceImpl
inOInstance.h
passes theLongLivedObjectCollection
instance fromTurboModulesProvider
to theOJSIExecutorFactory
. TheOJSIExecutorFactory
associates theLongLivedObjectCollection
instance with a JS engine instance by passing it to theTurboModuleBinding::install
call.Testing
We added new unit tests in
TurboModuleTests.cpp
to check behavior when callbacks and Promises are released after RN instance unload. They are all passing. These tests cause a crash without changes to theTurboModulesProvider
.Also, we added a new test utility class
TestNotificationService
that helps to coordinate setting and waiting for synchronization events. Unlike theTestEventService
that expects synchronization events to appear in a specified order, the newTestNotificationService
keeps a set of events and lets theWait
function to continue if the set contains an event that it waits for.Microsoft Reviewers: Open in CodeFlow