From 6f1c2a512e44d25edefea53e864f688018745c07 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Sat, 16 Nov 2024 08:22:37 -0800 Subject: [PATCH] synchronise dispatching of view commands through RuntimeScheduler (#47604) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/47604 ## Changelog: [iOS] [Fixed] - Fixed use of view commands from layout effects Mounting of views is delayed by runtime scheduler to allow React to run layout effects. Execution of view commands must by queued together with mounting of views, otherwise it might be executed before views are mounted. When this happens, view commands are ignored. So before, if view command was executed from layout effect (or ref function), it would get dispatched to the UI thread as quickly as possible. But mounting of views would be delayed. To fix this, both mounting of views and view commands are put on the same queue inside of RuntimeScheduler. ## What about Android? Android employs a [retry mechanism](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java#L211) that was needed for react tag based view commands. In paper, one could send a view command to a react tag which was completely disconnected from whether a view exists or not. iOS was built with ref commands in mind, so it doesn't have this mechanism. Fixes: https://github.com/facebook/react-native/issues/47576 Reviewed By: javache, cipolleschi Differential Revision: D65909191 fbshipit-source-id: 9d2a444879bee62a7b8b7d31edde450e18339b89 --- .../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 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 82 +++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../react/renderer/scheduler/Scheduler.cpp | 14 +++- .../ReactNativeFeatureFlags.config.js | 9 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 20 files changed, 169 insertions(+), 51 deletions(-) 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 43a0790ca27..8b434878dcc 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<<1adfbb1f0b9791d93ea00f39fbce5520>> */ /** @@ -118,6 +118,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableFabricRendererExclusively(): Boolean = accessor.enableFabricRendererExclusively() + /** + * Synchronise the view command dispatching with mounting of new transaction + */ + @JvmStatic + public fun enableFixForViewCommandRace(): Boolean = accessor.enableFixForViewCommandRace() + /** * When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing. */ 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 b494443cbd0..af74d71b02f 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<<2a25e30cbee2548ee5538ecbc4b92fdc>> + * @generated SignedSource<> */ /** @@ -35,6 +35,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var enableFabricLogsCache: Boolean? = null private var enableFabricRendererCache: Boolean? = null private var enableFabricRendererExclusivelyCache: Boolean? = null + private var enableFixForViewCommandRaceCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableLayoutAnimationsOnAndroidCache: Boolean? = null @@ -202,6 +203,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun enableFixForViewCommandRace(): Boolean { + var cached = enableFixForViewCommandRaceCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableFixForViewCommandRace() + enableFixForViewCommandRaceCache = cached + } + return cached + } + override fun enableGranularShadowTreeStateReconciliation(): Boolean { var cached = enableGranularShadowTreeStateReconciliationCache 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 3ce01e133fc..43ca37a4dd7 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<<0656ed5b5317e6caf85605bba62d08d1>> + * @generated SignedSource<<3980c51f97745c20e0fd510f595477dc>> */ /** @@ -58,6 +58,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableFabricRendererExclusively(): Boolean + @DoNotStrip @JvmStatic public external fun enableFixForViewCommandRace(): Boolean + @DoNotStrip @JvmStatic public external fun enableGranularShadowTreeStateReconciliation(): Boolean @DoNotStrip @JvmStatic public external fun enableIOSViewClipToPaddingBox(): 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 8f9715ab208..99fbdcdea63 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<<608eece71b01769be0df8731774f9b0a>> + * @generated SignedSource<> */ /** @@ -53,6 +53,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableFabricRendererExclusively(): Boolean = false + override fun enableFixForViewCommandRace(): Boolean = false + override fun enableGranularShadowTreeStateReconciliation(): Boolean = false override fun enableIOSViewClipToPaddingBox(): 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 6b2f7d028da..0402d14e36e 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<<71dcfc3812f442a35ae2c24621c05605>> + * @generated SignedSource<> */ /** @@ -39,6 +39,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var enableFabricLogsCache: Boolean? = null private var enableFabricRendererCache: Boolean? = null private var enableFabricRendererExclusivelyCache: Boolean? = null + private var enableFixForViewCommandRaceCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableLayoutAnimationsOnAndroidCache: Boolean? = null @@ -221,6 +222,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableFixForViewCommandRace(): Boolean { + var cached = enableFixForViewCommandRaceCache + if (cached == null) { + cached = currentProvider.enableFixForViewCommandRace() + accessedFeatureFlags.add("enableFixForViewCommandRace") + enableFixForViewCommandRaceCache = cached + } + return cached + } + override fun enableGranularShadowTreeStateReconciliation(): Boolean { var cached = enableGranularShadowTreeStateReconciliationCache 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 13d7ca329ee..59d83c8de65 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<<2dd3e32b571ae171bd5621424116188b>> + * @generated SignedSource<> */ /** @@ -53,6 +53,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableFabricRendererExclusively(): Boolean + @DoNotStrip public fun enableFixForViewCommandRace(): Boolean + @DoNotStrip public fun enableGranularShadowTreeStateReconciliation(): Boolean @DoNotStrip public fun enableIOSViewClipToPaddingBox(): 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 9338814c888..683af43fffc 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<<62bd0386265d0fa4bdccab1faf22d25c>> + * @generated SignedSource<<9c1572172189f9f9ded86a9fdb1cb021>> */ /** @@ -129,6 +129,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool enableFixForViewCommandRace() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableFixForViewCommandRace"); + return method(javaProvider_); + } + bool enableGranularShadowTreeStateReconciliation() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableGranularShadowTreeStateReconciliation"); @@ -394,6 +400,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableFabricRendererExclusively( return ReactNativeFeatureFlags::enableFabricRendererExclusively(); } +bool JReactNativeFeatureFlagsCxxInterop::enableFixForViewCommandRace( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableFixForViewCommandRace(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableGranularShadowTreeStateReconciliation( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation(); @@ -625,6 +636,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableFabricRendererExclusively", JReactNativeFeatureFlagsCxxInterop::enableFabricRendererExclusively), + makeNativeMethod( + "enableFixForViewCommandRace", + JReactNativeFeatureFlagsCxxInterop::enableFixForViewCommandRace), makeNativeMethod( "enableGranularShadowTreeStateReconciliation", JReactNativeFeatureFlagsCxxInterop::enableGranularShadowTreeStateReconciliation), 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 b74fa4ab351..bff84fc644e 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<<00e9f2368ec7745d960e8671b0e58d19>> + * @generated SignedSource<<92fcd7e8c814a6a76ec15c4cd60e00e4>> */ /** @@ -75,6 +75,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableFabricRendererExclusively( facebook::jni::alias_ref); + static bool enableFixForViewCommandRace( + facebook::jni::alias_ref); + static bool enableGranularShadowTreeStateReconciliation( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index b9962928aa3..4dbbc7656d2 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<> + * @generated SignedSource<<93b93588adcd6b45ec748dff7ee18f07>> */ /** @@ -86,6 +86,10 @@ bool ReactNativeFeatureFlags::enableFabricRendererExclusively() { return getAccessor().enableFabricRendererExclusively(); } +bool ReactNativeFeatureFlags::enableFixForViewCommandRace() { + return getAccessor().enableFixForViewCommandRace(); +} + bool ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation() { return getAccessor().enableGranularShadowTreeStateReconciliation(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 5b5b4f8a4b3..b1e39d72381 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<<079c1e8cd65a004c1b26f1a2517e9042>> + * @generated SignedSource<> */ /** @@ -114,6 +114,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableFabricRendererExclusively(); + /** + * Synchronise the view command dispatching with mounting of new transaction + */ + RN_EXPORT static bool enableFixForViewCommandRace(); + /** * When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 8e03edeecc4..59dbfc6bff6 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<<8f2e12ef2ddfb1e49b0cdfed8e695e54>> + * @generated SignedSource<<69c4aa85ed449d5d0fa90af4fa0be498>> */ /** @@ -299,6 +299,24 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRendererExclusively() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableFixForViewCommandRace() { + auto flagValue = enableFixForViewCommandRace_.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(15, "enableFixForViewCommandRace"); + + flagValue = currentProvider_->enableFixForViewCommandRace(); + enableFixForViewCommandRace_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliation() { auto flagValue = enableGranularShadowTreeStateReconciliation_.load(); @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliatio // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "enableGranularShadowTreeStateReconciliation"); + markFlagAsAccessed(16, "enableGranularShadowTreeStateReconciliation"); flagValue = currentProvider_->enableGranularShadowTreeStateReconciliation(); enableGranularShadowTreeStateReconciliation_ = flagValue; @@ -326,7 +344,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(16, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(17, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -344,7 +362,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(17, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(18, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -362,7 +380,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(18, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(19, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLongTaskAPI() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "enableLongTaskAPI"); + markFlagAsAccessed(20, "enableLongTaskAPI"); flagValue = currentProvider_->enableLongTaskAPI(); enableLongTaskAPI_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(21, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreciseSchedulingForPremountItemsOnA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enablePreciseSchedulingForPremountItemsOnAndroid"); + markFlagAsAccessed(22, "enablePreciseSchedulingForPremountItemsOnAndroid"); flagValue = currentProvider_->enablePreciseSchedulingForPremountItemsOnAndroid(); enablePreciseSchedulingForPremountItemsOnAndroid_ = flagValue; @@ -434,7 +452,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(22, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(23, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableReportEventPaintTime() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "enableReportEventPaintTime"); + markFlagAsAccessed(24, "enableReportEventPaintTime"); flagValue = currentProvider_->enableReportEventPaintTime(); enableReportEventPaintTime_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "enableSynchronousStateUpdates"); + markFlagAsAccessed(25, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableUIConsistency() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "enableUIConsistency"); + markFlagAsAccessed(26, "enableUIConsistency"); flagValue = currentProvider_->enableUIConsistency(); enableUIConsistency_ = flagValue; @@ -506,7 +524,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(26, "enableViewRecycling"); + markFlagAsAccessed(27, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::excludeYogaFromRawProps() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "excludeYogaFromRawProps"); + markFlagAsAccessed(28, "excludeYogaFromRawProps"); flagValue = currentProvider_->excludeYogaFromRawProps(); excludeYogaFromRawProps_ = flagValue; @@ -542,7 +560,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(28, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(29, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMountingCoordinatorReportedPendingTrans // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid"); + markFlagAsAccessed(30, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid"); flagValue = currentProvider_->fixMountingCoordinatorReportedPendingTransactionsOnAndroid(); fixMountingCoordinatorReportedPendingTransactionsOnAndroid_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledDebug() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "fuseboxEnabledDebug"); + markFlagAsAccessed(31, "fuseboxEnabledDebug"); flagValue = currentProvider_->fuseboxEnabledDebug(); fuseboxEnabledDebug_ = flagValue; @@ -596,7 +614,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(31, "fuseboxEnabledRelease"); + markFlagAsAccessed(32, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::initEagerTurboModulesOnNativeModulesQueueA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "initEagerTurboModulesOnNativeModulesQueueAndroid"); + markFlagAsAccessed(33, "initEagerTurboModulesOnNativeModulesQueueAndroid"); flagValue = currentProvider_->initEagerTurboModulesOnNativeModulesQueueAndroid(); initEagerTurboModulesOnNativeModulesQueueAndroid_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "lazyAnimationCallbacks"); + markFlagAsAccessed(34, "lazyAnimationCallbacks"); flagValue = currentProvider_->lazyAnimationCallbacks(); lazyAnimationCallbacks_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "loadVectorDrawablesOnImages"); + markFlagAsAccessed(35, "loadVectorDrawablesOnImages"); flagValue = currentProvider_->loadVectorDrawablesOnImages(); loadVectorDrawablesOnImages_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "setAndroidLayoutDirection"); + markFlagAsAccessed(36, "setAndroidLayoutDirection"); flagValue = currentProvider_->setAndroidLayoutDirection(); setAndroidLayoutDirection_ = flagValue; @@ -686,7 +704,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(36, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(37, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -704,7 +722,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(37, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(38, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -722,7 +740,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(38, "useFabricInterop"); + markFlagAsAccessed(39, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(40, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -758,7 +776,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(40, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(41, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "useOptimisedViewPreallocationOnAndroid"); + markFlagAsAccessed(42, "useOptimisedViewPreallocationOnAndroid"); flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid(); useOptimisedViewPreallocationOnAndroid_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(43, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(44, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -830,7 +848,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(44, "useTurboModuleInterop"); + markFlagAsAccessed(45, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "useTurboModules"); + markFlagAsAccessed(46, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 4121373011c..f976d08ea91 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<<1efdbd02124b25d6599e4867f08c69c9>> + * @generated SignedSource<> */ /** @@ -47,6 +47,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableFabricLogs(); bool enableFabricRenderer(); bool enableFabricRendererExclusively(); + bool enableFixForViewCommandRace(); bool enableGranularShadowTreeStateReconciliation(); bool enableIOSViewClipToPaddingBox(); bool enableLayoutAnimationsOnAndroid(); @@ -89,7 +90,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 46> accessedFeatureFlags_; + std::array, 47> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowRecursiveCommitsWithSynchronousMountOnAndroid_; @@ -106,6 +107,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableFabricLogs_; std::atomic> enableFabricRenderer_; std::atomic> enableFabricRendererExclusively_; + std::atomic> enableFixForViewCommandRace_; std::atomic> enableGranularShadowTreeStateReconciliation_; std::atomic> enableIOSViewClipToPaddingBox_; std::atomic> enableLayoutAnimationsOnAndroid_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 8efc431f2e2..c193a9a73bb 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<> + * @generated SignedSource<> */ /** @@ -87,6 +87,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableFixForViewCommandRace() override { + return false; + } + bool enableGranularShadowTreeStateReconciliation() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 39bf0acbc9e..d3b8ebd212b 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<<9038e8340a349ed8f4abee66555e98d3>> + * @generated SignedSource<<8633a91586017a22d562b1ba022a6586>> */ /** @@ -40,6 +40,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableFabricLogs() = 0; virtual bool enableFabricRenderer() = 0; virtual bool enableFabricRendererExclusively() = 0; + virtual bool enableFixForViewCommandRace() = 0; virtual bool enableGranularShadowTreeStateReconciliation() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; virtual bool enableLayoutAnimationsOnAndroid() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 655b1a85c05..d2a1c2becb2 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<<6736518a1e588cac9c28d3c0c91af280>> */ /** @@ -119,6 +119,11 @@ bool NativeReactNativeFeatureFlags::enableFabricRendererExclusively( return ReactNativeFeatureFlags::enableFabricRendererExclusively(); } +bool NativeReactNativeFeatureFlags::enableFixForViewCommandRace( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableFixForViewCommandRace(); +} + bool NativeReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 1cdeacd004b..163c39c3105 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<> + * @generated SignedSource<<5c391f7691147482153fa3cc4e3870aa>> */ /** @@ -67,6 +67,8 @@ class NativeReactNativeFeatureFlags bool enableFabricRendererExclusively(jsi::Runtime& runtime); + bool enableFixForViewCommandRace(jsi::Runtime& runtime); + bool enableGranularShadowTreeStateReconciliation(jsi::Runtime& runtime); bool enableIOSViewClipToPaddingBox(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 6569c24fae8..01c5af17603 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -316,7 +316,19 @@ void Scheduler::uiManagerDidDispatchCommand( if (delegate_ != nullptr) { auto shadowView = ShadowView(*shadowNode); - delegate_->schedulerDidDispatchCommand(shadowView, commandName, args); + if (ReactNativeFeatureFlags::enableFixForViewCommandRace()) { + runtimeScheduler_->scheduleRenderingUpdate( + shadowNode->getSurfaceId(), + [delegate = delegate_, + shadowView = std::move(shadowView), + commandName, + args]() { + delegate->schedulerDidDispatchCommand( + shadowView, commandName, args); + }); + } else { + delegate_->schedulerDidDispatchCommand(shadowView, commandName, args); + } } } diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 899674429e4..df43d14e6fc 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -171,6 +171,15 @@ const definitions: FeatureFlagDefinitions = { purpose: 'release', }, }, + enableFixForViewCommandRace: { + defaultValue: false, + metadata: { + dateAdded: '2024-11-14', + description: + 'Synchronise the view command dispatching with mounting of new transaction', + purpose: 'experimentation', + }, + }, enableGranularShadowTreeStateReconciliation: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index fda5cef1663..ed9b82d59cb 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<<625f763d82aeca846224a67d9580f1b7>> + * @generated SignedSource<<5630ca490d52be748f6de41e007d4dd4>> * @flow strict */ @@ -66,6 +66,7 @@ export type ReactNativeFeatureFlags = { enableFabricLogs: Getter, enableFabricRenderer: Getter, enableFabricRendererExclusively: Getter, + enableFixForViewCommandRace: Getter, enableGranularShadowTreeStateReconciliation: Getter, enableIOSViewClipToPaddingBox: Getter, enableLayoutAnimationsOnAndroid: Getter, @@ -248,6 +249,10 @@ export const enableFabricRenderer: Getter = createNativeFlagGetter('ena * When the app is completely migrated to Fabric, set this flag to true to disable parts of Paper infrastructure that are not needed anymore but consume memory and CPU. Specifically, UIViewOperationQueue and EventDispatcherImpl will no longer work as they will not subscribe to ReactChoreographer for updates. */ export const enableFabricRendererExclusively: Getter = createNativeFlagGetter('enableFabricRendererExclusively', false); +/** + * Synchronise the view command dispatching with mounting of new transaction + */ +export const enableFixForViewCommandRace: Getter = createNativeFlagGetter('enableFixForViewCommandRace', false); /** * When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index a34259d3966..6469094839a 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<<5fb648131bb9739c6d47de4ad59bdd6f>> * @flow strict */ @@ -39,6 +39,7 @@ export interface Spec extends TurboModule { +enableFabricLogs?: () => boolean; +enableFabricRenderer?: () => boolean; +enableFabricRendererExclusively?: () => boolean; + +enableFixForViewCommandRace?: () => boolean; +enableGranularShadowTreeStateReconciliation?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; +enableLayoutAnimationsOnAndroid?: () => boolean;