Avoid errors when dispatching mount operations within mount hooks (#50091)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50091

Changelog: [internal]

If a library uses mount hooks to perform mount operations, it's possible to get concurrent modifications of the list of pending surface IDs to report.

This fixes that potential error by making a copy of the list before dispatching the mount notifications.

Fixes https://github.com/facebook/react-native/issues/49783.

Reviewed By: javache

Differential Revision: D71387739

fbshipit-source-id: 96c723ef2d6bcc659c4452434b7a4d5af26117ef
This commit is contained in:
Rubén Norte
2025-03-18 05:59:50 -07:00
committed by Facebook GitHub Bot
parent e4d51cd866
commit 17da3cbbf4
@@ -173,7 +173,7 @@ public class FabricUIManager
private final CopyOnWriteArrayList<UIManagerListener> mListeners = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<UIManagerListener> mListeners = new CopyOnWriteArrayList<>();
private boolean mMountNotificationScheduled = false; private boolean mMountNotificationScheduled = false;
private final List<Integer> mMountedSurfaceIds = new ArrayList<>(); private List<Integer> mSurfaceIdsWithPendingMountNotification = new ArrayList<>();
@ThreadConfined(UI) @ThreadConfined(UI)
@NonNull @NonNull
@@ -1254,12 +1254,13 @@ public class FabricUIManager
// Collect surface IDs for all the mount items // Collect surface IDs for all the mount items
for (MountItem mountItem : mountItems) { for (MountItem mountItem : mountItems) {
if (mountItem != null && !mMountedSurfaceIds.contains(mountItem.getSurfaceId())) { if (mountItem != null
mMountedSurfaceIds.add(mountItem.getSurfaceId()); && !mSurfaceIdsWithPendingMountNotification.contains(mountItem.getSurfaceId())) {
mSurfaceIdsWithPendingMountNotification.add(mountItem.getSurfaceId());
} }
} }
if (!mMountNotificationScheduled && !mMountedSurfaceIds.isEmpty()) { if (!mMountNotificationScheduled && !mSurfaceIdsWithPendingMountNotification.isEmpty()) {
mMountNotificationScheduled = true; mMountNotificationScheduled = true;
// Notify mount when the effects are visible and prevent mount hooks to // Notify mount when the effects are visible and prevent mount hooks to
@@ -1271,17 +1272,19 @@ public class FabricUIManager
public void run() { public void run() {
mMountNotificationScheduled = false; mMountNotificationScheduled = false;
// Create a copy in case mount hooks trigger more mutations
final List<Integer> surfaceIdsToReportMount =
mSurfaceIdsWithPendingMountNotification;
mSurfaceIdsWithPendingMountNotification = new ArrayList<>();
final @Nullable FabricUIManagerBinding binding = mBinding; final @Nullable FabricUIManagerBinding binding = mBinding;
if (binding == null || mDestroyed) { if (binding == null || mDestroyed) {
mMountedSurfaceIds.clear();
return; return;
} }
for (int surfaceId : mMountedSurfaceIds) { for (int surfaceId : surfaceIdsToReportMount) {
binding.reportMount(surfaceId); binding.reportMount(surfaceId);
} }
mMountedSurfaceIds.clear();
} }
}); });
} }