Experiment to replace Fabric MountItem lists with concurrent queues (re-land)
Summary:
Currently, queuing any sort of MountItem or getting the list of MountItems requires synchronization. Since these can be queued up at any point from the JS thread, there will, in theory, be constant contention between JS and UI thread.
To see if this is true, I'm creating an experiment instead of just switching over to concurrent structures. After seeing results, we can hopefully make a decision and delete the non-concurrent stuff or improve on it somehow.
The original was unlanded in D24973616 (26787e2260
) due to a typo, which has been fixed now. The typo was that in Blocking Mode, we queued up all PreMountItems to the concurrent PreMountItems queue.
Changelog: [Internal]
Reviewed By: ShikaSD
Differential Revision: D24974851
fbshipit-source-id: d081c081bbd0de445bb92408f0ec822056b905a5
This commit is contained in:
Родитель
26787e2260
Коммит
752e7ab1f9
|
@ -88,4 +88,7 @@ public class ReactFeatureFlags {
|
|||
|
||||
/** Potential bugfix for crashes caused by mutating the view hierarchy during onDraw. */
|
||||
public static boolean enableDrawMutationFix = true;
|
||||
|
||||
/** Use lock-free data structures for Fabric MountItems. */
|
||||
public static boolean enableLockFreeMountInstructions = false;
|
||||
}
|
||||
|
|
|
@ -89,10 +89,12 @@ import com.facebook.react.views.text.TextLayoutManager;
|
|||
import com.facebook.systrace.Systrace;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@SuppressLint("MissingNativeLoadLibrary")
|
||||
|
@ -124,20 +126,35 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
new ConcurrentHashMap<>();
|
||||
|
||||
@NonNull private final EventBeatManager mEventBeatManager;
|
||||
@NonNull private final Object mViewCommandMountItemsLock = new Object();
|
||||
@NonNull private final Object mMountItemsLock = new Object();
|
||||
@NonNull private final Object mPreMountItemsLock = new Object();
|
||||
|
||||
private boolean mInDispatch = false;
|
||||
private int mReDispatchCounter = 0;
|
||||
|
||||
@NonNull
|
||||
private final CopyOnWriteArrayList<UIManagerListener> mListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
// Concurrent MountItem data-structures, experimental. TODO: T79662803
|
||||
@NonNull
|
||||
private final ConcurrentLinkedQueue<DispatchCommandMountItem> mViewCommandMountItemsConcurrent =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
@NonNull
|
||||
private final ConcurrentLinkedQueue<MountItem> mMountItemsConcurrent =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
@NonNull
|
||||
private final ConcurrentLinkedQueue<PreAllocateViewMountItem> mPreMountItemsConcurrent =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
// Non-concurrent MountItem data-structures
|
||||
@NonNull private final Object mViewCommandMountItemsLock = new Object();
|
||||
@NonNull private final Object mMountItemsLock = new Object();
|
||||
@NonNull private final Object mPreMountItemsLock = new Object();
|
||||
|
||||
@GuardedBy("mViewCommandMountItemsLock")
|
||||
@NonNull
|
||||
private List<DispatchCommandMountItem> mViewCommandMountItems = new ArrayList<>();
|
||||
|
||||
@NonNull
|
||||
private final CopyOnWriteArrayList<UIManagerListener> mListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
@GuardedBy("mMountItemsLock")
|
||||
@NonNull
|
||||
private List<MountItem> mMountItems = new ArrayList<>();
|
||||
|
@ -339,18 +356,15 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
// possible at teardown, but this race should *never* happen at startup.
|
||||
@Nullable ThemedReactContext context = mReactContextForRootTag.get(rootTag);
|
||||
|
||||
String component = getFabricComponentName(componentName);
|
||||
synchronized (mPreMountItemsLock) {
|
||||
mPreMountItems.add(
|
||||
new PreAllocateViewMountItem(
|
||||
context,
|
||||
rootTag,
|
||||
reactTag,
|
||||
component,
|
||||
props,
|
||||
(StateWrapper) stateWrapper,
|
||||
isLayoutable));
|
||||
}
|
||||
addPreAllocateMountItem(
|
||||
new PreAllocateViewMountItem(
|
||||
context,
|
||||
rootTag,
|
||||
reactTag,
|
||||
getFabricComponentName(componentName),
|
||||
props,
|
||||
(StateWrapper) stateWrapper,
|
||||
isLayoutable));
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
|
@ -616,9 +630,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
// If the reactTag exists, we assume that it might at the end of the next
|
||||
// batch of MountItems. Otherwise, we try to execute immediately.
|
||||
if (!mMountingManager.getViewExists(reactTag)) {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(synchronousMountItem);
|
||||
}
|
||||
addMountItem(synchronousMountItem);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -692,9 +704,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
}
|
||||
|
||||
if (shouldSchedule && mountItem != null) {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(mountItem);
|
||||
}
|
||||
addMountItem(mountItem);
|
||||
if (UiThreadUtil.isOnUiThread()) {
|
||||
// We only read these flags on the UI thread.
|
||||
tryDispatchMountItems();
|
||||
|
@ -777,9 +787,28 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
mLastExecutedMountItemSurfaceId = -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <E extends MountItem> List<E> drainConcurrentItemQueue(ConcurrentLinkedQueue<E> queue) {
|
||||
List<E> result = new ArrayList<>();
|
||||
while (!queue.isEmpty()) {
|
||||
E item = queue.poll();
|
||||
if (item != null) {
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
if (result.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
private List<DispatchCommandMountItem> getAndResetViewCommandMountItems() {
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
return drainConcurrentItemQueue(mViewCommandMountItemsConcurrent);
|
||||
}
|
||||
|
||||
synchronized (mViewCommandMountItemsLock) {
|
||||
List<DispatchCommandMountItem> result = mViewCommandMountItems;
|
||||
if (result.isEmpty()) {
|
||||
|
@ -793,6 +822,10 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
private List<MountItem> getAndResetMountItems() {
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
return drainConcurrentItemQueue(mMountItemsConcurrent);
|
||||
}
|
||||
|
||||
synchronized (mMountItemsLock) {
|
||||
List<MountItem> result = mMountItems;
|
||||
if (result.isEmpty()) {
|
||||
|
@ -803,7 +836,11 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
private ArrayDeque<PreAllocateViewMountItem> getAndResetPreMountItems() {
|
||||
private Collection<PreAllocateViewMountItem> getAndResetPreMountItems() {
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
return drainConcurrentItemQueue(mPreMountItemsConcurrent);
|
||||
}
|
||||
|
||||
synchronized (mPreMountItemsLock) {
|
||||
ArrayDeque<PreAllocateViewMountItem> result = mPreMountItems;
|
||||
if (result.isEmpty()) {
|
||||
|
@ -926,19 +963,18 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
|
||||
// If there are MountItems to dispatch, we make sure all the "pre mount items" are executed
|
||||
// first
|
||||
ArrayDeque<PreAllocateViewMountItem> mPreMountItemsToDispatch = getAndResetPreMountItems();
|
||||
Collection<PreAllocateViewMountItem> preMountItemsToDispatch = getAndResetPreMountItems();
|
||||
|
||||
if (mPreMountItemsToDispatch != null) {
|
||||
if (preMountItemsToDispatch != null) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"FabricUIManager::mountViews preMountItems to execute: "
|
||||
+ mPreMountItemsToDispatch.size());
|
||||
+ preMountItemsToDispatch.size());
|
||||
|
||||
while (!mPreMountItemsToDispatch.isEmpty()) {
|
||||
PreAllocateViewMountItem mountItem = mPreMountItemsToDispatch.pollFirst();
|
||||
for (PreAllocateViewMountItem preMountItem : preMountItemsToDispatch) {
|
||||
if (surfaceActiveForExecution(
|
||||
mountItem.getRootTag(), "dispatchMountItems PreAllocateViewMountItem")) {
|
||||
mountItem.execute(mMountingManager);
|
||||
preMountItem.getRootTag(), "dispatchMountItems PreAllocateViewMountItem")) {
|
||||
preMountItem.execute(mMountingManager);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1016,12 +1052,19 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
break;
|
||||
}
|
||||
|
||||
PreAllocateViewMountItem preMountItemToDispatch;
|
||||
synchronized (mPreMountItemsLock) {
|
||||
if (mPreMountItems.isEmpty()) {
|
||||
break;
|
||||
PreAllocateViewMountItem preMountItemToDispatch = null;
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
preMountItemToDispatch = mPreMountItemsConcurrent.poll();
|
||||
} else {
|
||||
synchronized (mPreMountItemsLock) {
|
||||
if (!mPreMountItems.isEmpty()) {
|
||||
preMountItemToDispatch = mPreMountItems.pollFirst();
|
||||
}
|
||||
}
|
||||
preMountItemToDispatch = mPreMountItems.pollFirst();
|
||||
}
|
||||
// If list is empty, `poll` will return null, or var will never be set
|
||||
if (preMountItemToDispatch == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (surfaceActiveForExecution(
|
||||
|
@ -1132,18 +1175,14 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
private void dispatchCommandMountItem(DispatchCommandMountItem command) {
|
||||
synchronized (mViewCommandMountItemsLock) {
|
||||
mViewCommandMountItems.add(command);
|
||||
}
|
||||
addViewCommandMountItem(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void sendAccessibilityEvent(int reactTag, int eventType) {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(new SendAccessibilityEvent(reactTag, eventType));
|
||||
}
|
||||
addMountItem(new SendAccessibilityEvent(reactTag, eventType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1156,15 +1195,13 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@DoNotStrip
|
||||
public void setJSResponder(
|
||||
final int reactTag, final int initialReactTag, final boolean blockNativeResponder) {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(
|
||||
new MountItem() {
|
||||
@Override
|
||||
public void execute(MountingManager mountingManager) {
|
||||
mountingManager.setJSResponder(reactTag, initialReactTag, blockNativeResponder);
|
||||
}
|
||||
});
|
||||
}
|
||||
addMountItem(
|
||||
new MountItem() {
|
||||
@Override
|
||||
public void execute(MountingManager mountingManager) {
|
||||
mountingManager.setJSResponder(reactTag, initialReactTag, blockNativeResponder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1173,15 +1210,13 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
*/
|
||||
@DoNotStrip
|
||||
public void clearJSResponder() {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(
|
||||
new MountItem() {
|
||||
@Override
|
||||
public void execute(MountingManager mountingManager) {
|
||||
mountingManager.clearJSResponder();
|
||||
}
|
||||
});
|
||||
}
|
||||
addMountItem(
|
||||
new MountItem() {
|
||||
@Override
|
||||
public void execute(MountingManager mountingManager) {
|
||||
mountingManager.clearJSResponder();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1229,6 +1264,51 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
return performanceCounters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction between concurrent and non-concurrent MountItem list.
|
||||
*
|
||||
* @param mountItem
|
||||
*/
|
||||
private void addMountItem(MountItem mountItem) {
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
mMountItemsConcurrent.add(mountItem);
|
||||
} else {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(mountItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction between concurrent and non-concurrent PreAllocateViewMountItem list.
|
||||
*
|
||||
* @param mountItem
|
||||
*/
|
||||
private void addPreAllocateMountItem(PreAllocateViewMountItem mountItem) {
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
mPreMountItemsConcurrent.add(mountItem);
|
||||
} else {
|
||||
synchronized (mPreMountItemsLock) {
|
||||
mPreMountItems.add(mountItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction between concurrent and non-concurrent DispatchCommandMountItem list.
|
||||
*
|
||||
* @param mountItem
|
||||
*/
|
||||
private void addViewCommandMountItem(DispatchCommandMountItem mountItem) {
|
||||
if (ReactFeatureFlags.enableLockFreeMountInstructions) {
|
||||
mViewCommandMountItemsConcurrent.add(mountItem);
|
||||
} else {
|
||||
synchronized (mViewCommandMountItemsLock) {
|
||||
mViewCommandMountItems.add(mountItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DispatchUIFrameCallback extends GuardedFrameCallback {
|
||||
|
||||
private volatile boolean mIsMountingEnabled = true;
|
||||
|
|
Загрузка…
Ссылка в новой задаче