From 4d4ab42166af4f584c9f7b3c2297085a3ed365a3 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 4 Sep 2026 10:03:01 -0700 Subject: [PATCH] Buffer async CallInvoker work with module calls (#58313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/react/react-native/pull/58313 In bridgeless, native reaches JS by two routes that end in the same `RuntimeScheduler` queue but get there differently. `callFunctionOnModule` goes through the instance's `BufferedRuntimeExecutor`; the `CallInvoker` goes straight to `scheduleTask`. The CallInvoker therefore skips the buffer entirely and can reach the runtime while a module call issued earlier is still parked, unflushed, because the bundle is mid-evaluation. Native code that issues both cannot rely on the order it issued them in, and `Task` is a min-heap on `now() + timeout(priority)` with no insertion tiebreak, so equal priorities do not settle it either. Gives the two channels the same buffering. `BufferedRuntimeExecutor` gains a priority-carrying `execute`, so work routed through it keeps the scheduler priority it was submitted with instead of collapsing to the executor default, and buffered work from both overloads stays in one submission-ordered stream. `BufferedCallInvoker` sits on that executor and becomes the bridgeless `jsCallInvoker` on Android, iOS and macOS. `invokeSync` deliberately keeps going straight to the scheduler: a synchronous call cannot wait for a flush that only happens once the bundle has run. Behind `enableBufferedCallInvoker`, default true. `ReactInstance` picks between the buffered invoker and the existing `RuntimeSchedulerCallInvoker` in one place, so the platform call sites are identical either way and the change is revertible at runtime — it moves when native-issued async work first reaches JS during startup, which is the intended contract but affects every native module. One lifetime hazard this surfaces, worth knowing about beyond this diff: `BufferedRuntimeExecutor` reaches the scheduler through a raw pointer captured at construction, which is safe only while the owning instance is alive. A CallInvoker is routinely held across instance teardown, so `BufferedCallInvoker` guards every async dispatch on a weak reference to the scheduler and drops the work when it has expired — the same contract `RuntimeSchedulerCallInvoker` has. Without that guard this reliably segfaults on a reload. Changelog: [General][Changed] - Async `CallInvoker` work is now buffered alongside callable module calls, so it no longer runs before the JS bundle has finished evaluating Reviewed By: rubennorte Differential Revision: D118456662 --- .../AppDelegate/RCTRootViewFactory.mm | 1 - .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../jni/react/runtime/jni/JReactInstance.cpp | 8 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 162 ++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../RuntimeSchedulerCallInvoker.h | 4 +- .../react/runtime/BufferedRuntimeExecutor.cpp | 24 ++- .../react/runtime/BufferedRuntimeExecutor.h | 21 ++- .../react/runtime/CallInvokerImpl.cpp | 48 ++++++ .../react/runtime/CallInvokerImpl.h | 53 ++++++ .../react/runtime/ReactInstance.cpp | 39 ++++- .../ReactCommon/react/runtime/ReactInstance.h | 12 ++ .../platform/ios/ReactCommon/RCTInstance.mm | 3 +- .../tests/cxx/BufferedRuntimeExecutorTest.cpp | 105 ++++++++++++ .../react/runtime/ReactHost.cpp | 7 +- .../ReactNativeFeatureFlags.config.js | 10 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- .../api-snapshots/ReactAndroidDebugCxx.api | 13 +- .../api-snapshots/ReactAndroidNewarchCxx.api | 13 +- .../api-snapshots/ReactAndroidReleaseCxx.api | 13 +- .../api-snapshots/ReactAppleDebugCxx.api | 13 +- .../api-snapshots/ReactAppleNewarchCxx.api | 13 +- .../api-snapshots/ReactAppleReleaseCxx.api | 13 +- .../api-snapshots/ReactCommonDebugCxx.api | 13 +- .../api-snapshots/ReactCommonNewarchCxx.api | 13 +- .../api-snapshots/ReactCommonReleaseCxx.api | 13 +- 41 files changed, 610 insertions(+), 130 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.cpp create mode 100644 packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h create mode 100644 packages/react-native/ReactCommon/react/runtime/tests/cxx/BufferedRuntimeExecutorTest.cpp diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index 4ca48c3b70ef..09f19e488262 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -27,7 +27,6 @@ #import #import #import -#import #import #import diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 5024fe15a313..8a3a0f464bf9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -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<> + * @generated SignedSource<> */ /** @@ -114,6 +114,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableBridgelessArchitecture(): Boolean = accessor.enableBridgelessArchitecture() + /** + * Route async CallInvoker work through the ReactInstance buffered runtime executor, so it is ordered against callable module calls and cannot run before the JS bundle has finished evaluating. invokeSync is unaffected. + */ + @JvmStatic + public fun enableBufferedCallInvoker(): Boolean = accessor.enableBufferedCallInvoker() + /** * Enable prop iterator setter-style construction of Props in C++ (this flag is not used in Java). */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 03b90ae5e4ed..eeef92fa9bdb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -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<<0bfeba3d07af6fade47b24974a80a592>> + * @generated SignedSource<<68aefd0293540d56f57e8badc0de04c8>> */ /** @@ -34,6 +34,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableAccumulatedUpdatesInRawPropsAndroidCache: Boolean? = null private var enableAndroidTextMeasurementOptimizationsCache: Boolean? = null private var enableBridgelessArchitectureCache: Boolean? = null + private var enableBufferedCallInvokerCache: Boolean? = null private var enableCppPropsIteratorSetterCache: Boolean? = null private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null private var enableDestroyShadowTreeRevisionAsyncCache: Boolean? = null @@ -232,6 +233,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableBufferedCallInvoker(): Boolean { + var cached = enableBufferedCallInvokerCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableBufferedCallInvoker() + enableBufferedCallInvokerCache = cached + } + return cached + } + override fun enableCppPropsIteratorSetter(): Boolean { var cached = enableCppPropsIteratorSetterCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index ad874487ab7a..5858d3e1cf1b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -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<> + * @generated SignedSource<<247f721796621af8615014477518bcd9>> */ /** @@ -56,6 +56,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableBridgelessArchitecture(): Boolean + @DoNotStrip @JvmStatic public external fun enableBufferedCallInvoker(): Boolean + @DoNotStrip @JvmStatic public external fun enableCppPropsIteratorSetter(): Boolean @DoNotStrip @JvmStatic public external fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 0ca4d5524bf9..cbddf085960a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -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<> + * @generated SignedSource<<33071257f9c96a8664c9af429e387061>> */ /** @@ -51,6 +51,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableBridgelessArchitecture(): Boolean = true + override fun enableBufferedCallInvoker(): Boolean = true + override fun enableCppPropsIteratorSetter(): Boolean = false override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index dab5705e7e45..2c4b30b0e025 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -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<<3605df96fad767e3ec3957d7305ef926>> + * @generated SignedSource<> */ /** @@ -38,6 +38,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableAccumulatedUpdatesInRawPropsAndroidCache: Boolean? = null private var enableAndroidTextMeasurementOptimizationsCache: Boolean? = null private var enableBridgelessArchitectureCache: Boolean? = null + private var enableBufferedCallInvokerCache: Boolean? = null private var enableCppPropsIteratorSetterCache: Boolean? = null private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null private var enableDestroyShadowTreeRevisionAsyncCache: Boolean? = null @@ -250,6 +251,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableBufferedCallInvoker(): Boolean { + var cached = enableBufferedCallInvokerCache + if (cached == null) { + cached = currentProvider.enableBufferedCallInvoker() + accessedFeatureFlags.add("enableBufferedCallInvoker") + enableBufferedCallInvokerCache = cached + } + return cached + } + override fun enableCppPropsIteratorSetter(): Boolean { var cached = enableCppPropsIteratorSetterCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 366da3d45758..d7d75f50493b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -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<<100e31bd98a30aba4abef10f168638fc>> + * @generated SignedSource<<915bf918212b9898319de61d4cadaa13>> */ /** @@ -51,6 +51,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableBridgelessArchitecture(): Boolean + @DoNotStrip public fun enableBufferedCallInvoker(): Boolean + @DoNotStrip public fun enableCppPropsIteratorSetter(): Boolean @DoNotStrip public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 8f8f7cd3a062..baa669d6b96b 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -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<> + * @generated SignedSource<<177c5cc7f6e970a2d4454c32d7f777ef>> */ /** @@ -123,6 +123,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableBufferedCallInvoker() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableBufferedCallInvoker"); + return method(javaProvider_); + } + bool enableCppPropsIteratorSetter() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableCppPropsIteratorSetter"); @@ -623,6 +629,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableBridgelessArchitecture( return ReactNativeFeatureFlags::enableBridgelessArchitecture(); } +bool JReactNativeFeatureFlagsCxxInterop::enableBufferedCallInvoker( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableBufferedCallInvoker(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableCppPropsIteratorSetter( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableCppPropsIteratorSetter(); @@ -1051,6 +1062,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableBridgelessArchitecture", JReactNativeFeatureFlagsCxxInterop::enableBridgelessArchitecture), + makeNativeMethod( + "enableBufferedCallInvoker", + JReactNativeFeatureFlagsCxxInterop::enableBufferedCallInvoker), makeNativeMethod( "enableCppPropsIteratorSetter", JReactNativeFeatureFlagsCxxInterop::enableCppPropsIteratorSetter), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 71b3da865135..73a8b8bfe5c3 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -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<<145b12c4a208db86c7bfdfed56cf433f>> + * @generated SignedSource<> */ /** @@ -72,6 +72,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableBridgelessArchitecture( facebook::jni::alias_ref); + static bool enableBufferedCallInvoker( + facebook::jni::alias_ref); + static bool enableCppPropsIteratorSetter( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp index b6d7e64a82f4..e79a30393b42 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -89,10 +88,11 @@ JReactInstance::JReactInstance( auto unbufferedRuntimeExecutor = instance_->getUnbufferedRuntimeExecutor(); // Set up the JS and native modules call invokers (for TurboModules) - auto jsInvoker = std::make_unique( - instance_->getRuntimeScheduler()); + // Shares the instance's BufferedRuntimeExecutor so async calls from native + // are ordered against callFunctionOnModule and cannot run before the bundle + // has evaluated. invokeSync still goes straight to the scheduler. jsCallInvokerHolder_ = jni::make_global( - CallInvokerHolder::newObjectCxxArgs(std::move(jsInvoker))); + CallInvokerHolder::newObjectCxxArgs(instance_->createJSCallInvoker())); auto nativeMethodCallInvoker = std::make_unique( sharedNativeMessageQueueThread); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 338b21c5a5ff..e2adeca46391 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -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<<9c1052fdfafd3f0ef6bcdaa740cf25ec>> + * @generated SignedSource<<7b726d7483bb35062b79e582323f0d7e>> */ /** @@ -82,6 +82,10 @@ bool ReactNativeFeatureFlags::enableBridgelessArchitecture() { return getAccessor().enableBridgelessArchitecture(); } +bool ReactNativeFeatureFlags::enableBufferedCallInvoker() { + return getAccessor().enableBufferedCallInvoker(); +} + bool ReactNativeFeatureFlags::enableCppPropsIteratorSetter() { return getAccessor().enableCppPropsIteratorSetter(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 9ae679bc6177..49079da65ebf 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -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<<414b7a90eeecd2abe9849955131fb122>> + * @generated SignedSource<<1e9009301b79f977132c4fa5599aebdd>> */ /** @@ -111,6 +111,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableBridgelessArchitecture(); + /** + * Route async CallInvoker work through the ReactInstance buffered runtime executor, so it is ordered against callable module calls and cannot run before the JS bundle has finished evaluating. invokeSync is unaffected. + */ + RN_EXPORT static bool enableBufferedCallInvoker(); + /** * Enable prop iterator setter-style construction of Props in C++ (this flag is not used in Java). */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index f0ba8df3dd6c..24db6caafcd3 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -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<<58600d43eecb5c6593a0edbac4f13ac6>> + * @generated SignedSource<<7b2f18c94d995b2108f91248fdfecb0c>> */ /** @@ -281,6 +281,24 @@ bool ReactNativeFeatureFlagsAccessor::enableBridgelessArchitecture() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableBufferedCallInvoker() { + auto flagValue = enableBufferedCallInvoker_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(14, "enableBufferedCallInvoker"); + + flagValue = currentProvider_->enableBufferedCallInvoker(); + enableBufferedCallInvoker_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableCppPropsIteratorSetter() { auto flagValue = enableCppPropsIteratorSetter_.load(); @@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCppPropsIteratorSetter() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(14, "enableCppPropsIteratorSetter"); + markFlagAsAccessed(15, "enableCppPropsIteratorSetter"); flagValue = currentProvider_->enableCppPropsIteratorSetter(); enableCppPropsIteratorSetter_ = flagValue; @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomFocusSearchOnClippedElementsAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "enableCustomFocusSearchOnClippedElementsAndroid"); + markFlagAsAccessed(16, "enableCustomFocusSearchOnClippedElementsAndroid"); flagValue = currentProvider_->enableCustomFocusSearchOnClippedElementsAndroid(); enableCustomFocusSearchOnClippedElementsAndroid_ = flagValue; @@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDestroyShadowTreeRevisionAsync() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(16, "enableDestroyShadowTreeRevisionAsync"); + markFlagAsAccessed(17, "enableDestroyShadowTreeRevisionAsync"); flagValue = currentProvider_->enableDestroyShadowTreeRevisionAsync(); enableDestroyShadowTreeRevisionAsync_ = flagValue; @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "enableDoubleMeasurementFixAndroid"); + markFlagAsAccessed(18, "enableDoubleMeasurementFixAndroid"); flagValue = currentProvider_->enableDoubleMeasurementFixAndroid(); enableDoubleMeasurementFixAndroid_ = flagValue; @@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerRootViewAttachment() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(18, "enableEagerRootViewAttachment"); + markFlagAsAccessed(19, "enableEagerRootViewAttachment"); flagValue = currentProvider_->enableEagerRootViewAttachment(); enableEagerRootViewAttachment_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableExclusivePropsUpdateAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "enableExclusivePropsUpdateAndroid"); + markFlagAsAccessed(20, "enableExclusivePropsUpdateAndroid"); flagValue = currentProvider_->enableExclusivePropsUpdateAndroid(); enableExclusivePropsUpdateAndroid_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricCommitBranching() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "enableFabricCommitBranching"); + markFlagAsAccessed(21, "enableFabricCommitBranching"); flagValue = currentProvider_->enableFabricCommitBranching(); enableFabricCommitBranching_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enableFabricLogs"); + markFlagAsAccessed(22, "enableFabricLogs"); flagValue = currentProvider_->enableFabricLogs(); enableFabricLogs_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFlexboxAutoMinSizeInStrictMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "enableFlexboxAutoMinSizeInStrictMode"); + markFlagAsAccessed(23, "enableFlexboxAutoMinSizeInStrictMode"); flagValue = currentProvider_->enableFlexboxAutoMinSizeInStrictMode(); enableFlexboxAutoMinSizeInStrictMode_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "enableFontScaleChangesUpdatingLayout"); + markFlagAsAccessed(24, "enableFontScaleChangesUpdatingLayout"); flagValue = currentProvider_->enableFontScaleChangesUpdatingLayout(); enableFontScaleChangesUpdatingLayout_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSCompressedTextFrameAdjustment() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "enableIOSCompressedTextFrameAdjustment"); + markFlagAsAccessed(25, "enableIOSCompressedTextFrameAdjustment"); flagValue = currentProvider_->enableIOSCompressedTextFrameAdjustment(); enableIOSCompressedTextFrameAdjustment_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(26, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(26, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(27, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "enableImagePrefetchingAndroid"); + markFlagAsAccessed(28, "enableImagePrefetchingAndroid"); flagValue = currentProvider_->enableImagePrefetchingAndroid(); enableImagePrefetchingAndroid_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImageTransparentTintColor() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "enableImageTransparentTintColor"); + markFlagAsAccessed(29, "enableImageTransparentTintColor"); flagValue = currentProvider_->enableImageTransparentTintColor(); enableImageTransparentTintColor_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetC // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "enableImmediateUpdateModeForContentOffsetChanges"); + markFlagAsAccessed(30, "enableImmediateUpdateModeForContentOffsetChanges"); flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges(); enableImmediateUpdateModeForContentOffsetChanges_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImperativeFocus() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "enableImperativeFocus"); + markFlagAsAccessed(31, "enableImperativeFocus"); flagValue = currentProvider_->enableImperativeFocus(); enableImperativeFocus_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(32, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIntersectionObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "enableIntersectionObserverByDefault"); + markFlagAsAccessed(33, "enableIntersectionObserverByDefault"); flagValue = currentProvider_->enableIntersectionObserverByDefault(); enableIntersectionObserverByDefault_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableKeyEvents() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "enableKeyEvents"); + markFlagAsAccessed(34, "enableKeyEvents"); flagValue = currentProvider_->enableKeyEvents(); enableKeyEvents_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(35, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(36, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(37, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMountingCoordinatorPullModelAndroid( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "enableMountingCoordinatorPullModelAndroid"); + markFlagAsAccessed(38, "enableMountingCoordinatorPullModelAndroid"); flagValue = currentProvider_->enableMountingCoordinatorPullModelAndroid(); enableMountingCoordinatorPullModelAndroid_ = flagValue; @@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMutationObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "enableMutationObserverByDefault"); + markFlagAsAccessed(39, "enableMutationObserverByDefault"); flagValue = currentProvider_->enableMutationObserverByDefault(); enableMutationObserverByDefault_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "enableNativeCSSParsing"); + markFlagAsAccessed(40, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "enablePreparedTextLayout"); + markFlagAsAccessed(41, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(42, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResizeObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "enableResizeObserverByDefault"); + markFlagAsAccessed(43, "enableResizeObserverByDefault"); flagValue = currentProvider_->enableResizeObserverByDefault(); enableResizeObserverByDefault_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(44, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableViewCulling"); + markFlagAsAccessed(45, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableViewRecycling"); + markFlagAsAccessed(46, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableViewRecyclingForImage"); + markFlagAsAccessed(47, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(48, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecyclingForText"); + markFlagAsAccessed(49, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForView"); + markFlagAsAccessed(50, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(51, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorParentTagForUnflattenCase // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "fixDifferentiatorParentTagForUnflattenCase"); + markFlagAsAccessed(52, "fixDifferentiatorParentTagForUnflattenCase"); flagValue = currentProvider_->fixDifferentiatorParentTagForUnflattenCase(); fixDifferentiatorParentTagForUnflattenCase_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(53, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(54, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(55, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "fuseboxEnabledRelease"); + markFlagAsAccessed(56, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(57, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(58, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxWebSocketEventsEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxWebSocketEventsEnabled"); + markFlagAsAccessed(59, "fuseboxWebSocketEventsEnabled"); flagValue = currentProvider_->fuseboxWebSocketEventsEnabled(); fuseboxWebSocketEventsEnabled_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "optimizedAnimatedPropUpdates"); + markFlagAsAccessed(60, "optimizedAnimatedPropUpdates"); flagValue = currentProvider_->optimizedAnimatedPropUpdates(); optimizedAnimatedPropUpdates_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(61, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "perfIssuesEnabled"); + markFlagAsAccessed(62, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "perfMonitorV2Enabled"); + markFlagAsAccessed(63, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1172,7 +1190,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "preparedTextCacheSize"); + markFlagAsAccessed(64, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(65, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "redBoxV2Android"); + markFlagAsAccessed(66, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "redBoxV2IOS"); + markFlagAsAccessed(67, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(68, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(69, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(70, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(71, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(72, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(73, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(74, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(75, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "useFabricInterop"); + markFlagAsAccessed(76, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(77, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useNestedScrollViewAndroid"); + markFlagAsAccessed(78, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useSharedAnimatedBackend"); + markFlagAsAccessed(79, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(80, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useTurboModuleInterop"); + markFlagAsAccessed(81, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1496,7 +1514,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "viewCullingOutsetRatio"); + markFlagAsAccessed(82, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "viewTransitionEnabled"); + markFlagAsAccessed(83, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(84, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1550,7 +1568,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "virtualViewPrerenderRatio"); + markFlagAsAccessed(85, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 179a6cafc5a7..c0716136aeb7 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -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<> + * @generated SignedSource<> */ /** @@ -48,6 +48,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableAccumulatedUpdatesInRawPropsAndroid(); bool enableAndroidTextMeasurementOptimizations(); bool enableBridgelessArchitecture(); + bool enableBufferedCallInvoker(); bool enableCppPropsIteratorSetter(); bool enableCustomFocusSearchOnClippedElementsAndroid(); bool enableDestroyShadowTreeRevisionAsync(); @@ -130,7 +131,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 85> accessedFeatureFlags_; + std::array, 86> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -146,6 +147,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableAccumulatedUpdatesInRawPropsAndroid_; std::atomic> enableAndroidTextMeasurementOptimizations_; std::atomic> enableBridgelessArchitecture_; + std::atomic> enableBufferedCallInvoker_; std::atomic> enableCppPropsIteratorSetter_; std::atomic> enableCustomFocusSearchOnClippedElementsAndroid_; std::atomic> enableDestroyShadowTreeRevisionAsync_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 7949c5b62064..0b167dc52b96 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -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<<9e798b7ae2a2f26097359ca37fa99d04>> + * @generated SignedSource<<2a5f641d6506566cc26cb76f62300bbb>> */ /** @@ -85,6 +85,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return true; } + bool enableBufferedCallInvoker() override { + return true; + } + bool enableCppPropsIteratorSetter() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index eb90dd2d5145..a296d81dd673 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -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<<3429660cde44d793af0e86ff425b1094>> + * @generated SignedSource<<570eb4a5cab112e5f8ea0b4fb1fa205c>> */ /** @@ -173,6 +173,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableBridgelessArchitecture(); } + bool enableBufferedCallInvoker() override { + auto value = values_["enableBufferedCallInvoker"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableBufferedCallInvoker(); + } + bool enableCppPropsIteratorSetter() override { auto value = values_["enableCppPropsIteratorSetter"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index d4da63764a89..1fe8ae4c5de5 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -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<<281f4bad9056734eec71185a3fdd6483>> + * @generated SignedSource<<0bbf1352906bbe1642cab3d2b92173e6>> */ /** @@ -41,6 +41,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableAccumulatedUpdatesInRawPropsAndroid() = 0; virtual bool enableAndroidTextMeasurementOptimizations() = 0; virtual bool enableBridgelessArchitecture() = 0; + virtual bool enableBufferedCallInvoker() = 0; virtual bool enableCppPropsIteratorSetter() = 0; virtual bool enableCustomFocusSearchOnClippedElementsAndroid() = 0; virtual bool enableDestroyShadowTreeRevisionAsync() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index bdd78c775a5e..fe848aff41dc 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -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<<54cf6211f3dbfc379c6058cbc9d9af07>> + * @generated SignedSource<<7505981b27be5298787edef4d80527e8>> */ /** @@ -114,6 +114,11 @@ bool NativeReactNativeFeatureFlags::enableBridgelessArchitecture( return ReactNativeFeatureFlags::enableBridgelessArchitecture(); } +bool NativeReactNativeFeatureFlags::enableBufferedCallInvoker( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableBufferedCallInvoker(); +} + bool NativeReactNativeFeatureFlags::enableCppPropsIteratorSetter( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableCppPropsIteratorSetter(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index c3be1b5fba54..33154de77629 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -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<<2c30d3942ab1183a99091db3566c020d>> + * @generated SignedSource<> */ /** @@ -66,6 +66,8 @@ class NativeReactNativeFeatureFlags bool enableBridgelessArchitecture(jsi::Runtime& runtime); + bool enableBufferedCallInvoker(jsi::Runtime& runtime); + bool enableCppPropsIteratorSetter(jsi::Runtime& runtime); bool enableCustomFocusSearchOnClippedElementsAndroid(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h index daa7420b81af..f10c06b7ca0a 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h @@ -19,7 +19,9 @@ class RuntimeScheduler; * Exposes RuntimeScheduler to native modules. All calls invoked on JavaScript * queue from native modules will be funneled through RuntimeScheduler. */ -class RuntimeSchedulerCallInvoker : public CallInvoker { +class [[deprecated( + "Use ReactInstance::createJSCallInvoker(), which shares the instance's buffered runtime executor so async calls are ordered against callable module calls")]] +RuntimeSchedulerCallInvoker : public CallInvoker { public: RuntimeSchedulerCallInvoker(std::weak_ptr runtimeScheduler); diff --git a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.cpp b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.cpp index db6dc089b6eb..67bf3c5f9e1c 100644 --- a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.cpp +++ b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.cpp @@ -9,16 +9,21 @@ namespace facebook::react { -BufferedRuntimeExecutor::BufferedRuntimeExecutor( - RuntimeExecutor runtimeExecutor) - : runtimeExecutor_(std::move(runtimeExecutor)), +BufferedRuntimeExecutor::BufferedRuntimeExecutor(Executor executor) + : executor_(std::move(executor)), isBufferingEnabled_(true), lastIndex_(0) {} void BufferedRuntimeExecutor::execute(Work&& callback) { + execute(SchedulerPriority::ImmediatePriority, std::move(callback)); +} + +void BufferedRuntimeExecutor::execute( + SchedulerPriority priority, + Work&& callback) { if (!isBufferingEnabled_) { - // Fast path: Schedule directly to RuntimeExecutor, without locking - runtimeExecutor_(std::move(callback)); + // Fast path: Schedule directly to the executor, without locking + executor_(priority, std::move(callback)); return; } @@ -30,14 +35,17 @@ void BufferedRuntimeExecutor::execute(Work&& callback) { uint64_t newIndex = lastIndex_++; std::scoped_lock guard(lock_); if (isBufferingEnabled_) { - queue_.push({.index_ = newIndex, .work_ = std::move(callback)}); + queue_.push( + {.index_ = newIndex, + .work_ = std::move(callback), + .priority_ = priority}); return; } // Force flush the queue to maintain the execution order. unsafeFlush(); - runtimeExecutor_(std::move(callback)); + executor_(priority, std::move(callback)); } void BufferedRuntimeExecutor::flush() { @@ -50,7 +58,7 @@ void BufferedRuntimeExecutor::unsafeFlush() { while (!queue_.empty()) { const BufferedWork& bufferedWork = queue_.top(); Work work = bufferedWork.work_; - runtimeExecutor_(std::move(work)); + executor_(bufferedWork.priority_, std::move(work)); queue_.pop(); } } diff --git a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h index 54178ba6f986..c5809f679e3a 100644 --- a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h +++ b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -21,10 +22,18 @@ class BufferedRuntimeExecutor { public: using Work = std::function; + /** + * Drains one piece of buffered work. Always given a priority; an executor + * that sits below the RuntimeScheduler, and so has no notion of one, ignores + * it. + */ + using Executor = std::function; + // A utility structure to track pending work in the order of when they arrive. struct BufferedWork { uint64_t index_; Work work_; + SchedulerPriority priority_; bool operator<(const BufferedWork &rhs) const { // Higher index has lower priority, so this inverted comparison puts @@ -33,10 +42,18 @@ class BufferedRuntimeExecutor { } }; - BufferedRuntimeExecutor(RuntimeExecutor runtimeExecutor); + BufferedRuntimeExecutor(Executor executor); + /** Equivalent to `execute(SchedulerPriority::ImmediatePriority, ...)`. */ void execute(Work &&callback); + /** + * Buffers [callback] alongside work submitted through the other overload, + * preserving submission order between them, and dispatches it at [priority] + * once flushed. + */ + void execute(SchedulerPriority priority, Work &&callback); + // Flush buffered JS calls and then diable JS buffering void flush(); @@ -44,7 +61,7 @@ class BufferedRuntimeExecutor { // Perform flushing without locking mechanism void unsafeFlush(); - RuntimeExecutor runtimeExecutor_; + Executor executor_; std::atomic isBufferingEnabled_; std::mutex lock_; std::atomic lastIndex_; diff --git a/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.cpp b/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.cpp new file mode 100644 index 000000000000..6de26f5b18ff --- /dev/null +++ b/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.cpp @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#include "CallInvokerImpl.h" + +#include + +namespace facebook::react { + +CallInvokerImpl::CallInvokerImpl( + std::shared_ptr bufferedRuntimeExecutor, + std::weak_ptr runtimeScheduler) + : bufferedRuntimeExecutor_(std::move(bufferedRuntimeExecutor)), + runtimeScheduler_(std::move(runtimeScheduler)) {} + +void CallInvokerImpl::invokeAsync(CallFunc&& func) noexcept { + // Held for the duration of the call: `BufferedRuntimeExecutor` reaches the + // scheduler through a raw pointer, which is only safe while the instance that + // owns it is alive. A CallInvoker outlives its instance routinely — a caller + // can hold one across a reload — so the weak reference is what keeps this + // from dispatching into a destroyed scheduler. Dropping the work matches what + // `RuntimeSchedulerCallInvoker` does once its scheduler is gone. + if (auto runtimeScheduler = runtimeScheduler_.lock()) { + // No priority given, so this takes the executor's default — matching what + // `RuntimeSchedulerCallInvoker` did via `scheduleWork`. + bufferedRuntimeExecutor_->execute(std::move(func)); + } +} + +void CallInvokerImpl::invokeAsync( + SchedulerPriority priority, + CallFunc&& func) noexcept { + if (auto runtimeScheduler = runtimeScheduler_.lock()) { + bufferedRuntimeExecutor_->execute(priority, std::move(func)); + } +} + +void CallInvokerImpl::invokeSync(CallFunc&& func) { + if (auto runtimeScheduler = runtimeScheduler_.lock()) { + runtimeScheduler->executeNowOnTheSameThread(std::move(func)); + } +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h b/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h new file mode 100644 index 000000000000..f4b44505ac1b --- /dev/null +++ b/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +namespace facebook::react { + +/** + * The bridgeless CallInvoker. Shares the instance's BufferedRuntimeExecutor, so async + * calls from native are ordered against `callFunctionOnModule` and the rest of + * the work that executor carries, and none of it runs before the main bundle + * has finished evaluating. + * + * Without this, the two channels reach the same RuntimeScheduler queue by + * different routes — the CallInvoker straight to `scheduleTask`, module calls + * through the buffer — so native code that issues both cannot rely on the order + * it issued them in. + * + * `invokeSync` is deliberately not buffered: a synchronous call cannot wait for + * a flush that only happens once the bundle has run, so it goes directly to the + * scheduler as before. + */ +class CallInvokerImpl : public CallInvoker { + public: + CallInvokerImpl( + std::shared_ptr bufferedRuntimeExecutor, + std::weak_ptr runtimeScheduler); + + void invokeAsync(CallFunc &&func) noexcept override; + + void invokeAsync(SchedulerPriority priority, CallFunc &&func) noexcept override; + + void invokeSync(CallFunc &&func) override; + + private: + std::shared_ptr bufferedRuntimeExecutor_; + std::weak_ptr runtimeScheduler_; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index 288bde9d70f6..97a4a6eec34e 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,6 @@ std::shared_ptr createRuntimeScheduler( // FIXME: Move creation of PerformanceEntryReporter to here and // guarantee that its lifetime is the same as the runtime. PerformanceEntryReporter::getInstance().get()); - return scheduler; } @@ -112,8 +112,18 @@ ReactInstance::ReactInstance( if (parentInspectorTarget_ != nullptr) { auto executor = parentInspectorTarget_->executorFromThis(); + // This buffer sits *below* the RuntimeScheduler — it is what feeds it — so + // there is nothing here that could act on a priority, and passing one + // through would have nowhere to go. Its only caller is + // `runtimeExecutorThatExecutesAfterInspectorSetup` below, a plain + // RuntimeExecutor, so in practice everything arrives at the default. auto bufferedRuntimeExecutorThatWaitsForInspectorSetup = - std::make_shared(runtimeExecutor); + std::make_shared( + [runtimeExecutor]( + SchedulerPriority /*priority*/, + std::function&& callback) { + runtimeExecutor(std::move(callback)); + }); auto runtimeExecutorThatExecutesAfterInspectorSetup = [bufferedRuntimeExecutorThatWaitsForInspectorSetup]( std::function&& callback) { @@ -168,10 +178,14 @@ ReactInstance::ReactInstance( setHermesEventLoopControl(runtime, runtimeScheduler); }); + // `scheduleWork` is `scheduleTask(ImmediatePriority)` on the modern + // scheduler, which is the only one bridgeless uses, so routing everything + // through `scheduleTask` leaves unprioritised callers where they were. bufferedRuntimeExecutor_ = std::make_shared( [runtimeScheduler = runtimeScheduler_.get()]( + SchedulerPriority priority, std::function&& callback) { - runtimeScheduler->scheduleWork(std::move(callback)); + runtimeScheduler->scheduleTask(priority, std::move(callback)); }); } ReactInstance::~ReactInstance() noexcept { @@ -226,6 +240,19 @@ ReactInstance::getRuntimeScheduler() noexcept { return runtimeScheduler_; } +std::shared_ptr ReactInstance::createJSCallInvoker() noexcept { + if (ReactNativeFeatureFlags::enableBufferedCallInvoker()) { + return std::make_shared( + bufferedRuntimeExecutor_, runtimeScheduler_); + } + // The flag-off path, and the last use of the deprecated invoker. It goes when + // `enableBufferedCallInvoker` is cleaned up. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return std::make_shared(runtimeScheduler_); +#pragma clang diagnostic pop +} + namespace { // Copied from JSIExecutor.cpp @@ -319,12 +346,6 @@ void ReactInstance::callFunctionOnModule( const std::string& moduleName, const std::string& methodName, folly::dynamic&& args) { - if (bufferedRuntimeExecutor_ == nullptr) { - LOG(ERROR) - << "Calling callFunctionOnModule with null BufferedRuntimeExecutor"; - return; - } - bufferedRuntimeExecutor_->execute([this, moduleName = moduleName, methodName = methodName, diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h index 33b5910baa0c..5d61e681fbb1 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -44,6 +46,16 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate { std::shared_ptr getRuntimeScheduler() noexcept; + /** + * The CallInvoker platforms should hand to TurboModules and other native + * callers. Behind `enableBufferedCallInvoker` this shares the instance's + * BufferedRuntimeExecutor, so async calls are ordered against + * `callFunctionOnModule` and do not run before the main bundle has finished + * evaluating; otherwise it is the unbuffered RuntimeScheduler-backed invoker. + * See CallInvokerImpl. + */ + std::shared_ptr createJSCallInvoker() noexcept; + struct JSRuntimeFlags { bool isProfiling = false; const std::string runtimeDiagnosticFlags = {}; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index 036551395323..de522b07d035 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -41,7 +41,6 @@ #import #import #import -#import #import #import #import @@ -333,7 +332,7 @@ - (void)_start RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor(); timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); - auto jsCallInvoker = make_shared(_reactInstance->getRuntimeScheduler()); + auto jsCallInvoker = _reactInstance->createJSCallInvoker(); RCTBridgeProxy *bridgeProxy = [[RCTBridgeProxy alloc] initWithViewRegistry:_bridgeModuleDecorator.viewRegistry_DEPRECATED moduleRegistry:_bridgeModuleDecorator.moduleRegistry diff --git a/packages/react-native/ReactCommon/react/runtime/tests/cxx/BufferedRuntimeExecutorTest.cpp b/packages/react-native/ReactCommon/react/runtime/tests/cxx/BufferedRuntimeExecutorTest.cpp new file mode 100644 index 000000000000..c05ff581b162 --- /dev/null +++ b/packages/react-native/ReactCommon/react/runtime/tests/cxx/BufferedRuntimeExecutorTest.cpp @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#include + +#include +#include + +#include +#include + +namespace facebook::react { + +namespace { + +/** + * Records what the executor was handed, without a runtime: these tests are + * about what is dispatched, in what order and at what priority, not about + * running it. + */ +struct RecordingExecutor { + std::vector priorities; + + BufferedRuntimeExecutor::Executor executor() { + return [this]( + SchedulerPriority priority, + std::function&& /*callback*/) { + priorities.push_back(priority); + }; + } +}; + +void noopWork(jsi::Runtime& /* unused */) {} + +} // namespace + +TEST(BufferedRuntimeExecutorTest, BuffersUntilFlushed) { + RecordingExecutor recorder; + auto bufferedExecutor = + std::make_shared(recorder.executor()); + + bufferedExecutor->execute(noopWork); + bufferedExecutor->execute(SchedulerPriority::LowPriority, noopWork); + + EXPECT_TRUE(recorder.priorities.empty()); + + bufferedExecutor->flush(); + + EXPECT_EQ(recorder.priorities.size(), 2u); +} + +TEST(BufferedRuntimeExecutorTest, PreservesSubmissionOrderAcrossBothOverloads) { + RecordingExecutor recorder; + auto bufferedExecutor = + std::make_shared(recorder.executor()); + + // The point of the shared buffer: work submitted with and without a priority + // is one ordered stream, so a caller that issues a module call and then a + // CallInvoker task gets them in that order. + bufferedExecutor->execute(noopWork); + bufferedExecutor->execute(SchedulerPriority::LowPriority, noopWork); + bufferedExecutor->execute(noopWork); + + bufferedExecutor->flush(); + + // The unprioritised overload reports as Immediate, which is what the modern + // scheduler's `scheduleWork` gave it before this class carried priorities. + ASSERT_EQ(recorder.priorities.size(), 3u); + EXPECT_EQ(recorder.priorities[0], SchedulerPriority::ImmediatePriority); + EXPECT_EQ(recorder.priorities[1], SchedulerPriority::LowPriority); + EXPECT_EQ(recorder.priorities[2], SchedulerPriority::ImmediatePriority); +} + +TEST(BufferedRuntimeExecutorTest, CarriesPriorityThroughTheBuffer) { + RecordingExecutor recorder; + auto bufferedExecutor = + std::make_shared(recorder.executor()); + + bufferedExecutor->execute(SchedulerPriority::LowPriority, noopWork); + bufferedExecutor->execute(SchedulerPriority::ImmediatePriority, noopWork); + bufferedExecutor->flush(); + + ASSERT_EQ(recorder.priorities.size(), 2u); + EXPECT_EQ(recorder.priorities[0], SchedulerPriority::LowPriority); + EXPECT_EQ(recorder.priorities[1], SchedulerPriority::ImmediatePriority); +} + +TEST(BufferedRuntimeExecutorTest, PassesThroughOnceFlushed) { + RecordingExecutor recorder; + BufferedRuntimeExecutor bufferedExecutor(recorder.executor()); + bufferedExecutor.flush(); + + bufferedExecutor.execute(noopWork); + bufferedExecutor.execute(SchedulerPriority::IdlePriority, noopWork); + + ASSERT_EQ(recorder.priorities.size(), 2u); + EXPECT_EQ(recorder.priorities[0], SchedulerPriority::ImmediatePriority); + EXPECT_EQ(recorder.priorities[1], SchedulerPriority::IdlePriority); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp index eb6e5f1fe5f0..2ef17cbfb33b 100644 --- a/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp +++ b/packages/react-native/ReactCxxPlatform/react/runtime/ReactHost.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -248,8 +247,10 @@ void ReactHost::createReactInstance() { reactInstanceData_->mountingManager->setUIManager(scheduler_->getUIManager()); - auto jsInvoker = std::make_shared( - reactInstance_->getRuntimeScheduler()); + // Behind `enableBufferedCallInvoker` this shares the instance's buffered + // runtime executor, so async calls are ordered against callable module calls + // and cannot run before the bundle has evaluated. + auto jsInvoker = reactInstance_->createJSCallInvoker(); if (inspector_ != nullptr) { inspector_->connectDebugger(devServerHelper_->getInspectorUrl()); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index ded56d146886..2166f296602a 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -200,6 +200,16 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'stable', }, + enableBufferedCallInvoker: { + defaultValue: true, + metadata: { + description: + 'Route async CallInvoker work through the ReactInstance buffered runtime executor, so it is ordered against callable module calls and cannot run before the JS bundle has finished evaluating. invokeSync is unaffected.', + expectedReleaseValue: true, + purpose: 'release', + }, + ossReleaseStage: 'none', + }, enableCppPropsIteratorSetter: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index c62bad74d895..e9e97cc712a9 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -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<<0774261e9f27a2df5005e11317556f83>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -63,6 +63,7 @@ export type ReactNativeFeatureFlags = Readonly<{ enableAccumulatedUpdatesInRawPropsAndroid: Getter, enableAndroidTextMeasurementOptimizations: Getter, enableBridgelessArchitecture: Getter, + enableBufferedCallInvoker: Getter, enableCppPropsIteratorSetter: Getter, enableCustomFocusSearchOnClippedElementsAndroid: Getter, enableDestroyShadowTreeRevisionAsync: Getter, @@ -266,6 +267,10 @@ export const enableAndroidTextMeasurementOptimizations: Getter = create * Feature flag to enable the new bridgeless architecture. */ export const enableBridgelessArchitecture: Getter = createNativeFlagGetter('enableBridgelessArchitecture', true); +/** + * Route async CallInvoker work through the ReactInstance buffered runtime executor, so it is ordered against callable module calls and cannot run before the JS bundle has finished evaluating. invokeSync is unaffected. + */ +export const enableBufferedCallInvoker: Getter = createNativeFlagGetter('enableBufferedCallInvoker', true); /** * Enable prop iterator setter-style construction of Props in C++ (this flag is not used in Java). */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 7d646a6a59df..32be0cf9d394 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -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<> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -39,6 +39,7 @@ export interface Spec extends TurboModule { readonly enableAccumulatedUpdatesInRawPropsAndroid?: () => boolean; readonly enableAndroidTextMeasurementOptimizations?: () => boolean; readonly enableBridgelessArchitecture?: () => boolean; + readonly enableBufferedCallInvoker?: () => boolean; readonly enableCppPropsIteratorSetter?: () => boolean; readonly enableCustomFocusSearchOnClippedElementsAndroid?: () => boolean; readonly enableDestroyShadowTreeRevisionAsync?: () => boolean; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 88c678d03edc..dbc39b765f3d 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -1902,15 +1902,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -1977,6 +1980,13 @@ class facebook::react::CallInvokerHolder : public jni::HybridClass getCallInvoker(); } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -4317,6 +4327,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 661a5bc50239..2e0f57b22350 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -1896,15 +1896,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -1971,6 +1974,13 @@ class facebook::react::CallInvokerHolder : public jni::HybridClass getCallInvoker(); } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -4161,6 +4171,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 90ee95c3c520..c057c28b79b9 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -1900,15 +1900,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -1975,6 +1978,13 @@ class facebook::react::CallInvokerHolder : public jni::HybridClass getCallInvoker(); } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -4314,6 +4324,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 1120c5614b99..c23703c476a3 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -4485,15 +4485,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -4555,6 +4558,13 @@ class facebook::react::CallInvoker { public virtual ~CallInvoker() = default; } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -6525,6 +6535,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 3a37ef145451..95f5e8b375fc 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -4472,15 +4472,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -4542,6 +4545,13 @@ class facebook::react::CallInvoker { public virtual ~CallInvoker() = default; } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -6401,6 +6411,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index fe73033a3f81..9e9d12b2d5f0 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -4483,15 +4483,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -4553,6 +4556,13 @@ class facebook::react::CallInvoker { public virtual ~CallInvoker() = default; } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -6522,6 +6532,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index b7585d7851af..97903e19cc4c 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -1208,15 +1208,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -1278,6 +1281,13 @@ class facebook::react::CallInvoker { public virtual ~CallInvoker() = default; } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -2881,6 +2891,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index f59f889b44ef..48a176dc7040 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -1203,15 +1203,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -1273,6 +1276,13 @@ class facebook::react::CallInvoker { public virtual ~CallInvoker() = default; } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -2765,6 +2775,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index 5de3379420de..3eef4b97c942 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -1206,15 +1206,18 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac } class facebook::react::BufferedRuntimeExecutor { - public BufferedRuntimeExecutor(facebook::react::RuntimeExecutor runtimeExecutor); + public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor); + public using Executor = std::function; public using Work = std::function; public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback); + public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback); public void flush(); } struct facebook::react::BufferedRuntimeExecutor::BufferedWork { public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const; public facebook::react::BufferedRuntimeExecutor::Work work_; + public facebook::react::SchedulerPriority priority_; public uint64_t index_; } @@ -1276,6 +1279,13 @@ class facebook::react::CallInvoker { public virtual ~CallInvoker() = default; } +class facebook::react::CallInvokerImpl : public facebook::react::CallInvoker { + public CallInvokerImpl(std::shared_ptr bufferedRuntimeExecutor, std::weak_ptr runtimeScheduler); + public virtual void invokeAsync(facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeAsync(facebook::react::SchedulerPriority priority, facebook::react::CallFunc&& func) noexcept override; + public virtual void invokeSync(facebook::react::CallFunc&& func) override; +} + class facebook::react::CallbackWrapper : public facebook::react::LongLivedObject { public facebook::jsi::Function& callback() noexcept; public facebook::jsi::Runtime& runtime() noexcept; @@ -2878,6 +2888,7 @@ class facebook::react::ReactInstance { public facebook::react::ReactInstance& operator=(facebook::react::ReactInstance&&) = delete; public facebook::react::RuntimeExecutor getBufferedRuntimeExecutor() noexcept; public facebook::react::RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; + public std::shared_ptr createJSCallInvoker() noexcept; public std::shared_ptr getRuntimeScheduler() noexcept; public using BindingsInstallFunc = std::function; public void callFunctionOnModule(const std::string& moduleName, const std::string& methodName, folly::dynamic&& args);