Delete LockFreeEventDispatcher

Summary:
This diff removes LockFreeEventDispatcher, its dependencies and feature flags

changelog: [internal] internal

Reviewed By: javache, sammy-SC

Differential Revision: D40356071

fbshipit-source-id: 42212e86fe4ace0da87fd8e7ab8ee50100175432
This commit is contained in:
David Vacca
2022-10-18 13:32:03 -07:00
committed by Facebook GitHub Bot
parent a766927ad6
commit ea6c7f504b
3 changed files with 1 additions and 249 deletions
@@ -82,8 +82,6 @@ public class ReactFeatureFlags {
/** Feature Flag to use overflowInset values provided by Yoga */
public static boolean useOverflowInset = false;
public static boolean enableLockFreeEventDispatcher = false;
public static boolean enableAggressiveEventEmitterCleanup = false;
public static boolean insertZReorderBarriersOnViewGroupChildren = true;
@@ -81,7 +81,6 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.react.uimanager.events.EventCategoryDef;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.EventDispatcherImpl;
import com.facebook.react.uimanager.events.LockFreeEventDispatcherImpl;
import com.facebook.react.views.text.TextLayoutManager;
import com.facebook.react.views.text.TextLayoutManagerMapBuffer;
import java.util.HashMap;
@@ -241,10 +240,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
mMountingManager = new MountingManager(viewManagerRegistry, mMountItemExecutor);
mMountItemDispatcher =
new MountItemDispatcher(mMountingManager, new MountItemDispatchListener());
mEventDispatcher =
ReactFeatureFlags.enableLockFreeEventDispatcher
? new LockFreeEventDispatcherImpl(reactContext)
: new EventDispatcherImpl(reactContext);
mEventDispatcher = new EventDispatcherImpl(reactContext);
mShouldDeallocateEventDispatcher = true;
mEventBeatManager = eventBeatManager;
mReactApplicationContext.addLifecycleEventListener(this);
@@ -1,242 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.uimanager.events;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.modules.core.ChoreographerCompat;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.common.UIManagerType;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Class responsible for dispatching UI events to JS. The main purpose of this class is to act as an
* intermediary between UI code generating events and JS, making sure we don't send more events than
* JS can process.
*
* <p>To use it, create a subclass of {@link Event} and call {@link #dispatchEvent(Event)} whenever
* there's a UI event to dispatch.
*
* <p>This class works by installing a Choreographer frame callback on the main thread. This
* callback then enqueues a runnable on the JS thread (if one is not already pending) that is
* responsible for actually dispatch events to JS. This implementation depends on the properties
* that 1) FrameCallbacks run after UI events have been processed in Choreographer.java 2) when we
* enqueue a runnable on the JS queue thread, it won't be called until after any previously enqueued
* JS jobs have finished processing
*
* <p>If JS is taking a long time processing events, then the UI events generated on the UI thread
* can be coalesced into fewer events so that when the runnable runs, we don't overload JS with a
* ton of events and make it get even farther behind.
*
* <p>Ideally, we don't need this and JS is fast enough to process all the events each frame, but
* bad things happen, including load on CPUs from the system, and we should handle this case well.
*
* <p>== Event Cookies ==
*
* <p>An event cookie is made up of the event type id, view tag, and a custom coalescing key. Only
* Events that have the same cookie can be coalesced.
*
* <p>Event Cookie Composition: VIEW_TAG_MASK = 0x00000000ffffffff EVENT_TYPE_ID_MASK =
* 0x0000ffff00000000 COALESCING_KEY_MASK = 0xffff000000000000
*
* <p>This is a copy of EventDispatcherImpl, meant only to remove locking and synchronization.
*/
public class LockFreeEventDispatcherImpl implements EventDispatcher, LifecycleEventListener {
private final boolean DEBUG_MODE = ReactBuildConfig.DEBUG;
private final String TAG = LockFreeEventDispatcherImpl.class.getSimpleName();
private final ReactApplicationContext mReactContext;
private final CopyOnWriteArrayList<EventDispatcherListener> mListeners =
new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<BatchEventDispatchedListener> mPostEventDispatchListeners =
new CopyOnWriteArrayList<>();
private final LockFreeEventDispatcherImpl.ScheduleDispatchFrameCallback mCurrentFrameCallback =
new LockFreeEventDispatcherImpl.ScheduleDispatchFrameCallback();
private volatile ReactEventEmitter mReactEventEmitter;
public LockFreeEventDispatcherImpl(ReactApplicationContext reactContext) {
mReactContext = reactContext;
mReactContext.addLifecycleEventListener(this);
mReactEventEmitter = new ReactEventEmitter(mReactContext);
}
/** Sends the given Event to C++, where it will be flushed to JS ASAP. */
public void dispatchEvent(Event event) {
Assertions.assertCondition(event.isInitialized(), "Dispatched event hasn't been initialized");
Assertions.assertNotNull(mReactEventEmitter);
if (DEBUG_MODE) {
FLog.v(TAG, "dispatchEvent: " + event.toString());
}
for (EventDispatcherListener listener : mListeners) {
listener.onEventDispatch(event);
}
event.dispatchModern(mReactEventEmitter);
event.dispose();
}
public void dispatchAllEvents() {
maybePostFrameCallbackFromNonUI();
}
private void maybePostFrameCallbackFromNonUI() {
if (mReactEventEmitter != null) {
// If the host activity is paused, the frame callback may not be currently
// posted. Ensure that it is so that this event gets delivered promptly.
mCurrentFrameCallback.maybePostFromNonUI();
} else {
// No JS application has started yet, or resumed. This can happen when a ReactRootView is
// added to view hierarchy, but ReactContext creation has not completed yet. In this case, any
// touch event dispatch will hit this codepath, and we simply queue them so that they
// are dispatched once ReactContext creation completes and JS app is running.
}
}
/** Add a listener to this EventDispatcher. */
public void addListener(EventDispatcherListener listener) {
mListeners.add(listener);
}
/** Remove a listener from this EventDispatcher. */
public void removeListener(EventDispatcherListener listener) {
mListeners.remove(listener);
}
public void addBatchEventDispatchedListener(BatchEventDispatchedListener listener) {
mPostEventDispatchListeners.add(listener);
}
public void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener) {
mPostEventDispatchListeners.remove(listener);
}
@Override
public void onHostResume() {
UiThreadUtil.assertOnUiThread();
mCurrentFrameCallback.resume();
}
@Override
public void onHostPause() {
stopFrameCallback();
}
@Override
public void onHostDestroy() {
stopFrameCallback();
}
public void onCatalystInstanceDestroyed() {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
stopFrameCallback();
}
});
}
private void stopFrameCallback() {
UiThreadUtil.assertOnUiThread();
mCurrentFrameCallback.stop();
}
public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) {
mReactEventEmitter.register(uiManagerType, eventEmitter);
}
public void registerEventEmitter(
@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) {
mReactEventEmitter.register(uiManagerType, eventEmitter);
}
public void unregisterEventEmitter(@UIManagerType int uiManagerType) {
mReactEventEmitter.unregister(uiManagerType);
}
private class ScheduleDispatchFrameCallback extends ChoreographerCompat.FrameCallback {
private volatile boolean mIsPosted = false;
private boolean mShouldStop = false;
@Override
public void doFrame(long frameTimeNanos) {
UiThreadUtil.assertOnUiThread();
if (mShouldStop) {
mIsPosted = false;
} else {
post();
}
driveEventBeats();
}
public void resume() {
mShouldStop = false;
maybePost();
}
public void stop() {
mShouldStop = true;
}
public void maybePost() {
if (!mIsPosted) {
mIsPosted = true;
post();
}
}
private void post() {
ReactChoreographer.getInstance()
.postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, mCurrentFrameCallback);
}
public void maybePostFromNonUI() {
if (mIsPosted) {
return;
}
// We should only hit this slow path when we receive events while the host activity is paused.
if (mReactContext.isOnUiQueueThread()) {
maybePost();
} else {
mReactContext.runOnUiQueueThread(
new Runnable() {
@Override
public void run() {
maybePost();
}
});
}
}
}
/**
* Is this a misnomer? It's called "driveEventBeats" but then is just calls
* `onBatchEventDispatched` listeners? Practically, all that `onBatchEventDispatched` is used for
* in Fabric is to drive AsyncEventBeats.
*
* <p>If this class ships for Fabric, we should remove `driveEventBeats` from here entirely,
* because it no longer needs to be connected to this event dispatcher. We should/probably can
* also delete `onRequestEventBeat` entirely.
*/
private void driveEventBeats() {
for (BatchEventDispatchedListener listener : mPostEventDispatchListeners) {
listener.onBatchEventDispatched();
}
}
}