Skip to content

Commit

Permalink
Add experiment to bypass bridgeless background executor (#44797)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #44797

Noticed when profiling bridgeless that  that every call into JS would be passed via a (default priority) background thread first. This is inefficient from a scheduling perspective. Instead use the Task's default/immediate executor to immediately execute the success callback on the current thread and avoid a thread change.

This diff adds a new feature flag, to use the immediate executor for any ReactInstance method that doesn't require further synchronization within ReactInstance. For most methods, this is indeed unnecessary as ReactInstance will synchronize internally by scheduling work on the JS thread.

Changelog: [Android][Added] Added featureflag to avoid additional background threads during execution

Reviewed By: cortinico

Differential Revision: D58186090

fbshipit-source-id: 67ffed2d34083a6b6e7871160a2f3d6f1967d630
  • Loading branch information
javache authored and facebook-github-bot committed Jun 6, 2024
1 parent 52cec1e commit 4324f08
Show file tree
Hide file tree
Showing 23 changed files with 167 additions and 39 deletions.
1 change: 1 addition & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -3848,6 +3848,7 @@ public abstract interface class com/facebook/react/runtime/internal/bolts/Contin
}

public class com/facebook/react/runtime/internal/bolts/Task : com/facebook/react/interfaces/TaskInterface {
public static final field IMMEDIATE_EXECUTOR Ljava/util/concurrent/Executor;
public static final field UI_THREAD_EXECUTOR Ljava/util/concurrent/Executor;
public static fun call (Ljava/util/concurrent/Callable;)Lcom/facebook/react/runtime/internal/bolts/Task;
public static fun call (Ljava/util/concurrent/Callable;Ljava/util/concurrent/Executor;)Lcom/facebook/react/runtime/internal/bolts/Task;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<bc03d88b59af436b2db4f9048184a881>>
* @generated SignedSource<<177f05d7b2fadcfffa32cb5a7a21c76b>>
*/

/**
Expand Down Expand Up @@ -136,6 +136,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun setAndroidLayoutDirection(): Boolean = accessor.setAndroidLayoutDirection()

/**
* Invoke callbacks immediately on the ReactInstance rather than going through a background thread for synchronization
*/
@JvmStatic
public fun useImmediateExecutorInAndroidBridgeless(): Boolean = accessor.useImmediateExecutorInAndroidBridgeless()

/**
* When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<546c981f0b56bb022ad4e96e6a6ddb17>>
* @generated SignedSource<<492902f307361b8f7b7d42973561a3b4>>
*/

/**
Expand Down Expand Up @@ -38,6 +38,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var lazyAnimationCallbacksCache: Boolean? = null
private var preventDoubleTextMeasureCache: Boolean? = null
private var setAndroidLayoutDirectionCache: Boolean? = null
private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null
private var useModernRuntimeSchedulerCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null
Expand Down Expand Up @@ -206,6 +207,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}

override fun useImmediateExecutorInAndroidBridgeless(): Boolean {
var cached = useImmediateExecutorInAndroidBridgelessCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useImmediateExecutorInAndroidBridgeless()
useImmediateExecutorInAndroidBridgelessCache = cached
}
return cached
}

override fun useModernRuntimeScheduler(): Boolean {
var cached = useModernRuntimeSchedulerCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d3e722e1630002aee16eaf33b60c8a8f>>
* @generated SignedSource<<05bf91e1b2a64cdc48615137deec627a>>
*/

/**
Expand Down Expand Up @@ -64,6 +64,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun setAndroidLayoutDirection(): Boolean

@DoNotStrip @JvmStatic public external fun useImmediateExecutorInAndroidBridgeless(): Boolean

@DoNotStrip @JvmStatic public external fun useModernRuntimeScheduler(): Boolean

@DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<8cb9343bf5aa9a7ec1940720ce253c5b>>
* @generated SignedSource<<7c95ebf976344317cd8904d71ea22fe5>>
*/

/**
Expand Down Expand Up @@ -59,6 +59,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun setAndroidLayoutDirection(): Boolean = false

override fun useImmediateExecutorInAndroidBridgeless(): Boolean = false

override fun useModernRuntimeScheduler(): Boolean = false

override fun useNativeViewConfigsInBridgelessMode(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<3b6ad9b32518e319bb7d11da4eaa262c>>
* @generated SignedSource<<d23d2a5f44f2b2068dde9e85e5b1ce9f>>
*/

/**
Expand Down Expand Up @@ -42,6 +42,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var lazyAnimationCallbacksCache: Boolean? = null
private var preventDoubleTextMeasureCache: Boolean? = null
private var setAndroidLayoutDirectionCache: Boolean? = null
private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null
private var useModernRuntimeSchedulerCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null
Expand Down Expand Up @@ -228,6 +229,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun useImmediateExecutorInAndroidBridgeless(): Boolean {
var cached = useImmediateExecutorInAndroidBridgelessCache
if (cached == null) {
cached = currentProvider.useImmediateExecutorInAndroidBridgeless()
accessedFeatureFlags.add("useImmediateExecutorInAndroidBridgeless")
useImmediateExecutorInAndroidBridgelessCache = cached
}
return cached
}

override fun useModernRuntimeScheduler(): Boolean {
var cached = useModernRuntimeSchedulerCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<af167f52f81a78ac200159717eb55e8e>>
* @generated SignedSource<<d84816a13ad49b6e1c69c968a8503385>>
*/

/**
Expand Down Expand Up @@ -59,6 +59,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun setAndroidLayoutDirection(): Boolean

@DoNotStrip public fun useImmediateExecutorInAndroidBridgeless(): Boolean

@DoNotStrip public fun useModernRuntimeScheduler(): Boolean

@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import com.facebook.react.interfaces.TaskInterface;
import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler;
import com.facebook.react.interfaces.fabric.ReactSurface;
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
import com.facebook.react.modules.appearance.AppearanceModule;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.DeviceEventManagerModule;
Expand Down Expand Up @@ -215,7 +216,8 @@ TaskInterface<Void> prerenderSurface(final ReactSurfaceImpl surface) {
reactInstance -> {
log(method, "Execute");
reactInstance.prerenderSurface(surface);
});
},
mBGExecutor);
}

/**
Expand All @@ -235,7 +237,8 @@ TaskInterface<Void> startSurface(final ReactSurfaceImpl surface) {
reactInstance -> {
log(method, "Execute");
reactInstance.startSurface(surface);
});
},
mBGExecutor);
}

/**
Expand All @@ -255,7 +258,8 @@ TaskInterface<Void> stopSurface(final ReactSurfaceImpl surface) {
reactInstance -> {
log(method, "Execute");
reactInstance.stopSurface(surface);
})
},
mBGExecutor)
.makeVoid();
}

Expand Down Expand Up @@ -756,7 +760,8 @@ DefaultHardwareBackBtnHandler getDefaultBackButtonHandler() {
reactInstance -> {
log(method, "Execute");
reactInstance.loadJSBundle(bundleLoader);
});
},
null);
}

/* package */ Task<Boolean> registerSegment(
Expand All @@ -771,7 +776,8 @@ DefaultHardwareBackBtnHandler getDefaultBackButtonHandler() {
log(method, "Execute");
reactInstance.registerSegment(segmentId, path);
assertNotNull(callback).invoke();
});
},
null);
}

/* package */ void handleHostException(Exception e) {
Expand Down Expand Up @@ -800,7 +806,8 @@ DefaultHardwareBackBtnHandler getDefaultBackButtonHandler() {
method,
reactInstance -> {
reactInstance.callFunctionOnModule(moduleName, methodName, args);
});
},
null);
}

/* package */ void attachSurface(ReactSurfaceImpl surface) {
Expand Down Expand Up @@ -852,8 +859,8 @@ public void removeBeforeDestroyListener(@NonNull Function0<Unit> onBeforeDestroy
}
}

/* package */ interface VeniceThenable<T> {
void then(T t);
private interface ReactInstanceCalback {
void then(ReactInstance reactInstance);
}

@ThreadConfined("ReactHost")
Expand Down Expand Up @@ -911,11 +918,23 @@ private void raiseSoftException(String method, String message, @Nullable Throwab
TAG, new ReactNoCrashSoftException(method + ": " + message));
}

private Executor getDefaultReactInstanceExecutor() {
return ReactNativeFeatureFlags.useImmediateExecutorInAndroidBridgeless()
? Task.IMMEDIATE_EXECUTOR
: mBGExecutor;
}

/** Schedule work on a ReactInstance that is already created. */
private Task<Boolean> callWithExistingReactInstance(
final String callingMethod, final VeniceThenable<ReactInstance> continuation) {
final String callingMethod,
final ReactInstanceCalback continuation,
@Nullable Executor executor) {
final String method = "callWithExistingReactInstance(" + callingMethod + ")";

if (executor == null) {
executor = getDefaultReactInstanceExecutor();
}

return mReactInstanceTaskRef
.get()
.onSuccess(
Expand All @@ -929,14 +948,20 @@ private Task<Boolean> callWithExistingReactInstance(
continuation.then(reactInstance);
return TRUE;
},
mBGExecutor);
executor);
}

/** Create a ReactInstance if it doesn't exist already, and schedule work on it. */
private Task<Void> callAfterGetOrCreateReactInstance(
final String callingMethod, final VeniceThenable<ReactInstance> runnable) {
final String callingMethod,
final ReactInstanceCalback runnable,
@Nullable Executor executor) {
final String method = "callAfterGetOrCreateReactInstance(" + callingMethod + ")";

if (executor == null) {
executor = getDefaultReactInstanceExecutor();
}

return getOrCreateReactInstance()
.onSuccess(
task -> {
Expand All @@ -949,15 +974,14 @@ private Task<Void> callAfterGetOrCreateReactInstance(
runnable.then(reactInstance);
return null;
},
mBGExecutor)
executor)
.continueWith(
task -> {
if (task.isFaulted()) {
handleHostException(task.getError());
}
return null;
},
mBGExecutor);
});
}

private BridgelessReactContext getOrCreateReactContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ public Collection<NativeModule> getNativeModules() {
}
}

@ThreadConfined("ReactHost")
/* package */ void prerenderSurface(ReactSurfaceImpl surface) {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstance.prerenderSurface");
FLog.d(TAG, "call prerenderSurface with surface: " + surface.getModuleName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class Task<TResult> implements TaskInterface<TResult> {
* stack runs too deep, at which point it will delegate to {@link Task#BACKGROUND_EXECUTOR} in
* order to trim the stack.
*/
private static final Executor IMMEDIATE_EXECUTOR = Executors.IMMEDIATE;
public static final Executor IMMEDIATE_EXECUTOR = Executors.IMMEDIATE;

/** An {@link java.util.concurrent.Executor} that executes tasks on the UI thread. */
public static final Executor UI_THREAD_EXECUTOR = Executors.UI_THREAD;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<43b256d5f58743df52f579dc69721083>>
* @generated SignedSource<<2af7a8ae4860f81b46e48afb17ee54b6>>
*/

/**
Expand Down Expand Up @@ -147,6 +147,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}

bool useImmediateExecutorInAndroidBridgeless() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useImmediateExecutorInAndroidBridgeless");
return method(javaProvider_);
}

bool useModernRuntimeScheduler() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useModernRuntimeScheduler");
Expand Down Expand Up @@ -271,6 +277,11 @@ bool JReactNativeFeatureFlagsCxxInterop::setAndroidLayoutDirection(
return ReactNativeFeatureFlags::setAndroidLayoutDirection();
}

bool JReactNativeFeatureFlagsCxxInterop::useImmediateExecutorInAndroidBridgeless(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useImmediateExecutorInAndroidBridgeless();
}

bool JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useModernRuntimeScheduler();
Expand Down Expand Up @@ -367,6 +378,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"setAndroidLayoutDirection",
JReactNativeFeatureFlagsCxxInterop::setAndroidLayoutDirection),
makeNativeMethod(
"useImmediateExecutorInAndroidBridgeless",
JReactNativeFeatureFlagsCxxInterop::useImmediateExecutorInAndroidBridgeless),
makeNativeMethod(
"useModernRuntimeScheduler",
JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<19fe3642ec462c23d361e0f036da887a>>
* @generated SignedSource<<1d1422d073ae40ee4bbc788dc222cc28>>
*/

/**
Expand Down Expand Up @@ -84,6 +84,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool setAndroidLayoutDirection(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool useImmediateExecutorInAndroidBridgeless(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool useModernRuntimeScheduler(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b7969efd5890b7be7be5fd8e6b0bf301>>
* @generated SignedSource<<ec12fd62d1dc2109e52a95094d7ad167>>
*/

/**
Expand Down Expand Up @@ -93,6 +93,10 @@ bool ReactNativeFeatureFlags::setAndroidLayoutDirection() {
return getAccessor().setAndroidLayoutDirection();
}

bool ReactNativeFeatureFlags::useImmediateExecutorInAndroidBridgeless() {
return getAccessor().useImmediateExecutorInAndroidBridgeless();
}

bool ReactNativeFeatureFlags::useModernRuntimeScheduler() {
return getAccessor().useModernRuntimeScheduler();
}
Expand Down
Loading

0 comments on commit 4324f08

Please sign in to comment.