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

Duplicate reactTag being pushed to sharedTransitionGroups_[sharedTransitionTag] causing freeze. #7030

Open
aymather opened this issue Feb 15, 2025 · 5 comments · May be fixed by #7040
Open
Labels
Missing info The user didn't precise the problem enough Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snippet of code, snack or repo is provided

Comments

@aymather
Copy link
Contributor

aymather commented Feb 15, 2025

Description

When I have multiple shared transitions on my screens and I engage/remove them a couple of times, my app runs into a freeze. I am unable to reproduce this in a minimal example, but it's happening in my production app. However, I have tracked down the problem...

In REASharedTransitionManager.m (lines 263:269)

do {
        NSLog(@"sharedView.reactTag: %@", sharedView.reactTag);
        NSLog(@"siblingViewTag: %@", siblingViewTag);
        siblingView = [_animationManager viewForTag:siblingViewTag];
        if (siblingView == nil) {
          NSLog(@"siblingView is nil");
          [self clearAllSharedConfigsForViewTag:siblingViewTag];
          siblingViewTag = _findPrecedingViewTagForTransition(sharedView.reactTag);
        } else {
          NSLog(@"siblingView is not nil");
        }
      } while (siblingView == nil && siblingViewTag != nil);
sharedView.reactTag: 7379
siblingViewTag: 2455
siblingView is nil
sharedTransitionRemovingBlock: 2455
clearSharedTransitionConfig: 2455
sharedView.reactTag: 7379
siblingViewTag: 2455
siblingView is nil
sharedTransitionRemovingBlock: 2455
clearSharedTransitionConfig: 2455
sharedView.reactTag: 7379
siblingViewTag: 2455
siblingView is nil

Relevant Crash Logs

Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: FRONTBOARD 2343432205 
<RBSTerminateContext| domain:10 code:0x8BADF00D explanation:[app<io.alysium.dev((null))>:87123] failed to terminate gracefully after 5.0s
ProcessVisibility: Unknown
ProcessState: Running
WatchdogEvent: process-exit
WatchdogVisibility: Background
WatchdogCPUStatistics: (
"Elapsed total CPU time (seconds): 17.880 (user 14.390, system 3.490), 18% CPU",
"Elapsed application CPU time (seconds): 6.185, 6% CPU"
) reportType:CrashLog maxTerminationResistance:Interactive>

Triggered by Thread:  0

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   alysium-dev                   	       0x1009c4f30 std::__1::__compressed_pair<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, void*>*, std::__1::__hash_node_destructor<std::__1::allocator<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, void*>>>>::first[abi:v160006]() const + 0 (compressed_pair.h:136)
1   alysium-dev                   	       0x1009c3fbc std::__1::unique_ptr<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, void*>, std::__1::__hash_node_destructor<std::__1::allocator<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, void*>>>>::operator->[abi:v160006]() const + 24 (unique_ptr.h:274)
2   alysium-dev                   	       0x1009c3ab4 std::__1::pair<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, void*>*>, bool> std::__1::__hash_table<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::__unordered_map_hasher<int, std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::hash<int>, std::__1::equal_to<int>, true>, std::__1::__unordered_map_equal<int, std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::equal_to<int>, std::__1::hash<int>, true>, std::__1::allocator<std::__1::__hash_value_type<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>>::__emplace_unique_key_args<int, std::__1::piecewise_construct_t const&, std::__1::tuple<int const&>, std::__1::tuple<>>(int const&, std::__1::piecewise_construct_t const&, std::__1::tuple<int const&>&&, std::__1::tuple<>&&) + 908 (__hash_table:2035)
3   alysium-dev                   	       0x1009b9a0c std::__1::unordered_map<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>>::operator[](int const&) + 88 (unordered_map:1804)
4   alysium-dev                   	       0x1009b9378 reanimated::LayoutAnimationsManager::clearSharedTransitionConfig(int) + 64 (LayoutAnimationsManager.cpp:79)
5   alysium-dev                   	       0x1009cf318 invocation function for block in reanimated::setupLayoutAnimationCallbacks(std::__1::shared_ptr<reanimated::NativeReanimatedModule>, REAAnimationsManager*) + 124 (NativeProxy.mm:218)
6   alysium-dev                   	       0x100a1e30c -[REAAnimationsManager clearSharedTransitionConfigForTag:] + 72 (REAAnimationsManager.m:630)
7   alysium-dev                   	       0x100a6ade4 -[REASharedTransitionManager clearAllSharedConfigsForViewTag:] + 100 (REASharedTransitionManager.m:970)
8   alysium-dev                   	       0x100a65e84 -[REASharedTransitionManager getSharedElementForCurrentTransition:withNewElements:withOffsetX:withOffsetY:] + 908 (REASharedTransitionManager.m:260)
9   alysium-dev                   	       0x100a64cc4 -[REASharedTransitionManager configureAsyncSharedTransitionForViews:] + 136 (REASharedTransitionManager.m:150)
10  alysium-dev                   	       0x100a64bf0 -[REASharedTransitionManager viewsDidLayout] + 60 (REASharedTransitionManager.m:139)
11  alysium-dev                   	       0x100a1e02c -[REAAnimationsManager viewsDidLayout] + 36 (REAAnimationsManager.m:594)
12  alysium-dev                   	       0x100a70db0 __70-[REASwizzledUIManager reanimated_uiBlockWithLayoutUpdateForRootView:]_block_invoke.59 + 4388 (REASwizzledUIManager.mm:349)
13  alysium-dev                   	       0x100c7d7dc __44-[RCTUIManager flushUIBlocksWithCompletion:]_block_invoke + 344 (RCTUIManager.m:1162)
14  alysium-dev                   	       0x100c7daac __44-[RCTUIManager flushUIBlocksWithCompletion:]_block_invoke.194 + 376 (RCTUIManager.m:1182)
15  alysium-dev                   	       0x100c8549c __RCTExecuteOnMainQueue_block_invoke + 40 (RCTUtils.m:279)
16  libdispatch.dylib             	       0x18016b4f4 _dispatch_call_block_and_release + 24
17  libdispatch.dylib             	       0x18016cd3c _dispatch_client_callout + 16
18  libdispatch.dylib             	       0x18017bb24 _dispatch_main_queue_drain + 1272
19  libdispatch.dylib             	       0x18017b61c _dispatch_main_queue_callback_4CF + 40
20  CoreFoundation                	       0x1803f1a30 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
21  CoreFoundation                	       0x1803ec148 __CFRunLoopRun + 1936
22  CoreFoundation                	       0x1803eb5a4 CFRunLoopRunSpecific + 572
23  GraphicsServices              	       0x18e9fbae4 GSEventRunModal + 160
24  UIKitCore                     	       0x1852f02e4 -[UIApplication _run] + 868
25  UIKitCore                     	       0x1852f3f5c UIApplicationMain + 124
26  alysium-dev                   	       0x1008d08ac main + 100 (main.m:8)
27  dyld_sim                      	       0x104179544 start_sim + 20
28  dyld                          	       0x1043620e0 start + 2360

Environment

  • react-native-reanimated: 3.16.6
  • React Native: 0.74.3
  • iOS/Android: iOS 17.2

Snack or a link to a repository

https://github.com/aymather/shared-element-transitions-hanging.git

Reanimated version

3.16.6

React Native version

0.74.3

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

iOS simulator

Device model

iPhone 15 Plus

Acknowledgements

Yes

@github-actions github-actions bot added Platform: iOS This issue is specific to iOS Missing info The user didn't precise the problem enough Repro provided A reproduction with a snippet of code, snack or repo is provided and removed Missing info The user didn't precise the problem enough labels Feb 15, 2025
Copy link

github-actions bot commented Feb 17, 2025

Hey! 👋

It looks like you've omitted a few important sections from the issue template.

Please complete Steps to reproduce section.

@aymather aymather changed the title Shared Element Transition Memory Management & View Lifecycle Issues Shared Element Transition Freezes App in Infinite Loop while clearing shared configs for view tag Feb 17, 2025
@aymather
Copy link
Contributor Author

aymather commented Feb 17, 2025

Update

It looks like shared transition groups are pushing duplicate reactTags to sharedTransitionGroups_[groupName]. Inside of this function i added some debugging logs:

/*
  The top screen on the stack triggers the animation, so we need to find
  the sibling view registered in the past. This method finds view
  registered in the same transition group (with the same transition tag)
  which has been added to that group directly before the one that we
  provide as an argument.
*/
int LayoutAnimationsManager::findPrecedingViewTagForTransition(const int tag) {
  std::cout << "findPrecedingViewTagForTransition: " << tag << std::endl;
  auto const &groupName = viewTagToSharedTag_[tag];
  std::cout << "groupName: " << groupName << std::endl;
  auto const &group = sharedTransitionGroups_[groupName];
  std::cout << "group: " << group.size() << std::endl;

  // Print all the tags in the group
  std::cout << "Group contents: ";
  for (const auto &viewTag : group) {
    std::cout << viewTag << " ";
  }
  std::cout << std::endl;

  auto position = std::find(group.begin(), group.end(), tag);
  if (position != group.end() && position != group.begin()) {
    return *std::prev(position);
  }
  return -1;
}

Which produced this output.

viewsDidLayout 19
configureAsyncSharedTransitionForViews 19
getSharedElementForCurrentTransition
sharedViews count: 1
findPrecedingViewTagForTransition: 3833
groupName: qr-code-ulECGhnSEEKkdNODirxKaHEF
group: 3
Group contents: 2455 2455 3833 
sharedView.reactTag: 3833
siblingViewTag: 2455
siblingView is not nil
viewsDidLayout 20
configureAsyncSharedTransitionForViews 20

The Group contents: 2455 2455 3833 line is specifically what is relevant here. This lead me to LayoutAnimationsManager::configureAnimationBatch which adds tags to the group contents without any sort of check to see if the group already contains that tag here LayoutAnimationsManager.cpp(line 40)

sharedTransitionGroups_[sharedTransitionTag].push_back(tag);

If you update that to the following, the freeze no longer happens.

// Check if tag already exists in group
auto &group = sharedTransitionGroups_[sharedTransitionTag];
if (std::find(group.begin(), group.end(), tag) == group.end()) {
  // Only add if tag isn't already in group
  group.push_back(tag);
}

Now this is only treating the symptom of the freeze, the question now is... why are duplicate tag ids being pushed to this group at all? Does that make sense to anyone?

@aymather aymather changed the title Shared Element Transition Freezes App in Infinite Loop while clearing shared configs for view tag Duplicate reactTag being pushed to sharedTransitionGroups_[sharedTransitionTag] Feb 17, 2025
@aymather aymather changed the title Duplicate reactTag being pushed to sharedTransitionGroups_[sharedTransitionTag] Duplicate reactTag being pushed to sharedTransitionGroups_[sharedTransitionTag] causing freeze. Feb 17, 2025
@aymather
Copy link
Contributor Author

Fix proposed #7040

@VladYuskiv
Copy link

@aymather I have the same issue.

@aymather
Copy link
Contributor Author

@VladYuskiv I have a pull request open and should be approved. There are still some buggy behaviors with this though, I'll try to sort them out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Missing info The user didn't precise the problem enough 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
2 participants