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 b54d7faf251899..b91c62ba7c72aa 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<<31638ef8ac6992a354785b74af8fbe0d>> */ /** @@ -90,6 +90,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = accessor.enableCustomFocusSearchOnClippedElementsAndroid() + /** + * Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work. + */ + @JvmStatic + public fun enableDestroyShadowTreeRevisionAsync(): Boolean = accessor.enableDestroyShadowTreeRevisionAsync() + /** * When enabled a subset of components will avoid double measurement on Android. */ 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 85f9f05f999062..2548e16d9b51fb 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<<9c1e0e7e87aead96e2554ef5ecf9e7af>> + * @generated SignedSource<<6b382661025db56592b44255f5a8694c>> */ /** @@ -30,6 +30,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableBridgelessArchitectureCache: Boolean? = null private var enableCppPropsIteratorSetterCache: Boolean? = null private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null + private var enableDestroyShadowTreeRevisionAsyncCache: Boolean? = null private var enableDoubleMeasurementFixAndroidCache: Boolean? = null private var enableEagerRootViewAttachmentCache: Boolean? = null private var enableFabricLogsCache: Boolean? = null @@ -156,6 +157,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableDestroyShadowTreeRevisionAsync(): Boolean { + var cached = enableDestroyShadowTreeRevisionAsyncCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableDestroyShadowTreeRevisionAsync() + enableDestroyShadowTreeRevisionAsyncCache = cached + } + return cached + } + override fun enableDoubleMeasurementFixAndroid(): Boolean { var cached = enableDoubleMeasurementFixAndroidCache 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 5f6f58688df6a6..e06c5328a2fec6 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<<8276fd1166cdd235f11a0b490bb7d924>> */ /** @@ -48,6 +48,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableDestroyShadowTreeRevisionAsync(): Boolean + @DoNotStrip @JvmStatic public external fun enableDoubleMeasurementFixAndroid(): Boolean @DoNotStrip @JvmStatic public external fun enableEagerRootViewAttachment(): 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 5df17cda4ddf24..17bca79efb97d9 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<> */ /** @@ -43,6 +43,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = true + override fun enableDestroyShadowTreeRevisionAsync(): Boolean = false + override fun enableDoubleMeasurementFixAndroid(): Boolean = false override fun enableEagerRootViewAttachment(): 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 9f45b7d5fb8c38..37a4cb6252d32c 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<<5b145ee103cf6b40e13d33c88cd52777>> + * @generated SignedSource<<5dc41059d71d3a345be45a6a233b05a0>> */ /** @@ -34,6 +34,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableBridgelessArchitectureCache: Boolean? = null private var enableCppPropsIteratorSetterCache: Boolean? = null private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null + private var enableDestroyShadowTreeRevisionAsyncCache: Boolean? = null private var enableDoubleMeasurementFixAndroidCache: Boolean? = null private var enableEagerRootViewAttachmentCache: Boolean? = null private var enableFabricLogsCache: Boolean? = null @@ -170,6 +171,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableDestroyShadowTreeRevisionAsync(): Boolean { + var cached = enableDestroyShadowTreeRevisionAsyncCache + if (cached == null) { + cached = currentProvider.enableDestroyShadowTreeRevisionAsync() + accessedFeatureFlags.add("enableDestroyShadowTreeRevisionAsync") + enableDestroyShadowTreeRevisionAsyncCache = cached + } + return cached + } + override fun enableDoubleMeasurementFixAndroid(): Boolean { var cached = enableDoubleMeasurementFixAndroidCache 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 b3ce0e12bc992e..59cc950c784a3c 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<<9cdb965a420d53cf36b6531684cb07ae>> + * @generated SignedSource<<35f6d9ae3c445c81fc4bab7509fd3179>> */ /** @@ -43,6 +43,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean + @DoNotStrip public fun enableDestroyShadowTreeRevisionAsync(): Boolean + @DoNotStrip public fun enableDoubleMeasurementFixAndroid(): Boolean @DoNotStrip public fun enableEagerRootViewAttachment(): 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 b575f3a26c7ca7..89fe373a814ee9 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<> */ /** @@ -99,6 +99,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableDestroyShadowTreeRevisionAsync() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableDestroyShadowTreeRevisionAsync"); + return method(javaProvider_); + } + bool enableDoubleMeasurementFixAndroid() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableDoubleMeasurementFixAndroid"); @@ -363,6 +369,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableCustomFocusSearchOnClippedElement return ReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableDestroyShadowTreeRevisionAsync( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableDoubleMeasurementFixAndroid( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid(); @@ -599,6 +610,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableCustomFocusSearchOnClippedElementsAndroid", JReactNativeFeatureFlagsCxxInterop::enableCustomFocusSearchOnClippedElementsAndroid), + makeNativeMethod( + "enableDestroyShadowTreeRevisionAsync", + JReactNativeFeatureFlagsCxxInterop::enableDestroyShadowTreeRevisionAsync), makeNativeMethod( "enableDoubleMeasurementFixAndroid", JReactNativeFeatureFlagsCxxInterop::enableDoubleMeasurementFixAndroid), 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 ab831a1328389d..efd5bb770ed729 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<<2a5ad62d2cb4f1111391f1b566e10c1d>> + * @generated SignedSource<<270b461fa199f8e6365948cded0785ad>> */ /** @@ -60,6 +60,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableCustomFocusSearchOnClippedElementsAndroid( facebook::jni::alias_ref); + static bool enableDestroyShadowTreeRevisionAsync( + facebook::jni::alias_ref); + static bool enableDoubleMeasurementFixAndroid( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index ea5b902d163925..efa23b559c84ce 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -46,12 +46,12 @@ Pod::Spec.new do |s| s.dependency "React-Core" s.dependency "React-debug" s.dependency "React-featureflags" - s.dependency "React-utils" s.dependency "React-runtimescheduler" s.dependency "React-cxxreact" add_dependency(s, "React-rendererdebug") add_dependency(s, "React-graphics", :additional_framework_paths => ["react/renderer/graphics/platform/ios"]) + add_dependency(s, "React-utils", :additional_framework_paths => ["react/utils/platform/ios"]) depend_on_js_engine(s) add_rn_third_party_dependencies(s) diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 4456694202a364..5e74d3f63bf0ea 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<<0eb63ead191be88440cbb73fd760dec7>> + * @generated SignedSource<<74f22c6a0302a9923e99d2455d84e231>> */ /** @@ -66,6 +66,10 @@ bool ReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndroid() return getAccessor().enableCustomFocusSearchOnClippedElementsAndroid(); } +bool ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync() { + return getAccessor().enableDestroyShadowTreeRevisionAsync(); +} + bool ReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid() { return getAccessor().enableDoubleMeasurementFixAndroid(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 631ac03eb06d50..3c5f69a2fa3de5 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<<80dcd75591a392144c2fd2817cc4408a>> + * @generated SignedSource<> */ /** @@ -89,6 +89,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableCustomFocusSearchOnClippedElementsAndroid(); + /** + * Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work. + */ + RN_EXPORT static bool enableDestroyShadowTreeRevisionAsync(); + /** * When enabled a subset of components will avoid double measurement on Android. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 8d1574a59f8841..9d33a09fe34e34 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<<0c39183e7858453bfc18515be46eb17f>> + * @generated SignedSource<<4efbb3f094edbcc89c6ac4d586169b9a>> */ /** @@ -209,6 +209,24 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomFocusSearchOnClippedElementsAn return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableDestroyShadowTreeRevisionAsync() { + auto flagValue = enableDestroyShadowTreeRevisionAsync_.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(10, "enableDestroyShadowTreeRevisionAsync"); + + flagValue = currentProvider_->enableDestroyShadowTreeRevisionAsync(); + enableDestroyShadowTreeRevisionAsync_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() { auto flagValue = enableDoubleMeasurementFixAndroid_.load(); @@ -218,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(10, "enableDoubleMeasurementFixAndroid"); + markFlagAsAccessed(11, "enableDoubleMeasurementFixAndroid"); flagValue = currentProvider_->enableDoubleMeasurementFixAndroid(); enableDoubleMeasurementFixAndroid_ = flagValue; @@ -236,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerRootViewAttachment() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(11, "enableEagerRootViewAttachment"); + markFlagAsAccessed(12, "enableEagerRootViewAttachment"); flagValue = currentProvider_->enableEagerRootViewAttachment(); enableEagerRootViewAttachment_ = flagValue; @@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(12, "enableFabricLogs"); + markFlagAsAccessed(13, "enableFabricLogs"); flagValue = currentProvider_->enableFabricLogs(); enableFabricLogs_ = flagValue; @@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRenderer() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(13, "enableFabricRenderer"); + markFlagAsAccessed(14, "enableFabricRenderer"); flagValue = currentProvider_->enableFabricRenderer(); enableFabricRenderer_ = flagValue; @@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFixForParentTagDuringReparenting() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(14, "enableFixForParentTagDuringReparenting"); + markFlagAsAccessed(15, "enableFixForParentTagDuringReparenting"); flagValue = currentProvider_->enableFixForParentTagDuringReparenting(); enableFixForParentTagDuringReparenting_ = flagValue; @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "enableFontScaleChangesUpdatingLayout"); + markFlagAsAccessed(16, "enableFontScaleChangesUpdatingLayout"); flagValue = currentProvider_->enableFontScaleChangesUpdatingLayout(); enableFontScaleChangesUpdatingLayout_ = 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::enableJSRuntimeGCOnMemoryPressureOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "enableJSRuntimeGCOnMemoryPressureOnIOS"); + markFlagAsAccessed(18, "enableJSRuntimeGCOnMemoryPressureOnIOS"); flagValue = currentProvider_->enableJSRuntimeGCOnMemoryPressureOnIOS(); enableJSRuntimeGCOnMemoryPressureOnIOS_ = flagValue; @@ -362,7 +380,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(18, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(19, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -380,7 +398,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(19, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(20, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueModulesOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "enableMainQueueModulesOnIOS"); + markFlagAsAccessed(21, "enableMainQueueModulesOnIOS"); flagValue = currentProvider_->enableMainQueueModulesOnIOS(); enableMainQueueModulesOnIOS_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enableNativeCSSParsing"); + markFlagAsAccessed(22, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "enableNetworkEventReporting"); + markFlagAsAccessed(23, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -452,7 +470,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(23, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(24, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -470,7 +488,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(24, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(25, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "enableResourceTimingAPI"); + markFlagAsAccessed(26, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -506,7 +524,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(26, "enableSynchronousStateUpdates"); + markFlagAsAccessed(27, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "enableViewCulling"); + markFlagAsAccessed(28, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -542,7 +560,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(28, "enableViewRecycling"); + markFlagAsAccessed(29, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "enableViewRecyclingForText"); + markFlagAsAccessed(30, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "enableViewRecyclingForView"); + markFlagAsAccessed(31, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -596,7 +614,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(31, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(32, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -614,7 +632,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(32, "fuseboxEnabledRelease"); + markFlagAsAccessed(33, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(34, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::incorporateMaxLinesDuringAndroidLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "incorporateMaxLinesDuringAndroidLayout"); + markFlagAsAccessed(35, "incorporateMaxLinesDuringAndroidLayout"); flagValue = currentProvider_->incorporateMaxLinesDuringAndroidLayout(); incorporateMaxLinesDuringAndroidLayout_ = flagValue; @@ -668,7 +686,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(35, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(36, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(37, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = 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::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(40, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -758,7 +776,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(40, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(41, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "useRawPropsJsiValue"); + markFlagAsAccessed(42, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "useShadowNodeStateOnClone"); + markFlagAsAccessed(43, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -812,7 +830,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(43, "useTurboModuleInterop"); + markFlagAsAccessed(44, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -830,7 +848,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(44, "useTurboModules"); + markFlagAsAccessed(45, "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 9c5f1db7f22ead..e2b2d91702d7e9 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<<336504fac084993c9aff86f274d45342>> + * @generated SignedSource<<31b5c2776ad91fd0d3bd2cfc3672575b>> */ /** @@ -42,6 +42,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableBridgelessArchitecture(); bool enableCppPropsIteratorSetter(); bool enableCustomFocusSearchOnClippedElementsAndroid(); + bool enableDestroyShadowTreeRevisionAsync(); bool enableDoubleMeasurementFixAndroid(); bool enableEagerRootViewAttachment(); bool enableFabricLogs(); @@ -88,7 +89,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 45> accessedFeatureFlags_; + std::array, 46> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -100,6 +101,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableBridgelessArchitecture_; std::atomic> enableCppPropsIteratorSetter_; std::atomic> enableCustomFocusSearchOnClippedElementsAndroid_; + std::atomic> enableDestroyShadowTreeRevisionAsync_; std::atomic> enableDoubleMeasurementFixAndroid_; std::atomic> enableEagerRootViewAttachment_; std::atomic> enableFabricLogs_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 5e8ea4a2ccd588..9800c4e3cc9ef8 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<<825294594363fb672b6cd64eaa954dd4>> + * @generated SignedSource<<9cc33ba9f5ef67d6c98700d772bbc0de>> */ /** @@ -67,6 +67,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return true; } + bool enableDestroyShadowTreeRevisionAsync() override { + return false; + } + bool enableDoubleMeasurementFixAndroid() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index de2b15cdeb3737..ed1f9a9319a3da 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -135,6 +135,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableCustomFocusSearchOnClippedElementsAndroid(); } + bool enableDestroyShadowTreeRevisionAsync() override { + auto value = values_["enableDestroyShadowTreeRevisionAsync"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableDestroyShadowTreeRevisionAsync(); + } + bool enableDoubleMeasurementFixAndroid() override { auto value = values_["enableDoubleMeasurementFixAndroid"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 641e52aa31b850..8c46a35b5052f9 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<<905f26c85dc731205b1a6f44c4e32468>> + * @generated SignedSource<> */ /** @@ -35,6 +35,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableBridgelessArchitecture() = 0; virtual bool enableCppPropsIteratorSetter() = 0; virtual bool enableCustomFocusSearchOnClippedElementsAndroid() = 0; + virtual bool enableDestroyShadowTreeRevisionAsync() = 0; virtual bool enableDoubleMeasurementFixAndroid() = 0; virtual bool enableEagerRootViewAttachment() = 0; virtual bool enableFabricLogs() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index f3a1d555024061..eb2d9d3a3c2740 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<<614814062283858cf70138a2ac3d6304>> + * @generated SignedSource<<82e2e7118453787377248101925b56d1>> */ /** @@ -94,6 +94,11 @@ bool NativeReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndr return ReactNativeFeatureFlags::enableCustomFocusSearchOnClippedElementsAndroid(); } +bool NativeReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync(); +} + bool NativeReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableDoubleMeasurementFixAndroid(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index c871564ef62e8b..f6f7f8a79bb14d 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<<1c0248c3eda43cc89a61b3e3ff8b4345>> + * @generated SignedSource<> */ /** @@ -57,6 +57,8 @@ class NativeReactNativeFeatureFlags bool enableCustomFocusSearchOnClippedElementsAndroid(jsi::Runtime& runtime); + bool enableDestroyShadowTreeRevisionAsync(jsi::Runtime& runtime); + bool enableDoubleMeasurementFixAndroid(jsi::Runtime& runtime); bool enableEagerRootViewAttachment(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index 70e64526ee1709..2f6627e1edec4f 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -9,7 +9,9 @@ #include #include +#include #include +#include #include #include "updateMountedFlag.h" @@ -186,6 +188,9 @@ std::optional MountingCoordinator::pullTransaction( #endif if (lastRevision_.has_value()) { + if (ReactNativeFeatureFlags::enableDestroyShadowTreeRevisionAsync()) { + LowPriorityExecutor::execute([toDelete = std::move(baseRevision_)]() {}); + } baseRevision_ = std::move(*lastRevision_); lastRevision_.reset(); diff --git a/packages/react-native/ReactCommon/react/utils/CMakeLists.txt b/packages/react-native/ReactCommon/react/utils/CMakeLists.txt index 532ab1cba8634f..89410f2dafa0c6 100644 --- a/packages/react-native/ReactCommon/react/utils/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/utils/CMakeLists.txt @@ -8,10 +8,14 @@ set(CMAKE_VERBOSE_MAKEFILE on) include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake) -file(GLOB react_utils_SRC CONFIGURE_DEPENDS *.cpp *.mm) +file(GLOB react_utils_SRC CONFIGURE_DEPENDS *.cpp platform/android/react/utils/*.cpp) add_library(react_utils OBJECT ${react_utils_SRC}) -target_include_directories(react_utils PUBLIC ${REACT_COMMON_DIR}) +target_include_directories(react_utils + PUBLIC + ${REACT_COMMON_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/platform/android/ +) target_link_libraries(react_utils glog diff --git a/packages/react-native/ReactCommon/react/utils/React-utils.podspec b/packages/react-native/ReactCommon/react/utils/React-utils.podspec index f799291920ed2c..0648b495107e7f 100644 --- a/packages/react-native/ReactCommon/react/utils/React-utils.podspec +++ b/packages/react-native/ReactCommon/react/utils/React-utils.podspec @@ -16,12 +16,12 @@ else source[:tag] = "v#{version}" end -header_search_paths = [ - "\"$(PODS_TARGET_SRCROOT)\"", - "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"", -] - Pod::Spec.new do |s| + source_files = "*.{m,mm,cpp,h}", "platform/ios/**/*.{m,mm,cpp,h}" + header_search_paths = [ + "\"$(PODS_TARGET_SRCROOT)/../../\"", + ] + s.name = "React-utils" s.version = version s.summary = "-" # TODO @@ -30,18 +30,21 @@ Pod::Spec.new do |s| s.author = "Meta Platforms, Inc. and its affiliates" s.platforms = min_supported_versions s.source = source - s.source_files = "**/*.{cpp,h,mm}" + s.source_files = source_files s.header_dir = "react/utils" s.exclude_files = "tests" - s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), - "HEADER_SEARCH_PATHS" => header_search_paths.join(' '), - "DEFINES_MODULE" => "YES" } if ENV['USE_FRAMEWORKS'] s.module_name = "React_utils" s.header_mappings_dir = "../.." + header_search_paths = header_search_paths + ["\"$(PODS_TARGET_SRCROOT)/platform/ios\""] end + s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", + "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + "HEADER_SEARCH_PATHS" => header_search_paths.join(' '), + "DEFINES_MODULE" => "YES" } + s.dependency "React-jsi", version depend_on_js_engine(s) diff --git a/packages/react-native/ReactCommon/react/utils/platform/android/react/utils/LowPriorityExecutor.cpp b/packages/react-native/ReactCommon/react/utils/platform/android/react/utils/LowPriorityExecutor.cpp new file mode 100644 index 00000000000000..d79eb426211990 --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/platform/android/react/utils/LowPriorityExecutor.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::react::LowPriorityExecutor { + +struct LowPriorityExecutorThread { + LowPriorityExecutorThread() : thread_{std::thread([this] { run(); })} { + pthread_t hThread = thread_.native_handle(); + struct sched_param param {}; + param.sched_priority = 19; // Higher value means lower priority + pthread_setschedparam(hThread, SCHED_OTHER, ¶m); + } + + // Deleted constructors + LowPriorityExecutorThread(const LowPriorityExecutorThread& other) = delete; + LowPriorityExecutorThread(LowPriorityExecutorThread&& other) = delete; + LowPriorityExecutorThread& operator=(const LowPriorityExecutorThread& other) = + delete; + LowPriorityExecutorThread& operator=(LowPriorityExecutorThread&& other) = + delete; + + ~LowPriorityExecutorThread() { + // Stop the thread + { + std::lock_guard lock(mutex_); + running_ = false; + } + + // Unblock the thread to check the running_ flag and terminate. + cv_.notify_one(); + + // Wait for thread completion to avoid use-after-free on background thread. + thread_.join(); + } + + void post(std::function&& workItem) { + // Move the object to the queue. + { + std::lock_guard lock(mutex_); + queue_.emplace(std::move(workItem)); + } + + // Notify the background thread. + cv_.notify_one(); + } + + private: + void run() { + pthread_setname_np(pthread_self(), "LowPriorityExecutorThread"); + + while (true) { + std::unique_lock lock(mutex_); + + // Wait until an object is in the queue or the thread is stopped. + cv_.wait(lock, [this] { return !queue_.empty() || !running_; }); + + // Empty the queue. + while (!queue_.empty()) { + queue_.front()(); + queue_.pop(); + } + + // Check if the thread is stopping. + if (!running_) { + break; + } + } + } + + std::string threadName_; + std::thread thread_; + std::queue> queue_; + std::mutex mutex_; + std::condition_variable cv_; + bool running_{true}; +}; + +void execute(std::function&& workItem) { + static LowPriorityExecutorThread thread{}; + thread.post(std::move(workItem)); +} + +} // namespace facebook::react::LowPriorityExecutor diff --git a/packages/react-native/ReactCommon/react/utils/platform/android/react/utils/LowPriorityExecutor.h b/packages/react-native/ReactCommon/react/utils/platform/android/react/utils/LowPriorityExecutor.h new file mode 100644 index 00000000000000..c09398d28e33cf --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/platform/android/react/utils/LowPriorityExecutor.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook::react::LowPriorityExecutor { + +void execute(std::function&& workItem); + +} // namespace facebook::react::LowPriorityExecutor diff --git a/packages/react-native/ReactCommon/react/utils/platform/cxx/react/utils/LowPriorityExecutor.h b/packages/react-native/ReactCommon/react/utils/platform/cxx/react/utils/LowPriorityExecutor.h new file mode 100644 index 00000000000000..eca44655e0482d --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/platform/cxx/react/utils/LowPriorityExecutor.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react::LowPriorityExecutor { + +inline void execute(std::function&& workItem) { + workItem(); +} + +} // namespace facebook::react::LowPriorityExecutor diff --git a/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/LowPriorityExecutor.h b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/LowPriorityExecutor.h new file mode 100644 index 00000000000000..49c69ee48d5ec6 --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/LowPriorityExecutor.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react::LowPriorityExecutor { + +void execute(std::function&& workItem); + +} // namespace facebook::react::LowPriorityExecutor diff --git a/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/LowPriorityExecutor.mm b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/LowPriorityExecutor.mm new file mode 100644 index 00000000000000..414a06dab3f898 --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/LowPriorityExecutor.mm @@ -0,0 +1,22 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "LowPriorityExecutor.h" + +#import + +namespace facebook::react::LowPriorityExecutor { + +void execute(std::function &&workItem) +{ + std::function localWorkItem = std::move(workItem); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + localWorkItem(); + }); +} + +} // namespace facebook::react::LowPriorityExecutor diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index f066ecb5d75778..35de559773c19e 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -155,6 +155,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableDestroyShadowTreeRevisionAsync: { + defaultValue: false, + metadata: { + dateAdded: '2025-04-29', + description: + 'Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableDoubleMeasurementFixAndroid: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index c850347fb0555b..27cd2279f8a308 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<> * @flow strict */ @@ -56,6 +56,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableBridgelessArchitecture: Getter, enableCppPropsIteratorSetter: Getter, enableCustomFocusSearchOnClippedElementsAndroid: Getter, + enableDestroyShadowTreeRevisionAsync: Getter, enableDoubleMeasurementFixAndroid: Getter, enableEagerRootViewAttachment: Getter, enableFabricLogs: Getter, @@ -197,6 +198,10 @@ export const enableCppPropsIteratorSetter: Getter = createNativeFlagGet * This enables the fabric implementation of focus search so that we can focus clipped elements */ export const enableCustomFocusSearchOnClippedElementsAndroid: Getter = createNativeFlagGetter('enableCustomFocusSearchOnClippedElementsAndroid', true); +/** + * Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work. + */ +export const enableDestroyShadowTreeRevisionAsync: Getter = createNativeFlagGetter('enableDestroyShadowTreeRevisionAsync', false); /** * When enabled a subset of components will avoid double measurement on Android. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 223a2924345932..f28e768250402c 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<<715d2a137ded85479120f5090c05b3b6>> + * @generated SignedSource<<995633c3b12e26369518ff579f006f68>> * @flow strict */ @@ -34,6 +34,7 @@ export interface Spec extends TurboModule { +enableBridgelessArchitecture?: () => boolean; +enableCppPropsIteratorSetter?: () => boolean; +enableCustomFocusSearchOnClippedElementsAndroid?: () => boolean; + +enableDestroyShadowTreeRevisionAsync?: () => boolean; +enableDoubleMeasurementFixAndroid?: () => boolean; +enableEagerRootViewAttachment?: () => boolean; +enableFabricLogs?: () => boolean;