-
Notifications
You must be signed in to change notification settings - Fork 25k
Description
Description
Greetings dear RN team.
We're experiencing production ANRs in our React Native application with Fabric (New Architecture) enabled. Based on the stack traces and our analysis, this appears to be related to lock contention in ShadowTreeRegistry. The main UI thread seems to become blocked while waiting on a shared lock, potentially while background threads hold exclusive locks, which may cause the Android system to trigger an ANR.
We're sharing this issue in case others have experienced similar problems or if the React Native team has any insights into this behaviour.
Environment
- React Native: 0.81.1 (Fabric/New Architecture enabled)
- Reanimated: 3.19.3
- Platform: Android (arm64-v8a)
- Occurrence: Intermittent in production environments
Observed Behavior
Based on Crashlytics reports, we're seeing what appears to be a deadlock scenario involving three threads competing for ShadowTreeRegistry locks:
- Main UI Thread: Appears to call
FabricUIManager.reportMount()which seems to needlock_shared()to iterate shadow trees - Background Thread: Seems to call
stopSurfaceWithSurfaceHandler()which may hold an exclusive lock duringShadowTreeRegistry::remove() - JS Thread: Reanimated commit appears to need
lock_shared()for layout animations
Potential lock contention pattern:
T1: Background thread may acquire exclusive lock (stopSurface)
T2: Main thread appears to wait for shared lock (reportMount)
T3: JS thread seems to wait for shared lock (reanimated commit)
T4: After 5 seconds → Android ANR dialog appears
Stack Traces from Production
Main Thread (Appears Blocked):
#00 pc 0x57840 libc.so (syscall + 32)
#04 pc 0xe0c48 libreactnative.so (std::__ndk1::__shared_mutex_base::lock_shared + 64)
#05 pc 0x405ab8 libreactnative.so (facebook::react::ShadowTreeRegistry::visit const + 56)
#06 pc 0x436c8c libreactnative.so (facebook::react::UIManager::reportMount const + 92)
#07 pc 0x2a5564 libreactnative.so (facebook::react::FabricUIManagerBinding::reportMount + 488)
Background Thread (Possibly Holding Exclusive Lock):
#04 pc 0xe0b50 libreactnative.so (std::__ndk1::__shared_mutex_base::lock + 88)
#05 pc 0x405940 libreactnative.so (facebook::react::ShadowTreeRegistry::remove const + 44)
#06 pc 0x435740 libreactnative.so (facebook::react::UIManager::stopSurface const + 84)
#07 pc 0x42028c libreactnative.so (facebook::react::SurfaceHandler::stop const + 68)
#08 pc 0x2a8234 libreactnative.so (facebook::react::FabricUIManagerBinding::stopSurfaceWithSurfaceHandler + 612)
JS Thread (Appears to be Waiting):
#04 pc 0xe0c48 libreactnative.so (std::__ndk1::__shared_mutex_base::lock_shared + 64)
#05 pc 0x405c2c libreactnative.so (facebook::react::ShadowTreeRegistry::enumerate const + 52)
#06 pc 0x67910 libreanimated.so (reanimated::ReanimatedCommitHook::maybeInitializeLayoutAnimations + 132)
#07 pc 0x67ac8 libreanimated.so (reanimated::ReanimatedCommitHook::shadowTreeWillCommit + 72)
Attempted Reproduction
I've created a minimal repro case app, that attempts to reproduce this behaviour by creating simultaneous lock contention. While I do understand this may (very most likely) not perfectly replicate the production scenario, it demonstrates similar symptoms:
Test approach:
- A native module spawns multiple background threads that simultaneously call FabricUIManager methods (accessed via reflection)
- The main thread is then forced to call the same FabricUIManager method
- This creates lock contention that may simulate the production scenario
Test repository is available in the link attached
Questions for the Team
We'd greatly appreciate any guidance on the following:
- Is this a known limitation of how Fabric manages concurrent access to shadow trees?
- Are there recommended patterns to avoid such lock contention scenarios?
- Has this been addressed in newer React Native versions?
- Would it be helpful if we shared our reproduction repository?
Context About Our Usage
This issue appears more frequently in scenarios involving:
- Applications using Reanimated with layout animations
- Navigation patterns that may cause rapid mount/unmount cycles
- Simultaneous UI updates across multiple components/screens
- Background operations occurring during render cycles
The issue appears to be timing-dependent and impacts user experience in our production application, particularly during complex navigation flows or when using animations. Although it seems that the amount of occurrences is fading out, but still persists.
I wonder if it is connected to the react-native update, which we hade performed (to v0.82). If it's the case would highly appreciate confirmation.
We understand this may be a complex architectural consideration, and we'd be happy to provide additional information or testing if it would be helpful to the team, if it's possible.
Thank you for your time and for all the work on React Native and the New Architecture. We're excited about Fabric's benefits and want to help address any edge cases that arise.
Steps to reproduce
- Go to the repro package
- Download it, follow instllation guide to install
- Tap on any of two available buttons.
- Wait about 30 seconds
- It should crash
React Native Version
0.81.1
Affected Platforms
Runtime - Android
Output of npx @react-native-community/cli info
info Fetching system and libraries information...
System:
OS: macOS 15.7.1
CPU: (12) arm64 Apple M3 Pro
Memory: 585.41 MB / 36.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 20.18.0
path: ~/.nvm/versions/node/v20.18.0/bin/node
Yarn:
version: 1.22.22
path: ~/.nvm/versions/node/v20.18.0/bin/yarn
npm:
version: 11.0.0
path: ~/.nvm/versions/node/v20.18.0/bin/npm
Watchman:
version: 2025.04.14.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.16.2
SDKs:
iOS SDK:
Platforms:
- DriverKit 24.1
- iOS 18.1
- macOS 15.1
- tvOS 18.1
- visionOS 2.1
- watchOS 11.1
Android SDK:
Android NDK: 26.1.10909125
IDEs:
Android Studio: 2025.1 AI-251.26094.121.2513.14007798
Xcode:
version: 16.1/16B40
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.13
path: /usr/bin/javac
Ruby:
version: 3.3.6
npmPackages:
"@react-native-community/cli":
installed: 20.0.0
wanted: "catalog:"
react:
installed: 19.1.1
wanted: "catalog:"
react-native:
installed: 0.81.1
wanted: "catalog:"
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: false
Stacktrace or Logs
main (native):tid=1 systid=31815
#00 pc 0x57840 libc.so (syscall + 32) (BuildId: edc1b601d9f8344c1eccd3d77a458f65)
#01 pc 0x5c5a0 libc.so (__futex_wait_ex + 148) (BuildId: edc1b601d9f8344c1eccd3d77a458f65)
#02 pc 0xc2b94 libc.so (pthread_cond_wait + 80) (BuildId: edc1b601d9f8344c1eccd3d77a458f65)
#03 pc 0xdf3b8 split_config.arm64_v8a.apk (std::__ndk1::condition_variable::wait + 24) (BuildId: 6783a7f3a0d9c67c55a74cadc441ed55aaa493da)
#04 pc 0xe0c48 split_config.arm64_v8a.apk (std::__ndk1::__shared_mutex_base::lock_shared + 64) (BuildId: 6783a7f3a0d9c67c55a74cadc441ed55aaa493da)
#05 pc 0x405ab8 split_config.arm64_v8a.apk (facebook::react::ShadowTreeRegistry::visit const + 56) (BuildId: a1b4dd2f3237aa45)
#06 pc 0x436c8c split_config.arm64_v8a.apk (facebook::react::UIManager::reportMount const + 92) (BuildId: a1b4dd2f3237aa45)
#07 pc 0x2a5564 split_config.arm64_v8a.apk (facebook::react::FabricUIManagerBinding::reportMount + 488) (BuildId: a1b4dd2f3237aa45)
#08 pc 0x2af714 split_config.arm64_v8a.apk (facebook::jni::detail::MethodWrapper<void , &facebook::react::FabricUIManagerBinding::reportMount, facebook::react::FabricUIManagerBinding, void, int>::dispatch + 64) (BuildId: a1b4dd2f3237aa45)
#09 pc 0x2aefa8 split_config.arm64_v8a.apk + 8650752 (BuildId: a1b4dd2f3237aa45)
at com.facebook.react.fabric.FabricUIManagerBinding.reportMount(FabricUIManagerBinding.kt)
at com.facebook.react.fabric.FabricUIManager$MountItemDispatchListener$1.run(FabricUIManager.java:1374)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at android.app.ActivityThread.main(ActivityThread.java:9063)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
MANDATORY Reproducer
https://github.com/LoserAntbear/anrrepro
Screenshots and Videos
No response