Skip to content

Commit

Permalink
Fix edge case when layout animation caused delete and create mutation…
Browse files Browse the repository at this point in the history
…s in the same batch

Summary:
This is a two step (2/2) fix to a race that could caused a DELETE...CREATE mutations being sent over to the fabric mounting layer. Such combination was assumed not possible from the differ (https://fburl.com/code/kg8z9t4w), yet it happened at least in the existence of layout animation and when certain commits happen while animation is active.

This diff fixes all potential races in the Fabric mounting layer directly. It captures all the `DELETE...CREATE` combinations and stop those from passing down to the native platforms. This should fix all such races should them not captured by the fix in the layout animation.

To help understand other races better, I also logged here to indicate such race so that future crashes will have more context.

Changelog:
[General][Fixed] - Fix edge case when layout animation caused delete and create mutations in the same batch

Reviewed By: javache

Differential Revision: D41900201

fbshipit-source-id: 280502ca32ce87a9e483cd859b11bcd3e5c4a435
  • Loading branch information
Xin Chen authored and facebook-github-bot committed Feb 7, 2023
1 parent 5b8faae commit d9f2491
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,10 @@ public class ReactFeatureFlags {
* state in Fabric SurfaceMountingManager.
*/
public static boolean reduceDeleteCreateMutationLayoutAnimation = true;

/**
* Allow fix to drop delete...create mutations which could cause missing view state in Fabric
* SurfaceMountingManager.
*/
public static boolean reduceDeleteCreateMutation = false;
}
32 changes: 29 additions & 3 deletions ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ FabricMountingManager::FabricMountingManager(
std::shared_ptr<const ReactNativeConfig> &config,
global_ref<jobject> &javaUIManager)
: javaUIManager_(javaUIManager),
useOverflowInset_(getFeatureFlagValue("useOverflowInset")) {
useOverflowInset_(getFeatureFlagValue("useOverflowInset")),
reduceDeleteCreateMutation_(
getFeatureFlagValue("reduceDeleteCreateMutation")) {
CoreFeatures::enableMapBuffer = getFeatureFlagValue("useMapBufferProps");
}

Expand Down Expand Up @@ -314,9 +316,33 @@ void FabricMountingManager::executeMount(
bool isVirtual = mutation.mutatedViewIsVirtual();
switch (mutationType) {
case ShadowViewMutation::Create: {
bool allocationCheck =
bool shouldCreateView =
!allocatedViewTags.contains(newChildShadowView.tag);
bool shouldCreateView = allocationCheck;
if (reduceDeleteCreateMutation_) {
// Detect DELETE...CREATE situation on the same node and do NOT push
// back to the mount items. This is an edge case that may happen
// when for example animation runs while commit happened, and we
// want to filter them out here to capture all possible sources of
// such mutations. The re-ordering logic here assumes no
// DELETE...CREATE in the mutations, as we will re-order mutations
// and batch all DELETE instructions in the end.
auto it = std::remove_if(
cppDeleteMountItems.begin(),
cppDeleteMountItems.end(),
[&](auto &deletedMountItem) -> bool {
return deletedMountItem.oldChildShadowView.tag ==
newChildShadowView.tag;
});
bool hasDeletedViewsWithSameTag = it != cppDeleteMountItems.end();
cppDeleteMountItems.erase(it, cppDeleteMountItems.end());

if (hasDeletedViewsWithSameTag) {
shouldCreateView = false;
LOG(ERROR)
<< "XIN: Detect DELETE...CREATE on the same tag from mutations in the same batch. The DELETE and CREATE mutations are removed before sending to the native platforms";
}
}

if (shouldCreateView) {
cppCommonMountItems.push_back(
CppMountItem::CreateMountItem(newChildShadowView));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class FabricMountingManager final {
std::recursive_mutex allocatedViewsMutex_;

bool const useOverflowInset_{false};
bool const reduceDeleteCreateMutation_{false};

jni::local_ref<jobject> getProps(
ShadowView const &oldShadowView,
Expand Down

0 comments on commit d9f2491

Please sign in to comment.