Skip to content
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

[ios] useAnimatedKeyboard causes crash when reloading app #3786

Closed
danscan opened this issue Nov 17, 2022 · 6 comments · Fixed by #3932
Closed

[ios] useAnimatedKeyboard causes crash when reloading app #3786

danscan opened this issue Nov 17, 2022 · 6 comments · Fixed by #3932
Labels
Needs review Issue is ready to be reviewed by a maintainer Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snippet of code, snack or repo is provided

Comments

@danscan
Copy link

danscan commented Nov 17, 2022

Description

When using the useAnimatedKeyboard hook on iOS, attempting to reload the app causes a crash.

I've observed this with:

  • react-native-reanimated 2.10.0, 2.11.0 and 2.12.0

  • expo 46 and 47

  • react-native 0.69 and 0.70

  • in both debug and release schemes (on release, via expo's Updates.reloadAsync())

  • on both the iOS simulator and a real device

  • Using JSC, the crash happens immediately.

  • Using hermes, sometimes the crash doesn't happen until you focus a text input (presenting the keyboard) after reloading the app.

I created two snacks to demonstrate the crash:

Steps to reproduce

  1. Add useAnimatedKeyboard() to any component that's rendered in your app.
  2. Reload the app.
  3. Observe the app crash.

Snack or a link to a repository

https://snack.expo.dev/@danscan/fa5482

Reanimated version

2.12.0

React Native version

0.70.5

Platforms

iOS

JavaScript runtime

JSC

Workflow

Expo managed workflow

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

iOS simulator

Device model

No response

Acknowledgements

Yes

@danscan danscan added the Needs review Issue is ready to be reviewed by a maintainer label Nov 17, 2022
@github-actions github-actions bot added Repro provided A reproduction with a snippet of code, snack or repo is provided Platform: iOS This issue is specific to iOS labels Nov 17, 2022
@danscan
Copy link
Author

danscan commented Nov 17, 2022

Here's a screenshot of the exception I encounter when attempting to reload my app with useAnimatedKeyboard in xcode:

Screen Shot 2022-11-16 at 4 43 35 PM

@Norfeldt
Copy link
Contributor

I think I have a similar issue (declaring runOnJS - not calling it) and I think it's due to some xcode 14.1 issue.

Screenshot 2022-11-21 at 11 22 39

I bet it works when you open it on a real iPhone and it's only the simulator that crashes. Is this correct?

@danscan
Copy link
Author

danscan commented Nov 21, 2022

@Norfeldt I think this may be different, and not just an xcode version issue. I'm encountering it in debug mode on xcode 14.0.1, as well as in the Expo Snack I provided for repro. This also occurs in release builds and on real iPhones when reloading the app (e.g. via expo's Updates.reloadAsync())

@Norfeldt
Copy link
Contributor

Thanks for investigating if it is the same issue. Sorry for the disturbance.

@justinkx
Copy link

justinkx commented Dec 2, 2022

Got similar crash in iOS when tried to reload using expo Updates.reloadAsync()

EXC_BAD_ACCESS RCTModalHostViewController
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: BUS_NOOP
Crashed Thread: 2
Application Specific Information:
ttcf > XTUM >
Attempted to dereference null pointer.
Thread 2 name: com.facebook.react.JavaScript Crashed:
0                     0x20272605c         [inlined] facebook::jsi::Pointer::~Pointer (jsi.h:371)
1                     0x20272605c         [inlined] facebook::jsi::Object::~Object (jsi.h:562)
2                     0x20272605c         [inlined] facebook::jsi::Function::~Function (jsi.h:854)
3                     0x20272605c         [inlined] facebook::jsi::Function::~Function (jsi.h:854)
4                     0x20272605c         std::__1::__shared_ptr_emplace<T>::__on_zero_shared (shared_ptr.h:315)
5                     0x202698514         [inlined] std::__1::__shared_count::__release_shared (shared_ptr.h:177)
6                      0x202698514         [inlined] std::__1::__shared_weak_count::__release_shared (shared_ptr.h:219)
7                      0x202698514         std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:959)
8                      0x20282b1d8         [inlined] std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:957)
9                      0x20282b1d8         [inlined] reanimated::HostFunctionHandler::~HostFunctionHandler (HostFunctionHandler.h:11)
10                     0x20282b1d8         reanimated::HostFunctionHandler::~HostFunctionHandler (HostFunctionHandler.h:11)
11                     0x202698514         [inlined] std::__1::__shared_count::__release_shared (shared_ptr.h:177)
12                     0x202698514         [inlined] std::__1::__shared_weak_count::__release_shared (shared_ptr.h:219)
13                     0x202698514         std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:959)
14                     0x20282bd3c         [inlined] std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:957)
15                     0x20282bd3c         [inlined] reanimated::ShareableValue::toJSValue::lambda::~lambda (ShareableValue.cpp:326)
16                     0x20282bd3c         [inlined] reanimated::ShareableValue::toJSValue::lambda::~lambda (ShareableValue.cpp:326)
17                     0x20282bd3c         [inlined] std::__1::__compressed_pair_elem<T>::~__compressed_pair_elem (compressed_pair.h:35)
18                     0x20282bd3c         [inlined] std::__1::__compressed_pair<T>::~__compressed_pair (compressed_pair.h:109)
19                     0x20282bd3c         [inlined] std::__1::__compressed_pair<T>::~__compressed_pair (compressed_pair.h:109)
20                     0x20282bd3c         [inlined] std::__1::__function::__alloc_func<T>::destroy (function.h:197)
21                     0x20282bd3c         std::__1::__function::__func<T>::destroy_deallocate (function.h:344)
22                     0x202963c10         [inlined] std::__1::__function::__value_func<T>::~__value_func
23                     0x202963c10         [inlined] std::__1::__function::__value_func<T>::~__value_func (function.h:462)
24                     0x202963c10         [inlined] std::__1::function<T>::~function (function.h:1169)
25                     0x202963c10         [inlined] std::__1::function<T>::~function (function.h:1169)
26                     0x202963c10         [inlined] facebook::jsi::DecoratedHostFunction::~DecoratedHostFunction (decorator.h:25)
27                     0x202963c10         [inlined] facebook::jsi::DecoratedHostFunction::~DecoratedHostFunction (decorator.h:25)
28                     0x202963c10         [inlined] std::__1::__compressed_pair_elem<T>::~__compressed_pair_elem (compressed_pair.h:35)
29                     0x202963c10         [inlined] std::__1::__compressed_pair<T>::~__compressed_pair (compressed_pair.h:109)
30                     0x202963c10         [inlined] std::__1::__compressed_pair<T>::~__compressed_pair (compressed_pair.h:109)
31                     0x202963c10         [inlined] std::__1::__function::__alloc_func<T>::destroy (function.h:197)
32                     0x202963c10         std::__1::__function::__func<T>::destroy_deallocate (function.h:344)
33  hermes                          0x103b24994         facebook::hermes::debugger::Debugger::jsiValueFromHermesValue
34  hermes                          0x103c28768         facebook::jsi::JSError::~JSError
35  hermes                          0x103b9a2dc         facebook::jsi::JSError::~JSError
36  hermes                          0x103b9a824         facebook::jsi::JSError::~JSError
37  hermes                          0x103b1fdbc         facebook::hermes::debugger::Debugger::jsiValueFromHermesValue
38  hermes                          0x103b1dcd4         facebook::hermes::debugger::Debugger::jsiValueFromHermesValue
39                     0x202698514         [inlined] std::__1::__shared_count::__release_shared (shared_ptr.h:177)
40                     0x202698514         [inlined] std::__1::__shared_weak_count::__release_shared (shared_ptr.h:219)
41                     0x202698514         std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:959)
42                     0x20296157c         [inlined] std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:957)
43                     0x20296157c         [inlined] facebook::react::(anonymous namespace)::DecoratedRuntime::~DecoratedRuntime (HermesExecutorFactory.cpp:183)
44                     0x20296157c         facebook::react::(anonymous namespace)::DecoratedRuntime::~DecoratedRuntime (HermesExecutorFactory.cpp:177)
45                     0x202698514         [inlined] std::__1::__shared_count::__release_shared (shared_ptr.h:177)
46                     0x202698514         [inlined] std::__1::__shared_weak_count::__release_shared (shared_ptr.h:219)
47                     0x202698514         std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:959)
48                     0x202960d84         [inlined] std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:957)
49                     0x202960d84         facebook::react::JSIExecutor::~JSIExecutor (JSIExecutor.h:71)
50                     0x202960f88         [inlined] facebook::react::HermesExecutor::~HermesExecutor (HermesExecutorFactory.h:49)
51                     0x202960f88         [inlined] facebook::react::HermesExecutor::~HermesExecutor (HermesExecutorFactory.h:49)
52                     0x202960f88         facebook::react::HermesExecutor::~HermesExecutor (HermesExecutorFactory.h:49)
53                     0x2028922b4         [inlined] std::__1::__function::__value_func<T>::operator() (function.h:505)
54                     0x2028922b4         [inlined] std::__1::function<T>::operator() (function.h:1182)
55                     0x2028922b4         facebook::react::tryAndReturnError (RCTCxxUtils.mm:74)
56                     0x20289d330         facebook::react::RCTMessageThread::tryFunc (RCTMessageThread.mm:69)
57                     0x20289d674         facebook::react::RCTMessageThread::runOnQueueSync (RCTMessageThread.mm:92)
58                     0x2029361d4         facebook::react::NativeToJsBridge::destroy (NativeToJsBridge.cpp:285)
59                     0x20292ca10         facebook::react::Instance::~Instance (Instance.cpp:38)
60                     0x20288faa0         [inlined] std::__1::default_delete<T>::operator() (unique_ptr.h:57)
61                     0x20288faa0         std::__1::__shared_ptr_pointer<T>::__on_zero_shared (shared_ptr.h:267)
62                     0x20288cdb4         [inlined] std::__1::__shared_count::__release_shared (shared_ptr.h:177)
63                     0x20288cdb4         [inlined] std::__1::__shared_weak_count::__release_shared (shared_ptr.h:219)
64                     0x20288cdb4         [inlined] std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:959)
65                     0x20288cdb4         [inlined] std::__1::shared_ptr<T>::~shared_ptr (shared_ptr.h:957)
66                     0x20288cdb4         [inlined] std::__1::shared_ptr<T>::reset (shared_ptr.h:1054)
67                     0x20288cdb4         __26-[RCTCxxBridge invalidate]_block_invoke (RCTCxxBridge.mm:1302)
68                     0x2028922b4         [inlined] std::__1::__function::__value_func<T>::operator() (function.h:505)
69                     0x2028922b4         [inlined] std::__1::function<T>::operator() (function.h:1182)
70                     0x2028922b4         facebook::react::tryAndReturnError (RCTCxxUtils.mm:74)
71                     0x202888574         -[RCTCxxBridge _tryAndHandleError:] (RCTCxxBridge.mm:344)
72  Foundation                      0x341d1faac         __NSThreadPerformPerform
73  CoreFoundation                  0x34d5f9f50         __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
74  CoreFoundation                  0x34d606328         __CFRunLoopDoSource0
75  CoreFoundation                  0x34d58a20c         __CFRunLoopDoSources0
76  CoreFoundation                  0x34d59fba4         __CFRunLoopRun
77  CoreFoundation                  0x34d5a4ed0         CFRunLoopRunSpecific
78                     0x2028884ac         +[RCTCxxBridge runRunLoop] (RCTCxxBridge.mm:335)
79  Foundation                      0x341d3f804         __NSThread__start__
80  libsystem_pthread.dylib         0x3e75e36c8         _pthread_start

@ansh
Copy link

ansh commented Dec 31, 2022

#3895 opened with reproduction

graszka22 added a commit that referenced this issue Feb 10, 2023
<!-- Thanks for submitting a pull request! We appreciate you spending
the time to work on these changes. Please follow the template so that
the reviewers can easily understand what the code changes affect. -->

## Summary

Fixes
#3786

### Why it crashes: 
On the main thread `CADisplayLink` calls `updateKeyboardFrame` 60 times
per second. Calling `updateKeyboardFrame` calls listener on the JS side.
When reloading the runtime gets destroyed on the JavaScript thread. So
when those two things happen at the same time (which in this case
happens often) we get the crash that we're trying to call a js function
on destroyed js runtime.

### Why don't you just remove the listeners in
`NativeReanimatedModule::~NativeReanimatedModule()`, we're cleaning up
everything here:
I've tried and it appeared to work at first but I still got crashes when
running [the 1000 listeners
example](#3911)
and probably that's why:

![Screenshot 2023-01-11 at 23 02
18](https://user-images.githubusercontent.com/6280457/211935579-16ff642d-fb15-469b-909e-e36ba9d72781.png)
![Screenshot 2023-01-11 at 23 02
35](https://user-images.githubusercontent.com/6280457/211935599-88cb9e81-20d7-4f96-bfd0-9c3b5da13b24.png)

So when `~NativeReanimatedModule` is being called the runtime related
stuff is already deleted and there is a short window of time that the
runtime is being deleted and we still call js stuff, or at least that's
my theory 🤷‍♀️

So my proposition for now is to listen for
`RCTBridgeDidInvalidateModulesNotification` notification. It's called
just before deleting happens. Also I'm using
`RCTUnsafeExecuteOnMainQueueSync` because I want to wait until the
listeners are completely deleted on the main thread and then delete js
stuff.

### A nicer solution? 
If you look a bit above `[[NSNotificationCenter defaultCenter]
postNotificationName:RCTBridgeDidInvalidateModulesNotification` line in
React code you'll see that React calls `invalidate` on all modules' data
before even posting the notification. Maybe we should clean reanimated
stuff here. I haven't explored that though yet and I don't know where is
reanimated's module data and what is module data at all and where to put
that `invalidate` function, I just got this idea while posting this PR.

## Test plan

Just run AnimatedKeyboardExample and reload the app.
Also the [the 1000 listeners
example](#3911)
nicely catches other data races.

Tested with changes from
#3911
and it works
Also tested on FabricExample and surprisingly it also works.
fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this issue Jun 5, 2023
<!-- Thanks for submitting a pull request! We appreciate you spending
the time to work on these changes. Please follow the template so that
the reviewers can easily understand what the code changes affect. -->

## Summary

Fixes
software-mansion#3786

### Why it crashes: 
On the main thread `CADisplayLink` calls `updateKeyboardFrame` 60 times
per second. Calling `updateKeyboardFrame` calls listener on the JS side.
When reloading the runtime gets destroyed on the JavaScript thread. So
when those two things happen at the same time (which in this case
happens often) we get the crash that we're trying to call a js function
on destroyed js runtime.

### Why don't you just remove the listeners in
`NativeReanimatedModule::~NativeReanimatedModule()`, we're cleaning up
everything here:
I've tried and it appeared to work at first but I still got crashes when
running [the 1000 listeners
example](software-mansion#3911)
and probably that's why:

![Screenshot 2023-01-11 at 23 02
18](https://user-images.githubusercontent.com/6280457/211935579-16ff642d-fb15-469b-909e-e36ba9d72781.png)
![Screenshot 2023-01-11 at 23 02
35](https://user-images.githubusercontent.com/6280457/211935599-88cb9e81-20d7-4f96-bfd0-9c3b5da13b24.png)

So when `~NativeReanimatedModule` is being called the runtime related
stuff is already deleted and there is a short window of time that the
runtime is being deleted and we still call js stuff, or at least that's
my theory 🤷‍♀️

So my proposition for now is to listen for
`RCTBridgeDidInvalidateModulesNotification` notification. It's called
just before deleting happens. Also I'm using
`RCTUnsafeExecuteOnMainQueueSync` because I want to wait until the
listeners are completely deleted on the main thread and then delete js
stuff.

### A nicer solution? 
If you look a bit above `[[NSNotificationCenter defaultCenter]
postNotificationName:RCTBridgeDidInvalidateModulesNotification` line in
React code you'll see that React calls `invalidate` on all modules' data
before even posting the notification. Maybe we should clean reanimated
stuff here. I haven't explored that though yet and I don't know where is
reanimated's module data and what is module data at all and where to put
that `invalidate` function, I just got this idea while posting this PR.

## Test plan

Just run AnimatedKeyboardExample and reload the app.
Also the [the 1000 listeners
example](software-mansion#3911)
nicely catches other data races.

Tested with changes from
software-mansion#3911
and it works
Also tested on FabricExample and surprisingly it also works.
ishikawa pushed a commit to ishikawa/react-native-reanimated that referenced this issue Aug 28, 2023
<!-- Thanks for submitting a pull request! We appreciate you spending
the time to work on these changes. Please follow the template so that
the reviewers can easily understand what the code changes affect. -->

Fixes
software-mansion#3786

On the main thread `CADisplayLink` calls `updateKeyboardFrame` 60 times
per second. Calling `updateKeyboardFrame` calls listener on the JS side.
When reloading the runtime gets destroyed on the JavaScript thread. So
when those two things happen at the same time (which in this case
happens often) we get the crash that we're trying to call a js function
on destroyed js runtime.

`NativeReanimatedModule::~NativeReanimatedModule()`, we're cleaning up
everything here:
I've tried and it appeared to work at first but I still got crashes when
running [the 1000 listeners
example](software-mansion#3911)
and probably that's why:

![Screenshot 2023-01-11 at 23 02
18](https://user-images.githubusercontent.com/6280457/211935579-16ff642d-fb15-469b-909e-e36ba9d72781.png)
![Screenshot 2023-01-11 at 23 02
35](https://user-images.githubusercontent.com/6280457/211935599-88cb9e81-20d7-4f96-bfd0-9c3b5da13b24.png)

So when `~NativeReanimatedModule` is being called the runtime related
stuff is already deleted and there is a short window of time that the
runtime is being deleted and we still call js stuff, or at least that's
my theory 🤷‍♀️

So my proposition for now is to listen for
`RCTBridgeDidInvalidateModulesNotification` notification. It's called
just before deleting happens. Also I'm using
`RCTUnsafeExecuteOnMainQueueSync` because I want to wait until the
listeners are completely deleted on the main thread and then delete js
stuff.

If you look a bit above `[[NSNotificationCenter defaultCenter]
postNotificationName:RCTBridgeDidInvalidateModulesNotification` line in
React code you'll see that React calls `invalidate` on all modules' data
before even posting the notification. Maybe we should clean reanimated
stuff here. I haven't explored that though yet and I don't know where is
reanimated's module data and what is module data at all and where to put
that `invalidate` function, I just got this idea while posting this PR.

Just run AnimatedKeyboardExample and reload the app.
Also the [the 1000 listeners
example](software-mansion#3911)
nicely catches other data races.

Tested with changes from
software-mansion#3911
and it works
Also tested on FabricExample and surprisingly it also works.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs review Issue is ready to be reviewed by a maintainer Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snippet of code, snack or repo is provided
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants