From f8bfdeb189e86fc817b4975a694bde1d5e117cb7 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Thu, 3 Sep 2026 05:49:53 -0700 Subject: [PATCH] Buffer async CallInvoker work with module calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: 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 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 9fa600171dd6..15e4cfe41410 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<<2d59cf5ea42847d6174fc433ba5a6fe4>> + * @generated SignedSource<<511e884fc32f9d0b44b4234dac4fed0b>> */ /** @@ -109,6 +109,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 4f5d310ebe45..fbaa92c41c98 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<<55f19e7a0d7fbbda3aa131a74909ec07>> + * @generated SignedSource<<2a789ceebb0fdfcfd52b073e54be713f>> */ /** @@ -46,6 +46,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableAccumulatedUpdatesInRawPropsAndroid(); bool enableAndroidTextMeasurementOptimizations(); bool enableBridgelessArchitecture(); + bool enableBufferedCallInvoker(); bool enableCppPropsIteratorSetter(); bool enableCustomFocusSearchOnClippedElementsAndroid(); bool enableDestroyShadowTreeRevisionAsync(); @@ -128,7 +129,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 85> accessedFeatureFlags_; + std::array, 86> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -144,6 +145,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 5e144a271817..8559ec862b54 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<<8e00ed57e2faa8e13ae2eb58afd64962>> + * @generated SignedSource<<48e738c9b760424576f0417c907cf208>> */ /** @@ -83,6 +83,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 a969aad4ad05..cac6a0e83508 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<<9c0b6df9433a5696f5eacf47713b9040>> + * @generated SignedSource<<4715d6622ac5a97d727c68f891f3dc05>> */ /** @@ -171,6 +171,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 d7e20196f597..d43be17e7eaa 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<<51d2bd8d3e88e7848039f845c1709a39>> + * @generated SignedSource<<5948f2dbffaeb9734273ace6283eb3f1>> */ /** @@ -39,6 +39,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 274015587fde..b2eda83d1270 100644 --- a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h +++ b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -19,10 +20,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 @@ -31,10 +40,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(); @@ -42,7 +59,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 b850d0fac316..24d5d5d17e9e 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -42,6 +44,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 2e6dc7e0b4e2..fa9b6e8b8691 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.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; @@ -4315,6 +4325,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 7ec351405ee3..e6a50192d4ce 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -1894,15 +1894,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_; } @@ -1969,6 +1972,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; @@ -4159,6 +4169,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 6843410835c3..564685e45ae9 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -1898,15 +1898,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_; } @@ -1973,6 +1976,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; @@ -4312,6 +4322,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 d6dc3f80a6ad..08502ba519f5 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.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; @@ -6523,6 +6533,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 da7a542fd69f..c2e154e82cfc 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -4470,15 +4470,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_; } @@ -4540,6 +4543,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; @@ -6399,6 +6409,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 a92742d8c14e..ba5b41df883c 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -4481,15 +4481,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_; } @@ -4551,6 +4554,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; @@ -6520,6 +6530,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 763c902b2e27..af785c84716a 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.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; @@ -2879,6 +2889,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 abad9815f5c4..a46347bcd0da 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -1201,15 +1201,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_; } @@ -1271,6 +1274,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; @@ -2763,6 +2773,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 0c58351b5c06..4707c2052aee 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -1204,15 +1204,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_; } @@ -1274,6 +1277,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; @@ -2876,6 +2886,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);