From 712402f4140fde5808520070b0dfeaba36e78f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 25 Jan 2024 05:20:53 -0800 Subject: [PATCH] Move feature flags for the event loop to ReactNativeFeatureFlags (#42434) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42434 Changelog: [internal] The flags for the event loop were set up using different mechanisms due to the limitations of the previous feature flags systems. Now we can centralize on the new system and use them consistently on Android and iOS. Reviewed By: RSNara Differential Revision: D52819137 fbshipit-source-id: 268f790f259c327dbc9555729537ab249e4ee935 --- .../ReactAndroid/api/ReactAndroid.api | 1 - .../react/config/ReactFeatureFlags.java | 6 --- .../featureflags/ReactNativeFeatureFlags.kt | 17 +++++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 32 ++++++++++- .../ReactNativeFeatureFlagsCxxInterop.kt | 5 +- .../ReactNativeFeatureFlagsDefaults.kt | 5 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 35 +++++++++++- .../ReactNativeFeatureFlagsProvider.kt | 5 +- .../facebook/react/runtime/ReactInstance.java | 9 +--- .../JReactNativeFeatureFlagsCxxInterop.cpp | 26 ++++++++- .../JReactNativeFeatureFlagsCxxInterop.h | 11 +++- .../ReactNativeFeatureFlagsProviderHolder.cpp | 26 ++++++++- .../ReactNativeFeatureFlagsProviderHolder.h | 5 +- .../jni/react/runtime/jni/JReactInstance.cpp | 12 ++--- .../jni/react/runtime/jni/JReactInstance.h | 6 +-- .../featureflags/ReactNativeFeatureFlags.cpp | 14 ++++- .../featureflags/ReactNativeFeatureFlags.h | 17 +++++- .../ReactNativeFeatureFlagsAccessor.cpp | 53 ++++++++++++++++++- .../ReactNativeFeatureFlagsAccessor.h | 8 ++- .../ReactNativeFeatureFlagsDefaults.h | 14 ++++- .../ReactNativeFeatureFlagsProvider.h | 5 +- .../NativeReactNativeFeatureFlags.cpp | 17 +++++- .../NativeReactNativeFeatureFlags.h | 8 ++- .../renderer/runtimescheduler/CMakeLists.txt | 1 + .../React-runtimescheduler.podspec | 1 + .../runtimescheduler/RuntimeScheduler.cpp | 6 +-- .../runtimescheduler/RuntimeScheduler.h | 1 - .../RuntimeScheduler_Modern.cpp | 10 ++-- .../tests/RuntimeSchedulerTest.cpp | 47 ++++++++++++---- .../react/renderer/scheduler/Scheduler.cpp | 11 ---- .../ReactCommon/react/runtime/CMakeLists.txt | 1 + .../react/runtime/React-RuntimeCore.podspec | 1 + .../react/runtime/React-RuntimeHermes.podspec | 1 + .../react/runtime/ReactInstance.cpp | 11 ++-- .../ReactCommon/react/runtime/ReactInstance.h | 3 +- .../react/runtime/hermes/CMakeLists.txt | 1 + .../react/runtime/hermes/HermesInstance.cpp | 13 +---- .../platform/ios/ReactCommon/RCTHost.h | 12 ----- .../platform/ios/ReactCommon/RCTHost.mm | 13 +---- .../platform/ios/ReactCommon/RCTInstance.h | 12 ----- .../platform/ios/ReactCommon/RCTInstance.mm | 8 +-- .../ReactCommon/react/utils/CoreFeatures.cpp | 2 - .../ReactCommon/react/utils/CoreFeatures.h | 9 ---- .../featureflags/ReactNativeFeatureFlags.json | 12 +++++ .../NativeReactNativeFeatureFlags.js | 5 +- .../featureflags/ReactNativeFeatureFlags.js | 17 +++++- 46 files changed, 385 insertions(+), 150 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index a6cbbc6d8d7966..31ca94e1501734 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1918,7 +1918,6 @@ public class com/facebook/react/config/ReactFeatureFlags { public static field unstable_useFabricInterop Z public static field unstable_useTurboModuleInterop Z public static field unstable_useTurboModuleInteropForAllTurboModules Z - public static field useModernRuntimeScheduler Z public static field useNativeViewConfigsInBridgelessMode Z public static field useTurboModules Z public fun ()V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index dc656c3143b672..f372d0bc7bc166 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -130,12 +130,6 @@ public class ReactFeatureFlags { /** When enabled, rawProps in Props will not include Yoga specific props. */ public static boolean excludeYogaFromRawProps = false; - /** - * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with - * priorities from any thread. - */ - public static boolean useModernRuntimeScheduler = false; - /** * Enables storing js caller stack when creating promise in native module. This is useful in case * of Promise rejection and tracing the cause. 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 1707a7591b1ced..580bbde3a5306f 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<<52367278b4d0fdf7f436bd8c511d4ffe>> */ /** @@ -33,6 +33,21 @@ object ReactNativeFeatureFlags { */ fun commonTestFlag() = accessor.commonTestFlag() + /** + * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. + */ + fun useModernRuntimeScheduler() = accessor.useModernRuntimeScheduler() + + /** + * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). + */ + fun enableMicrotasks() = accessor.enableMicrotasks() + + /** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ + fun batchRenderingUpdatesInEventLoop() = accessor.batchRenderingUpdatesInEventLoop() + /** * Overrides the feature flags with the ones provided by the given provider * (generally one that extends `ReactNativeFeatureFlagsDefaults`). 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 c3521e833ebced..ed422f43ff68a6 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<<961f1fb0a7ad802a492437f15b1f2dcb>> + * @generated SignedSource<<920bb26d238f935f63a77943df7ef6e2>> */ /** @@ -21,6 +21,9 @@ package com.facebook.react.internal.featureflags class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccessor { private var commonTestFlagCache: Boolean? = null + private var useModernRuntimeSchedulerCache: Boolean? = null + private var enableMicrotasksCache: Boolean? = null + private var batchRenderingUpdatesInEventLoopCache: Boolean? = null override fun commonTestFlag(): Boolean { var cached = commonTestFlagCache @@ -31,6 +34,33 @@ class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccessor { return cached } + override fun useModernRuntimeScheduler(): Boolean { + var cached = useModernRuntimeSchedulerCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.useModernRuntimeScheduler() + useModernRuntimeSchedulerCache = cached + } + return cached + } + + override fun enableMicrotasks(): Boolean { + var cached = enableMicrotasksCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableMicrotasks() + enableMicrotasksCache = cached + } + return cached + } + + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.batchRenderingUpdatesInEventLoop() + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + override fun override(provider: ReactNativeFeatureFlagsProvider) = ReactNativeFeatureFlagsCxxInterop.override(provider as Any) 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 cb65ae352fba59..3f8f3dcce81ec0 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<<07894609de6f9d34f7d25d372fd0e6ef>> */ /** @@ -29,6 +29,9 @@ object ReactNativeFeatureFlagsCxxInterop { } @DoNotStrip @JvmStatic external fun commonTestFlag(): Boolean + @DoNotStrip @JvmStatic external fun useModernRuntimeScheduler(): Boolean + @DoNotStrip @JvmStatic external fun enableMicrotasks(): Boolean + @DoNotStrip @JvmStatic external fun batchRenderingUpdatesInEventLoop(): Boolean @DoNotStrip @JvmStatic external fun override(provider: Any) 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 363a9990b44a06..25b0522daa8d93 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<<8c97f44276e919fc32eea07408441404>> */ /** @@ -24,4 +24,7 @@ open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvider { // but that is more expensive than just duplicating the defaults here. override fun commonTestFlag(): Boolean = false + override fun useModernRuntimeScheduler(): Boolean = false + override fun enableMicrotasks(): Boolean = false + override fun batchRenderingUpdatesInEventLoop(): Boolean = false } 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 bc622a3d70955c..8ff0a2c2778df3 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<<8bbd7e8cc2c50cfbf44ba6671d095f23>> + * @generated SignedSource<<6254686d99a8bfd0531ed629655cf673>> */ /** @@ -25,6 +25,9 @@ class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAccessor { private val accessedFeatureFlags = mutableSetOf() private var commonTestFlagCache: Boolean? = null + private var useModernRuntimeSchedulerCache: Boolean? = null + private var enableMicrotasksCache: Boolean? = null + private var batchRenderingUpdatesInEventLoopCache: Boolean? = null override fun commonTestFlag(): Boolean { var cached = commonTestFlagCache @@ -36,6 +39,36 @@ class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAccessor { return cached } + override fun useModernRuntimeScheduler(): Boolean { + var cached = useModernRuntimeSchedulerCache + if (cached == null) { + cached = currentProvider.useModernRuntimeScheduler() + accessedFeatureFlags.add("useModernRuntimeScheduler") + useModernRuntimeSchedulerCache = cached + } + return cached + } + + override fun enableMicrotasks(): Boolean { + var cached = enableMicrotasksCache + if (cached == null) { + cached = currentProvider.enableMicrotasks() + accessedFeatureFlags.add("enableMicrotasks") + enableMicrotasksCache = cached + } + return cached + } + + override fun batchRenderingUpdatesInEventLoop(): Boolean { + var cached = batchRenderingUpdatesInEventLoopCache + if (cached == null) { + cached = currentProvider.batchRenderingUpdatesInEventLoop() + accessedFeatureFlags.add("batchRenderingUpdatesInEventLoop") + batchRenderingUpdatesInEventLoopCache = cached + } + return cached + } + override fun override(provider: ReactNativeFeatureFlagsProvider) { if (accessedFeatureFlags.isNotEmpty()) { val accessedFeatureFlagsStr = accessedFeatureFlags.joinToString(separator = ", ") { it } 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 23cd91d0da34dc..604ffee96ba49c 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<> + * @generated SignedSource<<42a6943246197e110c58027b285bdde5>> */ /** @@ -21,4 +21,7 @@ package com.facebook.react.internal.featureflags interface ReactNativeFeatureFlagsProvider { fun commonTestFlag(): Boolean + fun useModernRuntimeScheduler(): Boolean + fun enableMicrotasks(): Boolean + fun batchRenderingUpdatesInEventLoop(): Boolean } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java index da24155a6758f3..f9191d23d5b81d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java @@ -170,9 +170,6 @@ public void onHostDestroy() { // Notify JS if profiling is enabled boolean isProfiling = Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS | Systrace.TRACE_TAG_REACT_JS_VM_CALLS); - // TODO(T166383606): Remove this parameter when we remove the legacy runtime scheduler or we - // have access to ReactNativeConfig before we initialize it. - boolean useModernRuntimeScheduler = ReactFeatureFlags.useModernRuntimeScheduler; mHybridData = initHybrid( jsRuntimeFactory, @@ -182,8 +179,7 @@ public void onHostDestroy() { jsTimerExecutor, reactExceptionManager, bindingsInstaller, - isProfiling, - useModernRuntimeScheduler); + isProfiling); // Set up TurboModules Systrace.beginSection( @@ -457,8 +453,7 @@ private native HybridData initHybrid( JSTimerExecutor jsTimerExecutor, ReactJsExceptionHandler jReactExceptionsManager, @Nullable BindingsInstaller jBindingsInstaller, - boolean isProfiling, - boolean useModernRuntimeScheduler); + boolean isProfiling); @DoNotStrip private static native JSTimerExecutor createJSTimerExecutor(); 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 ea67eac8841533..4a123e471a4686 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<> */ /** @@ -28,6 +28,21 @@ bool JReactNativeFeatureFlagsCxxInterop::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::useModernRuntimeScheduler(); +} + +bool JReactNativeFeatureFlagsCxxInterop::enableMicrotasks( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableMicrotasks(); +} + +bool JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + void JReactNativeFeatureFlagsCxxInterop::override( facebook::jni::alias_ref /*unused*/, jni::alias_ref provider) { @@ -48,6 +63,15 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "commonTestFlag", JReactNativeFeatureFlagsCxxInterop::commonTestFlag), + makeNativeMethod( + "useModernRuntimeScheduler", + JReactNativeFeatureFlagsCxxInterop::useModernRuntimeScheduler), + makeNativeMethod( + "enableMicrotasks", + JReactNativeFeatureFlagsCxxInterop::enableMicrotasks), + makeNativeMethod( + "batchRenderingUpdatesInEventLoop", + JReactNativeFeatureFlagsCxxInterop::batchRenderingUpdatesInEventLoop), }); } 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 0c02ca2f5ef89b..0a2e6b9c7f2318 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<> + * @generated SignedSource<> */ /** @@ -33,6 +33,15 @@ class JReactNativeFeatureFlagsCxxInterop static bool commonTestFlag( facebook::jni::alias_ref); + static bool useModernRuntimeScheduler( + facebook::jni::alias_ref); + + static bool enableMicrotasks( + facebook::jni::alias_ref); + + static bool batchRenderingUpdatesInEventLoop( + facebook::jni::alias_ref); + static void override( facebook::jni::alias_ref, jni::alias_ref provider); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.cpp index 99ded3d88d1a60..e155ac51d61bc6 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.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<<7a019bd967a22f93cd9e2e0ddb5201e3>> + * @generated SignedSource<> */ /** @@ -29,4 +29,28 @@ bool ReactNativeFeatureFlagsProviderHolder::commonTestFlag() { return method(javaProvider_); } +bool ReactNativeFeatureFlagsProviderHolder::useModernRuntimeScheduler() { + static const auto method = + facebook::jni::findClassStatic( + "com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider") + ->getMethod("useModernRuntimeScheduler"); + return method(javaProvider_); +} + +bool ReactNativeFeatureFlagsProviderHolder::enableMicrotasks() { + static const auto method = + facebook::jni::findClassStatic( + "com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider") + ->getMethod("enableMicrotasks"); + return method(javaProvider_); +} + +bool ReactNativeFeatureFlagsProviderHolder::batchRenderingUpdatesInEventLoop() { + static const auto method = + facebook::jni::findClassStatic( + "com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider") + ->getMethod("batchRenderingUpdatesInEventLoop"); + return method(javaProvider_); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.h index 6b22df569e8862..056c4ae634fab7 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/ReactNativeFeatureFlagsProviderHolder.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<<9a2d162cbd83f3b5122d0eb86f6f9177>> + * @generated SignedSource<<3550f7ee28a53a4024a48301ee38ce7e>> */ /** @@ -36,6 +36,9 @@ class ReactNativeFeatureFlagsProviderHolder : javaProvider_(make_global(javaProvider)){}; bool commonTestFlag() override; + bool useModernRuntimeScheduler() override; + bool enableMicrotasks() override; + bool batchRenderingUpdatesInEventLoop() override; private: jni::global_ref javaProvider_; 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 09a46dc4e58c90..0adccf64d7f1c1 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 @@ -36,8 +36,7 @@ JReactInstance::JReactInstance( jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling, - bool useModernRuntimeScheduler) noexcept { + bool isProfiling) noexcept { // TODO(janzer): Lazily create runtime auto sharedJSMessageQueueThread = std::make_shared(jsMessageQueueThread); @@ -65,8 +64,7 @@ JReactInstance::JReactInstance( jsRuntimeFactory->cthis()->createJSRuntime(sharedJSMessageQueueThread), sharedJSMessageQueueThread, timerManager, - std::move(jsErrorHandlingFunc), - useModernRuntimeScheduler); + std::move(jsErrorHandlingFunc)); auto bufferedRuntimeExecutor = instance_->getBufferedRuntimeExecutor(); timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); @@ -117,8 +115,7 @@ jni::local_ref JReactInstance::initHybrid( jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling, - bool useModernRuntimeScheduler) { + bool isProfiling) { return makeCxxInstance( jsRuntimeFactory, jsMessageQueueThread, @@ -127,8 +124,7 @@ jni::local_ref JReactInstance::initHybrid( jsTimerExecutor, jReactExceptionManager, jBindingsInstaller, - isProfiling, - useModernRuntimeScheduler); + isProfiling); } void JReactInstance::loadJSBundleFromAssets( diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h index 41ea4891bd50bf..b07ed7377fdfeb 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h @@ -45,8 +45,7 @@ class JReactInstance : public jni::HybridClass { jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling, - bool useModernRuntimeScheduler); + bool isProfiling); /* * Instantiates and returns an instance of `JSTimerExecutor`. @@ -91,8 +90,7 @@ class JReactInstance : public jni::HybridClass { jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling, - bool useModernRuntimeScheduler) noexcept; + bool isProfiling) noexcept; jni::alias_ref getJSCallInvokerHolder(); jni::alias_ref diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 5325717c0f38b9..b524ab794b6f15 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<<85960128b845159e7de70d0e85910dd9>> + * @generated SignedSource<> */ /** @@ -25,6 +25,18 @@ bool ReactNativeFeatureFlags::commonTestFlag() { return getAccessor().commonTestFlag(); } +bool ReactNativeFeatureFlags::useModernRuntimeScheduler() { + return getAccessor().useModernRuntimeScheduler(); +} + +bool ReactNativeFeatureFlags::enableMicrotasks() { + return getAccessor().enableMicrotasks(); +} + +bool ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop() { + return getAccessor().batchRenderingUpdatesInEventLoop(); +} + void ReactNativeFeatureFlags::override( std::unique_ptr provider) { getAccessor().override(std::move(provider)); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 96bf37fdf01503..74a859aa35c722 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<<502f6e74853b5bf526e42e682d4ca890>> + * @generated SignedSource<> */ /** @@ -37,6 +37,21 @@ class ReactNativeFeatureFlags { */ static bool commonTestFlag(); + /** + * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. + */ + static bool useModernRuntimeScheduler(); + + /** + * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). + */ + static bool enableMicrotasks(); + + /** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ + static bool batchRenderingUpdatesInEventLoop(); + /** * Overrides the feature flags with the ones provided by the given provider * (generally one that extends `ReactNativeFeatureFlagsDefaults`). diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 28bd0bc1a4502d..566c870dee004d 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<<7f8b2ae5c0b18aceeaac0ee60e53bdbb>> + * @generated SignedSource<<5df50987338c0541436b11cd5433013c>> */ /** @@ -45,6 +45,57 @@ bool ReactNativeFeatureFlagsAccessor::commonTestFlag() { return commonTestFlag_.value(); } +bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { + if (!useModernRuntimeScheduler_.has_value()) { + // Mark the flag as accessed. + static const char* flagName = "useModernRuntimeScheduler"; + if (std::find( + accessedFeatureFlags_.begin(), + accessedFeatureFlags_.end(), + flagName) == accessedFeatureFlags_.end()) { + accessedFeatureFlags_.push_back(flagName); + } + + useModernRuntimeScheduler_.emplace(currentProvider_->useModernRuntimeScheduler()); + } + + return useModernRuntimeScheduler_.value(); +} + +bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { + if (!enableMicrotasks_.has_value()) { + // Mark the flag as accessed. + static const char* flagName = "enableMicrotasks"; + if (std::find( + accessedFeatureFlags_.begin(), + accessedFeatureFlags_.end(), + flagName) == accessedFeatureFlags_.end()) { + accessedFeatureFlags_.push_back(flagName); + } + + enableMicrotasks_.emplace(currentProvider_->enableMicrotasks()); + } + + return enableMicrotasks_.value(); +} + +bool ReactNativeFeatureFlagsAccessor::batchRenderingUpdatesInEventLoop() { + if (!batchRenderingUpdatesInEventLoop_.has_value()) { + // Mark the flag as accessed. + static const char* flagName = "batchRenderingUpdatesInEventLoop"; + if (std::find( + accessedFeatureFlags_.begin(), + accessedFeatureFlags_.end(), + flagName) == accessedFeatureFlags_.end()) { + accessedFeatureFlags_.push_back(flagName); + } + + batchRenderingUpdatesInEventLoop_.emplace(currentProvider_->batchRenderingUpdatesInEventLoop()); + } + + return batchRenderingUpdatesInEventLoop_.value(); +} + void ReactNativeFeatureFlagsAccessor::override( std::unique_ptr provider) { if (!accessedFeatureFlags_.empty()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 8c869a3c5c4c45..e6d2e177599a2c 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<<3fa0171b372cf6aae150b2ec159fc41e>> + * @generated SignedSource<<11335a9c0d793a3a5a0dfdb01cd43efd>> */ /** @@ -31,6 +31,9 @@ class ReactNativeFeatureFlagsAccessor { ReactNativeFeatureFlagsAccessor(); bool commonTestFlag(); + bool useModernRuntimeScheduler(); + bool enableMicrotasks(); + bool batchRenderingUpdatesInEventLoop(); void override(std::unique_ptr provider); @@ -39,6 +42,9 @@ class ReactNativeFeatureFlagsAccessor { std::vector accessedFeatureFlags_; std::optional commonTestFlag_; + std::optional useModernRuntimeScheduler_; + std::optional enableMicrotasks_; + std::optional batchRenderingUpdatesInEventLoop_; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 5b196a5a5d073c..6e9965979bbb80 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<<76033e9fb87174da88e0ee922df28701>> + * @generated SignedSource<> */ /** @@ -30,6 +30,18 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { bool commonTestFlag() override { return false; } + + bool useModernRuntimeScheduler() override { + return false; + } + + bool enableMicrotasks() override { + return false; + } + + bool batchRenderingUpdatesInEventLoop() override { + return false; + } }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 5c1c30c24d5825..68c16ecad83ea2 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<<20dfc971dddc23a6d0cc55938b0d65b7>> + * @generated SignedSource<<6bf1fc0d7b32a36041c2371fe191792b>> */ /** @@ -26,6 +26,9 @@ class ReactNativeFeatureFlagsProvider { virtual ~ReactNativeFeatureFlagsProvider() = default; virtual bool commonTestFlag() = 0; + virtual bool useModernRuntimeScheduler() = 0; + virtual bool enableMicrotasks() = 0; + virtual bool batchRenderingUpdatesInEventLoop() = 0; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 63bd0852c036a8..421e4b3925f437 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<> + * @generated SignedSource<<01b940f6716765f9359c42180db497c0>> */ /** @@ -40,4 +40,19 @@ bool NativeReactNativeFeatureFlags::commonTestFlag( return ReactNativeFeatureFlags::commonTestFlag(); } +bool NativeReactNativeFeatureFlags::useModernRuntimeScheduler( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::useModernRuntimeScheduler(); +} + +bool NativeReactNativeFeatureFlags::enableMicrotasks( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableMicrotasks(); +} + +bool NativeReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop(); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 44bb7771143fa9..d3f46b1a667e91 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<<18a3543b75c44e00bdf73ca2f12d230c>> + * @generated SignedSource<<200fe2cf84a044164c60f6dd3c5569dd>> */ /** @@ -31,6 +31,12 @@ class NativeReactNativeFeatureFlags NativeReactNativeFeatureFlags(std::shared_ptr jsInvoker); bool commonTestFlag(jsi::Runtime& runtime); + + bool useModernRuntimeScheduler(jsi::Runtime& runtime); + + bool enableMicrotasks(jsi::Runtime& runtime); + + bool batchRenderingUpdatesInEventLoop(jsi::Runtime& runtime); }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt index b8ad2984c8e563..7c7ec30f4ebf7a 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt @@ -26,4 +26,5 @@ target_link_libraries(react_render_runtimescheduler react_render_core react_render_debug react_utils + react_featureflags runtimeexecutor) diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec index 3d44f25c8b2e06..642d71980a948b 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec @@ -56,6 +56,7 @@ Pod::Spec.new do |s| s.dependency "React-cxxreact" s.dependency "React-rendererdebug" s.dependency "React-utils" + s.dependency "React-featureflags" s.dependency "glog" s.dependency "RCT-Folly", folly_version s.dependency "React-jsi" diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp index df44f0523a6639..79be477d2c240a 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp @@ -10,6 +10,7 @@ #include "RuntimeScheduler_Modern.h" #include "SchedulerPriorityUtils.h" +#include #include #include #include "ErrorUtils.h" @@ -19,9 +20,8 @@ namespace facebook::react { namespace { std::unique_ptr getRuntimeSchedulerImplementation( RuntimeExecutor runtimeExecutor, - bool useModernRuntimeScheduler, std::function now) { - if (useModernRuntimeScheduler) { + if (ReactNativeFeatureFlags::useModernRuntimeScheduler()) { return std::make_unique( std::move(runtimeExecutor), std::move(now)); } else { @@ -33,11 +33,9 @@ std::unique_ptr getRuntimeSchedulerImplementation( RuntimeScheduler::RuntimeScheduler( RuntimeExecutor runtimeExecutor, - bool useModernRuntimeScheduler, std::function now) : runtimeSchedulerImpl_(getRuntimeSchedulerImplementation( std::move(runtimeExecutor), - useModernRuntimeScheduler, std::move(now))) {} void RuntimeScheduler::scheduleWork(RawCallback&& callback) noexcept { diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h index 40482a9872a838..26c2c336202df2 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h @@ -44,7 +44,6 @@ class RuntimeScheduler final : RuntimeSchedulerBase { public: explicit RuntimeScheduler( RuntimeExecutor runtimeExecutor, - bool useModernRuntimeScheduler = false, std::function now = RuntimeSchedulerClock::now); diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp index 033d5a2219e451..4a30a2ff5c4034 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp @@ -9,8 +9,8 @@ #include "SchedulerPriorityUtils.h" #include +#include #include -#include #include #include "ErrorUtils.h" @@ -173,7 +173,7 @@ void RuntimeScheduler_Modern::executeNowOnTheSameThread( void RuntimeScheduler_Modern::callExpiredTasks(jsi::Runtime& runtime) { // If we have first-class support for microtasks, this a no-op. - if (CoreFeatures::enableMicrotasks) { + if (ReactNativeFeatureFlags::enableMicrotasks()) { return; } @@ -185,7 +185,7 @@ void RuntimeScheduler_Modern::scheduleRenderingUpdate( RuntimeSchedulerRenderingUpdate&& renderingUpdate) { SystraceSection s("RuntimeScheduler::scheduleRenderingUpdate"); - if (CoreFeatures::blockPaintForUseLayoutEffect) { + if (ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop()) { pendingRenderingUpdates_.push(renderingUpdate); } else { if (renderingUpdate != nullptr) { @@ -296,12 +296,12 @@ void RuntimeScheduler_Modern::executeTask( executeMacrotask(runtime, task, didUserCallbackTimeout); - if (CoreFeatures::enableMicrotasks) { + if (ReactNativeFeatureFlags::enableMicrotasks()) { // "Perform a microtask checkpoint" step. executeMicrotasks(runtime); } - if (CoreFeatures::blockPaintForUseLayoutEffect) { + if (ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop()) { // "Update the rendering" step. updateRendering(); } diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp index 8897a223d05b87..0eb595cca89a80 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp @@ -8,8 +8,9 @@ #include #include #include +#include +#include #include -#include #include #include @@ -21,19 +22,41 @@ namespace facebook::react { using namespace std::chrono_literals; +static bool forcedBatchRenderingUpdatesInEventLoop = false; + +class RuntimeSchedulerTestFeatureFlags + : public ReactNativeFeatureFlagsDefaults { + public: + RuntimeSchedulerTestFeatureFlags(bool useModernRuntimeScheduler) + : useModernRuntimeScheduler_(useModernRuntimeScheduler) {} + + bool useModernRuntimeScheduler() override { + return useModernRuntimeScheduler_; + } + + bool enableMicrotasks() override { + return useModernRuntimeScheduler_; + } + + bool batchRenderingUpdatesInEventLoop() override { + return forcedBatchRenderingUpdatesInEventLoop; + } + + private: + bool useModernRuntimeScheduler_; +}; + class RuntimeSchedulerTest : public testing::TestWithParam { protected: void SetUp() override { hostFunctionCallCount_ = 0; - auto useModernRuntimeScheduler = GetParam(); - - CoreFeatures::enableMicrotasks = useModernRuntimeScheduler; + ReactNativeFeatureFlags::override( + std::make_unique(GetParam())); // Configuration that enables microtasks ::hermes::vm::RuntimeConfig::Builder runtimeConfigBuilder = - ::hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue( - useModernRuntimeScheduler); + ::hermes::vm::RuntimeConfig::Builder().withMicrotaskQueue(GetParam()); runtime_ = facebook::hermes::makeHermesRuntime(runtimeConfigBuilder.build()); @@ -54,8 +77,12 @@ class RuntimeSchedulerTest : public testing::TestWithParam { return stubClock_->getNow(); }; - runtimeScheduler_ = std::make_unique( - runtimeExecutor, useModernRuntimeScheduler, stubNow); + runtimeScheduler_ = + std::make_unique(runtimeExecutor, stubNow); + } + + void TearDown() override { + ReactNativeFeatureFlags::reset(); } jsi::Function createHostFunctionFromLambda( @@ -125,7 +152,7 @@ TEST_P(RuntimeSchedulerTest, scheduleSingleTask) { } TEST_P(RuntimeSchedulerTest, scheduleNonBatchedRenderingUpdate) { - CoreFeatures::blockPaintForUseLayoutEffect = false; + forcedBatchRenderingUpdatesInEventLoop = false; bool didRunRenderingUpdate = false; @@ -143,7 +170,7 @@ TEST_P( return; } - CoreFeatures::blockPaintForUseLayoutEffect = true; + forcedBatchRenderingUpdatesInEventLoop = true; uint nextOperationPosition = 1; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 3c08d80aa3de4d..b5493677cd007e 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -142,17 +142,6 @@ Scheduler::Scheduler( "react_fabric:remove_outstanding_surfaces_on_destruction_ios"); #endif -#ifdef ANDROID - CoreFeatures::enableMicrotasks = - reactNativeConfig_->getBool("react_fabric:enable_microtasks_android"); -#else - CoreFeatures::enableMicrotasks = - reactNativeConfig_->getBool("react_fabric:enable_microtasks_ios"); -#endif - - CoreFeatures::blockPaintForUseLayoutEffect = reactNativeConfig_->getBool( - "react_fabric:block_paint_for_use_layout_effect"); - CoreFeatures::cacheLastTextMeasurement = reactNativeConfig_->getBool("react_fabric:enable_text_measure_cache"); diff --git a/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt b/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt index 85cd27912630aa..712f8c0cf858b8 100644 --- a/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt @@ -37,4 +37,5 @@ target_link_libraries( jsireact react_utils jsinspector + react_featureflags ) diff --git a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec index d75f5ea1cd8fe2..93f5f2f120eb1e 100644 --- a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec +++ b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec @@ -55,6 +55,7 @@ Pod::Spec.new do |s| s.dependency "React-jserrorhandler" s.dependency "React-runtimescheduler" s.dependency "React-utils" + s.dependency "React-featureflags" if ENV["USE_HERMES"] == nil || ENV["USE_HERMES"] == "1" s.dependency "hermes-engine" diff --git a/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec b/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec index 6fb5d40b348b37..15ba2bde3eafa8 100644 --- a/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec +++ b/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec @@ -50,6 +50,7 @@ Pod::Spec.new do |s| s.dependency "React-utils" s.dependency "React-jsi" s.dependency "React-RuntimeCore" + s.dependency "React-featureflags" if ENV["USE_HERMES"] == nil || ENV["USE_HERMES"] == "1" s.dependency "hermes-engine" diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index 76b8029d258e46..75960335f68daf 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -15,8 +15,8 @@ #include #include #include +#include #include -#include #include #include @@ -29,8 +29,7 @@ ReactInstance::ReactInstance( std::unique_ptr runtime, std::shared_ptr jsMessageQueueThread, std::shared_ptr timerManager, - JsErrorHandler::JsErrorHandlingFunc jsErrorHandlingFunc, - bool useModernRuntimeScheduler) + JsErrorHandler::JsErrorHandlingFunc jsErrorHandlingFunc) : runtime_(std::move(runtime)), jsMessageQueueThread_(jsMessageQueueThread), timerManager_(std::move(timerManager)), @@ -70,7 +69,7 @@ ReactInstance::ReactInstance( // If we have first-class support for microtasks, // they would've been called as part of the previous callback. - if (!CoreFeatures::enableMicrotasks) { + if (!ReactNativeFeatureFlags::enableMicrotasks()) { if (auto strongTimerManager = weakTimerManager.lock()) { strongTimerManager->callReactNativeMicrotasks(jsiRuntime); } @@ -83,8 +82,8 @@ ReactInstance::ReactInstance( } }; - runtimeScheduler_ = std::make_shared( - std::move(runtimeExecutor), useModernRuntimeScheduler); + runtimeScheduler_ = + std::make_shared(std::move(runtimeExecutor)); auto pipedRuntimeExecutor = [runtimeScheduler = runtimeScheduler_.get()]( diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h index caad761b7f07f5..b33bef28db0a23 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -33,8 +33,7 @@ class ReactInstance final { std::unique_ptr runtime, std::shared_ptr jsMessageQueueThread, std::shared_ptr timerManager, - JsErrorHandler::JsErrorHandlingFunc JsErrorHandlingFunc, - bool useModernRuntimeScheduler = false); + JsErrorHandler::JsErrorHandlingFunc JsErrorHandlingFunc); RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; diff --git a/packages/react-native/ReactCommon/react/runtime/hermes/CMakeLists.txt b/packages/react-native/ReactCommon/react/runtime/hermes/CMakeLists.txt index e46f159bd12f96..cbca9d9ba948fb 100644 --- a/packages/react-native/ReactCommon/react/runtime/hermes/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/runtime/hermes/CMakeLists.txt @@ -23,6 +23,7 @@ target_link_libraries(bridgelesshermes jsi hermes_executor_common bridgeless + react_featureflags ) if(${CMAKE_BUILD_TYPE} MATCHES Debug) diff --git a/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp b/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp index 6938f93b400814..50ec845f41cf61 100644 --- a/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/hermes/HermesInstance.cpp @@ -9,6 +9,7 @@ #include #include +#include #ifdef HERMES_ENABLE_DEBUGGER #include @@ -107,16 +108,6 @@ std::unique_ptr HermesInstance::createJSRuntime( ? static_cast<::hermes::vm::gcheapsize_t>(heapSizeConfig) : 3072; -#ifdef ANDROID - bool enableMicrotasks = reactNativeConfig - ? reactNativeConfig->getBool("react_fabric:enable_microtasks_android") - : false; -#else - bool enableMicrotasks = reactNativeConfig - ? reactNativeConfig->getBool("react_fabric:enable_microtasks_ios") - : false; -#endif - ::hermes::vm::RuntimeConfig::Builder runtimeConfigBuilder = ::hermes::vm::RuntimeConfig::Builder() .withGCConfig(::hermes::vm::GCConfig::Builder() @@ -131,7 +122,7 @@ std::unique_ptr HermesInstance::createJSRuntime( .build()) .withES6Proxy(false) .withEnableSampleProfiling(true) - .withMicrotaskQueue(enableMicrotasks) + .withMicrotaskQueue(ReactNativeFeatureFlags::enableMicrotasks()) .withVMExperimentFlags(vmExperimentFlags); if (cm) { diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h index 124504a638f997..6b869377a836a5 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h @@ -35,18 +35,6 @@ NS_ASSUME_NONNULL_BEGIN @end -/** - * This is a private protocol used to configure internal behavior of the runtime. - * DO NOT USE THIS OUTSIDE OF THE REACT NATIVE CODEBASE. - */ -@protocol RCTHostDelegateInternal - -// TODO(T166383606): Remove this method when we remove the legacy runtime scheduler or we have access to -// ReactNativeConfig before we initialize it. -- (BOOL)useModernRuntimeScheduler:(RCTHost *)host; - -@end - @protocol RCTHostRuntimeDelegate - (void)host:(RCTHost *)host didInitializeRuntime:(facebook::jsi::Runtime &)runtime; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index 6b7cc6111412aa..16f9426e2a7125 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -42,7 +42,7 @@ void onReload(const PageReloadRequest &request) override __weak RCTHost *host_; }; -@interface RCTHost () +@interface RCTHost () @end @implementation RCTHost { @@ -299,17 +299,6 @@ - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Ru [self.runtimeDelegate host:self didInitializeRuntime:runtime]; } -#pragma mark - RCTInstanceDelegateInternal - -- (BOOL)useModernRuntimeScheduler:(RCTHost *)host -{ - if ([_hostDelegate respondsToSelector:@selector(useModernRuntimeScheduler:)]) { - return [(id)_hostDelegate useModernRuntimeScheduler:self]; - } - - return NO; -} - #pragma mark - RCTContextContainerHandling - (void)didCreateContextContainer:(std::shared_ptr)contextContainer diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h index 047dc38af97774..befe718b0535be 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h @@ -46,18 +46,6 @@ RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *_Nullable flags); @end -/** - * This is a private protocol used to configure internal behavior of the runtime. - * DO NOT USE THIS OUTSIDE OF THE REACT NATIVE CODEBASE. - */ -@protocol RCTInstanceDelegateInternal - -// TODO(T166383606): Remove this method when we remove the legacy runtime scheduler or we have access to -// ReactNativeConfig before we initialize it. -- (BOOL)useModernRuntimeScheduler:(RCTInstance *)instance; - -@end - typedef void (^_Null_unspecified RCTInstanceInitialBundleLoadCompletionBlock)(); /** 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 6ed73ed4d85814..03d6d9a25ae47f 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 @@ -216,18 +216,12 @@ - (void)_start __weak __typeof(self) weakSelf = self; auto jsErrorHandlingFunc = [=](MapBuffer errorMap) { [weakSelf _handleJSErrorMap:std::move(errorMap)]; }; - auto useModernRuntimeScheduler = false; - if ([_delegate respondsToSelector:@selector(useModernRuntimeScheduler:)]) { - useModernRuntimeScheduler = [(id)_delegate useModernRuntimeScheduler:self]; - } - // Create the React Instance _reactInstance = std::make_unique( _jsRuntimeFactory->createJSRuntime(_jsThreadManager.jsMessageThread), _jsThreadManager.jsMessageThread, timerManager, - jsErrorHandlingFunc, - useModernRuntimeScheduler); + jsErrorHandlingFunc); _valid = true; RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor(); diff --git a/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp b/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp index fcdc92e97f9941..19b3535c5473c1 100644 --- a/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp +++ b/packages/react-native/ReactCommon/react/utils/CoreFeatures.cpp @@ -10,7 +10,6 @@ namespace facebook::react { bool CoreFeatures::enablePropIteratorSetter = false; -bool CoreFeatures::blockPaintForUseLayoutEffect = false; bool CoreFeatures::cacheLastTextMeasurement = false; bool CoreFeatures::cancelImageDownloadsOnRecycle = false; bool CoreFeatures::enableGranularScrollViewStateUpdatesIOS = false; @@ -19,7 +18,6 @@ bool CoreFeatures::enableGranularShadowTreeStateReconciliation = false; bool CoreFeatures::enableDefaultAsyncBatchedPriority = false; bool CoreFeatures::enableClonelessStateProgression = false; bool CoreFeatures::excludeYogaFromRawProps = false; -bool CoreFeatures::enableMicrotasks = false; bool CoreFeatures::enableReportEventPaintTime = false; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/utils/CoreFeatures.h b/packages/react-native/ReactCommon/react/utils/CoreFeatures.h index 2af28d74a10b9e..7e94452c58d56a 100644 --- a/packages/react-native/ReactCommon/react/utils/CoreFeatures.h +++ b/packages/react-native/ReactCommon/react/utils/CoreFeatures.h @@ -19,11 +19,6 @@ class CoreFeatures { // Specifies whether the iterator-style prop parsing is enabled. static bool enablePropIteratorSetter; - // When enabled, Fabric will block paint to allow for state updates in - // useLayoutEffect hooks to be processed. This changes affects scheduling of - // when a transaction is mounted. - static bool blockPaintForUseLayoutEffect; - // Yoga might measure multiple times the same Text with the same constraints // This flag enables a caching mechanism to avoid subsequents measurements // of the same Text with the same constrainst. @@ -54,10 +49,6 @@ class CoreFeatures { // When enabled, rawProps in Props will not include Yoga specific props. static bool excludeYogaFromRawProps; - // Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler - // (execution). - static bool enableMicrotasks; - // Report paint time inside the Event Timing API implementation // (PerformanceObserver). static bool enableReportEventPaintTime; diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.json b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.json index 974c2f6fce5c13..dc94424def4951 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.json +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.json @@ -3,6 +3,18 @@ "commonTestFlag": { "description": "Common flag for testing. Do NOT modify.", "defaultValue": false + }, + "useModernRuntimeScheduler": { + "description": "When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread.", + "defaultValue": false + }, + "enableMicrotasks": { + "description": "Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution).", + "defaultValue": false + }, + "batchRenderingUpdatesInEventLoop": { + "description": "When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop.", + "defaultValue": false } }, "jsOnly": { diff --git a/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js index 3132692c948d21..f7f7cfc43347bf 100644 --- a/packages/react-native/src/private/featureflags/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/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<<564159837241d197ebc3084ff9e72b7c>> * @flow strict-local */ @@ -24,6 +24,9 @@ import * as TurboModuleRegistry from '../../../Libraries/TurboModule/TurboModule export interface Spec extends TurboModule { +commonTestFlag?: () => boolean; + +useModernRuntimeScheduler?: () => boolean; + +enableMicrotasks?: () => boolean; + +batchRenderingUpdatesInEventLoop?: () => boolean; } const NativeReactNativeFeatureFlags: ?Spec = TurboModuleRegistry.get( diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 8495e3af234525..09fa4b1a8c6807 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<> + * @generated SignedSource<<4e4ce58c1ce1a95355bba0dfe099e26e>> * @flow strict-local */ @@ -34,6 +34,9 @@ export type ReactNativeFeatureFlagsJsOnlyOverrides = Partial, + useModernRuntimeScheduler: Getter, + enableMicrotasks: Getter, + batchRenderingUpdatesInEventLoop: Getter, } /** @@ -45,6 +48,18 @@ export const jsOnlyTestFlag: Getter = createJavaScriptFlagGetter('jsOnl * Common flag for testing. Do NOT modify. */ export const commonTestFlag: Getter = createNativeFlagGetter('commonTestFlag', false); +/** + * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with priorities from any thread. + */ +export const useModernRuntimeScheduler: Getter = createNativeFlagGetter('useModernRuntimeScheduler', false); +/** + * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). + */ +export const enableMicrotasks: Getter = createNativeFlagGetter('enableMicrotasks', false); +/** + * When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop. + */ +export const batchRenderingUpdatesInEventLoop: Getter = createNativeFlagGetter('batchRenderingUpdatesInEventLoop', false); /** * Overrides the feature flags with the provided methods.