-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
fix: crash on OTA reload #7196
base: main
Are you sure you want to change the base?
fix: crash on OTA reload #7196
Conversation
cc @lukmccall |
@@ -249,3 +256,38 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti | |||
this.#reanimatedModuleProxy.unregisterCSSTransition(viewTag); | |||
} | |||
} | |||
|
|||
class DummyReanimatedModuleProxy implements ReanimatedModuleProxy { |
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.
I've asked my friend GPT about JSProxy that provides a default fallback implementation for undefined methods, and he suggested me something like that:
// Define a default method to be used when a non-existent method is called
function defaultMethod(...args) {
console.log("Default method triggered with arguments: ", args);
}
// Create a handler object with a `get` trap
const handler = {
get(target, prop, receiver) {
// If the property exists, return it
if (prop in target) {
return target[prop];
}
// If the property does not exist, return the default method
return function(...args) {
defaultMethod(...args);
};
}
};
// Define an object with some methods
const originalObject = {
existingMethod: function() {
console.log("Existing method called");
}
};
// Create a proxy for the object
const proxyObject = new Proxy(originalObject, handler);
// Test invoking existing method
proxyObject.existingMethod(); // Output: Existing method called
// Test invoking a non-existent method
proxyObject.nonExistentMethod(1, 2, 3); // Output: Default method triggered with arguments: [ 1, 2, 3 ]
we could avoid listing all methods here, and it would work for methods that we add in a future
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.
While using proxy looks cool I think simpler code would be more maintainable. You also you need extra methods for functions that return arguments, like subscribeForKeyboardEvent
etc.
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.
Also, TypeScript will have our back if we forget to add some methods.
Summary
Fixes #7182
Requires #7197
Currently on iOS React Native can go into a non-fatal race condition on a double reload. Double reload can happen during an OTA update, when an app is reloaded immediately after evaluating the bundle.
The flow goes more or less like this. I haven't explored it to the deepest details.
RCTModuleRegistry
.WorkletsModule.installTurboModule()
is called from the first runtime.WorkletsModule
is fetched from theRCTModuleRegistry
.RCTModuleRegistry
and starts to populate it. I don't know why both runtimes target the same instance ofRCTModuleRegistry
.WorkletsModule.installTurboModule()
finishes successfully on the first runtime.ReanimatedModule.installTurboModule()
is invoked in quick succession on the first runtime.ReanimatedModule
tries to fetchWorkletsModule
fromRCTModuleRegistry
. However,RCTModuleRegistry
was wiped and it doesn't have the fully installed instance ofWorkletsModule
anymore. Therefore a new instance ofWorkletsModule
is created and inserted into theRCTModuleRegistry
.ReanimatedModule.installTurboModule()
is invoked only afterWorkletsModule.installTurboModule()
. We follow that heuristic in Java and therefore try operating on a non-installed instance ofWorkletsModule
resulting in aNullPtrException
.We can effectively detect that flow and ignore it, since that race condition is fatal only for the first runtime which is torn down soon after anyway.
See the provided screenshots which illustrate the problems.
moduleRegistry
forWorkletsModule
during a healthy execution ofWorkletsModule.installTurboModule()
moduleRegistry
forWorkletsModule
during race-condition execution. Note that the list of the modules is much shorter and thatWorkletsModule
isn't there.Test plan
To reproduce this issue, reload twice on iOS in
fabric-example
. For best effect try to reload after ~0.5s after the first time, but it's easily reproducible if the App isn't slowed heavily by debugger instructions.With these changes Reanimated doesn't crash, but the race condition continues to manifest.