react-native-reanimated: Deadlock in [REANodesManager performOperations]
Description
performOperations blocks the main queue while waiting for the UIManager queue to do work. It’s possible that the UIManager queue is already blocked waiting to perform work on the main queue (See somewhat common usage of RCTUnsafeExecuteOnMainQueueSync
). This can result in deadlock like the following:
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001b82e7540 semaphore_wait_trap + 8
1 libdispatch.dylib 0x00000001809ffc00 _dispatch_sema4_wait + 28 (lock.c:139)
2 libdispatch.dylib 0x0000000180a002b8 _dispatch_semaphore_wait_slow + 132 (semaphore.c:132)
3 MyApp 0x0000000104795c64 -[REANodesManager performOperations] + 284 (REANodesManager.m:261)
4 MyApp 0x0000000104795aa4 -[REANodesManager onAnimationFrame:] + 452 (REANodesManager.m:209)
5 QuartzCore 0x0000000184a5e378 CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 756 (CADisplay.mm:4177)
6 QuartzCore 0x0000000184a64fac display_timer_callback(__CFMachPort*, void*, long, void*) + 372 (CADisplayTimer.cpp:219)
7 CoreFoundation 0x0000000180cff440 __CFMachPortPerform + 176 (CFMachPort.c:549)
8 CoreFoundation 0x0000000180d427d4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 60 (CFRunLoop.c:1996)
9 CoreFoundation 0x0000000180d45fe0 __CFRunLoopDoSource1 + 596 (CFRunLoop.c:2136)
10 CoreFoundation 0x0000000180cffebc __CFRunLoopRun + 2380 (CFRunLoop.c:3172)
11 CoreFoundation 0x0000000180d13468 CFRunLoopRunSpecific + 600 (CFRunLoop.c:3268)
12 GraphicsServices 0x000000019c8b738c GSEventRunModal + 164 (GSEvent.c:2200)
13 UIKitCore 0x00000001836b65d0 -[UIApplication _run] + 1100 (UIApplication.m:3493)
14 UIKitCore 0x0000000183434f74 UIApplicationMain + 364 (UIApplication.m:5047)
15 MyApp 0x000000010421bf2c main + 68 (AnalyticsKey.swift:29)
16 dyld 0x0000000105e9daa4 start + 520 (dyldMain.cpp:879)
Thread 12:
0 libsystem_kernel.dylib 0x00000001b82e81a4 __ulock_wait + 8
1 libdispatch.dylib 0x0000000180a00064 _dlock_wait + 56 (lock.c:326)
2 libdispatch.dylib 0x00000001809ffe08 _dispatch_thread_event_wait_slow + 56 (lock.c:366)
3 libdispatch.dylib 0x0000000180a0ebac __DISPATCH_WAIT_FOR_QUEUE__ + 364 (lock.h:330)
4 libdispatch.dylib 0x0000000180a0e754 _dispatch_sync_f_slow + 144 (queue.c:1762)
5 MyApp 0x0000000104849324 RCTUnsafeExecuteOnMainQueueSync + 120 (RCTUtils.m:277)
6 libdispatch.dylib 0x00000001809ff670 _dispatch_client_callout + 20 (object.m:560)
7 libdispatch.dylib 0x0000000180a00f18 _dispatch_once_callout + 32 (once.c:52)
8 MyApp 0x00000001048494e4 dispatch_once + 20 (once.h:84)
9 MyApp 0x00000001048494e4 RCTScreenSize + 68 (RCTUtils.m:343)
10 MyApp 0x0000000104815898 -[RCTModalHostShadowView insertReactSubview:atIndex:] + 124 (RCTModalHostViewManager.m:42)
11 MyApp 0x0000000104842ad0 RCTSetChildren + 280 (RCTUIManager.m:884)
12 MyApp 0x00000001048428e8 -[RCTUIManager setChildren:reactTags:] + 68 (RCTUIManager.m:865)
13 CoreFoundation 0x0000000180d163a4 __invoking___ + 148
14 CoreFoundation 0x0000000180d33b74 -[NSInvocation invoke] + 468 (NSForwarding.m:3378)
15 CoreFoundation 0x0000000180d6a9d4 -[NSInvocation invokeWithTarget:] + 80 (NSForwarding.m:3475)
16 MyApp 0x000000010481b928 -[RCTModuleMethod invokeWithBridge:module:arguments:] + 460 (RCTModuleMethod.mm:584)
17 MyApp 0x000000010481df2c facebook::react::invokeInner(RCTBridge*, RCTModuleData*, unsigned int, folly::dynamic const&, int, (anonymous namespace)::SchedulingContext) + 540 (RCTNativeModule.mm:183)
18 MyApp 0x000000010481db5c operator() + 68 (RCTNativeModule.mm:104)
19 MyApp 0x000000010481db5c invocation function for block in facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int) + 112 (RCTNativeModule.mm:95)
20 libdispatch.dylib 0x00000001809fd924 _dispatch_call_block_and_release + 32 (init.c:1517)
21 libdispatch.dylib 0x00000001809ff670 _dispatch_client_callout + 20 (object.m:560)
22 libdispatch.dylib 0x0000000180a06df4 _dispatch_lane_serial_drain + 672 (inline_internal.h:2601)
23 libdispatch.dylib 0x0000000180a07968 _dispatch_lane_invoke + 392 (queue.c:3937)
24 libdispatch.dylib 0x0000000180a121b8 _dispatch_workloop_worker_thread + 656 (queue.c:6727)
25 libsystem_pthread.dylib 0x00000001f181b0f4 _pthread_wqthread + 288 (pthread.c:2599)
26 libsystem_pthread.dylib 0x00000001f181ae94 start_wqthread + 8
There is some documentation in RCTUIManagerUtils.h
on the threading model of this system. There is a noteworthy piece here:
This solution assumes that the code running on UIManager queue will never explicitly block the Main queue via calling
RCTUnsafeExecuteOnMainQueueSync
. Otherwise, it can cause a deadlock.
What is happening in performOperations
is effectively a custom implementation of RCTUnsafeExecuteOnMainQueueSync
and so we get deadlock.
This logic was introduced with https://github.com/software-mansion/react-native-reanimated/pull/1215, I don’t fully understand the issue this was fixing or why this needed to be synchronously waiting, but there atleast is a connected issue with some context.
This pull request https://github.com/software-mansion/react-native-reanimated/pull/3082 could “fix” the issue by breaking the deadlock, but I’m thinking more could be improved here instead of just timing out the deadlock. It’s also not clear to me what happens when this runs asynchronously instead of synchronously.
Potentially related: https://github.com/software-mansion/react-native-reanimated/issues/2938, https://github.com/software-mansion/react-native-reanimated/issues/2327 This same logic is also on Android, which is likely the root cause of https://github.com/software-mansion/react-native-reanimated/issues/3062 and https://github.com/software-mansion/react-native-reanimated/issues/2251
Expected behavior
Never deadlock. Generally best practice is to never block the main thread.
Actual behavior & steps to reproduce
performOperations blocks the main queue waiting on the UIManager queue. It’s possible that at this same time the UIManager queue is already blocked trying to perform work on the main queue.
Snack or minimal code example
I don’t have anything other than stacktraces showing the deadlock.
Package versions
name | version |
---|---|
react-native | 0.66.4 |
react-native-reanimated | 2.4.1 (with https://github.com/software-mansion/react-native-reanimated/pull/2681 applied) |
NodeJS | 16.14.2 |
Xcode | 13.3.1 |
Affected platforms
- Android (maybe)
- iOS
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 29
- Comments: 34 (2 by maintainers)
Is anyone reviewing cltnschlosser’s PR? Or fixing the problem in some other way?
I can observe this issue using in the latest version (2.8.0) as well.
I have the same issue.
After merging in this PR locally, we saw a noticeable drop in iOS native app crashes over the last 90 days:
same here
same error w/ pretty similar stack trace, can only see this behaviour on iOS though.
"react-native-reanimated": "^2.5.0"
“react-native-reanimated”: “^3.1.0”,
same here: 2023-05-10 02:11:32.575475+0200 baby_music[39315:2687260] *** Assertion failure in -REANodesManager uiManager:performMountingWithBlock:, /Users/simo97/Desktop/baby_music/node_modules/react-native-reanimated/ios/REANodesManager.mm:246 2023-05-10 02:11:32.576152+0200 baby_music[39315:2687260] *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Mouting block is expected to not be set’ *** First throw call stack: (0x1a6776d94 0x19f82c3d0 0x1a0f21808 0x102807534 0x1029bcd94 0x1029b7bcc 0x1029b7928 0x1029b76a4 0x102806a6c 0x102807c18 0x1029bd5c4 0x105f50520 0x105f52038 0x105f5a0b0 0x105f5adf4 0x105f67c74 0x206103ddc 0x206103b7c) (lldb)
Also seeing this. iPhone7 and iPad mini. I’m still on react-native-reanimated 2.3.3 though.
Facing same issue “react-native-reanimated”: “2.17.0”
anybody here some thoughts on a workaround? I am still stuck and don’t know how to resolve the matter as it is the navigation library that causes it. 😕