Fix race condition on startSurface

Summary:
The root cause of this bug is a race condition between the onMeasure method and setupReactContext.

ReactInstanceManager.attachRootView() method is responsible of the initialization of ReactRootViews and it is invoked by ReactRootView.onMeasure() method in the UIThread

Important initialization steps:
1. Clear the Id of the ReactRootView
2. Add the ReactRootView to the mAttachedReactRoots
3. Call StartSurface (if the bridge has been initialized)

Sometimes, when this method is invoked for the first time, the bridge is not initialized, in those cases we delay the start of the surface.

Once the bridge is initialized, StartSurface is called by the setupReactContext() running in the NativeModuleThread.

Since onMeasure can be called multiple times, it is possible that we call "StartSurface" twice for the same ReactRootView, causing the bug reported on T78832286.

This diff adds an extra check to prevent calling "StartSurface" twice. The fix is done using an AtomicInteger comparison and it is gated by the flag "enableStartSurfaceRaceConditionFix". Once we verify this works fine in production we will clean up the code, remove the flags and maybe revisit the API of ReactRoot.

changelog: [Android] Fix race-condition on the initialization of ReactRootViews

Reviewed By: JoshuaGross

Differential Revision: D25255877

fbshipit-source-id: ca8fb00f50e86891fb4c5a06240177cc1a0186d9
This commit is contained in:
David Vacca
2020-12-04 19:55:54 -08:00
committed by Facebook GitHub Bot
parent dee937c7c1
commit 74a756846f
4 changed files with 50 additions and 5 deletions
@@ -56,6 +56,7 @@ import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.systrace.Systrace;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Default root view for catalyst apps. Provides the ability to listen for size changes so that a UI
@@ -98,6 +99,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
private int mLastOffsetX = Integer.MIN_VALUE;
private int mLastOffsetY = Integer.MIN_VALUE;
private @UIManagerType int mUIManagerType = DEFAULT;
private final AtomicInteger mState = new AtomicInteger(STATE_STOPPED);
public ReactRootView(Context context) {
super(context);
@@ -413,6 +415,10 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
return appProperties != null ? appProperties.getString("surfaceID") : null;
}
public AtomicInteger getState() {
return mState;
}
public static Point getViewportOffset(View v) {
int[] locationInWindow = new int[2];
v.getLocationInWindow(locationInWindow);