From 818e70f7ebfd7f1960fd6b930cf9513e737df199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 2 Jul 2024 07:58:37 -0700 Subject: [PATCH] Set up experiment to fix race conditions when doing Fabric state updates on Android (#45236) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45236 Changelog: [internal] ## Context We're currently observing inconsistencies between the state of the UI on Android and the propagated state in Fabric. When investigating the issue, we saw that there are some state updates that were going to be dispatched from scroll views to Fabric were skipped because the state object in native was deallocated. The reason for that is a race condition between: 1. Dispatching new state updates from the UI thread 2. Updating the state previously dispatched from the UI thread on the JS thread. {F1735383134} ## Changes This creates a new feature flag to replace the weak reference with a strong one, so when the previous state is deallocated we can still access it to set new state. The use of weak references was introduced in D44472121 to avoid holding onto JSI references (which could be contained in the state) when the runtime was deallocated, but we later introduced an explicit clean up mechanism in D45905628 that would make that unnecessary. Reviewed By: javache Differential Revision: D59233070 fbshipit-source-id: 018d8935f506430ecab96df0f7a998a37ee0f556 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 ++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../react/fabric/FabricMountingManager.cpp | 6 +- .../jni/react/fabric/StateWrapperImpl.cpp | 62 +++++++++++++++---- .../main/jni/react/fabric/StateWrapperImpl.h | 5 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 ++- .../ReactNativeFeatureFlagsAccessor.cpp | 46 +++++++++----- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 ++- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 5 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 ++- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 22 files changed, 189 insertions(+), 50 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 ccddca4abb1..9650cafef4b 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<<9a33a6bc10cdb2b0f9fcb15805a06982>> + * @generated SignedSource<> */ /** @@ -100,6 +100,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact() + /** + * Enables a fix to prevent the possibility of state updates in Fabric being missed due to race conditions with previous state updates. + */ + @JvmStatic + public fun fixMissedFabricStateUpdatesOnAndroid(): Boolean = accessor.fixMissedFabricStateUpdatesOnAndroid() + /** * Fixes a leak in SurfaceMountingManager.mRemoveDeleteTreeUIFrameCallback */ 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 884795d8109..444f6937c4d 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<<5d67280406c16b01ba71b7b75e814a79>> + * @generated SignedSource<<92ebcb510939039fc157e0f85b08073e>> */ /** @@ -32,6 +32,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var enableSynchronousStateUpdatesCache: Boolean? = null private var enableUIConsistencyCache: Boolean? = null private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null + private var fixMissedFabricStateUpdatesOnAndroidCache: Boolean? = null private var fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeakCache: Boolean? = null private var forceBatchingMountItemsOnAndroidCache: Boolean? = null private var fuseboxEnabledDebugCache: Boolean? = null @@ -154,6 +155,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun fixMissedFabricStateUpdatesOnAndroid(): Boolean { + var cached = fixMissedFabricStateUpdatesOnAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.fixMissedFabricStateUpdatesOnAndroid() + fixMissedFabricStateUpdatesOnAndroidCache = cached + } + return cached + } + override fun fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(): Boolean { var cached = fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeakCache 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 9abef85cb31..8d2e3d244e0 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<<154baea748cdf7b8e05a1e4448053673>> + * @generated SignedSource<<2434a09bc40a30291c642a26453f939f>> */ /** @@ -52,6 +52,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean + @DoNotStrip @JvmStatic public external fun fixMissedFabricStateUpdatesOnAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(): Boolean @DoNotStrip @JvmStatic public external fun forceBatchingMountItemsOnAndroid(): 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 04d17b77e8c..935248b2e16 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<<34bbd584a612fa88cc6adf2d2bc51b92>> + * @generated SignedSource<> */ /** @@ -47,6 +47,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false + override fun fixMissedFabricStateUpdatesOnAndroid(): Boolean = false + override fun fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(): Boolean = false override fun forceBatchingMountItemsOnAndroid(): 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 9d3d163dc48..8d20e04f5cc 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<> + * @generated SignedSource<<65a88218f057b56211d84b6d473e8a90>> */ /** @@ -36,6 +36,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var enableSynchronousStateUpdatesCache: Boolean? = null private var enableUIConsistencyCache: Boolean? = null private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null + private var fixMissedFabricStateUpdatesOnAndroidCache: Boolean? = null private var fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeakCache: Boolean? = null private var forceBatchingMountItemsOnAndroidCache: Boolean? = null private var fuseboxEnabledDebugCache: Boolean? = null @@ -170,6 +171,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun fixMissedFabricStateUpdatesOnAndroid(): Boolean { + var cached = fixMissedFabricStateUpdatesOnAndroidCache + if (cached == null) { + cached = currentProvider.fixMissedFabricStateUpdatesOnAndroid() + accessedFeatureFlags.add("fixMissedFabricStateUpdatesOnAndroid") + fixMissedFabricStateUpdatesOnAndroidCache = cached + } + return cached + } + override fun fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(): Boolean { var cached = fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeakCache 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 eb3e21e1278..9809e6de524 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<3a0f547e2b68072a76b1eabc28c371cb>> */ /** @@ -47,6 +47,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean + @DoNotStrip public fun fixMissedFabricStateUpdatesOnAndroid(): Boolean + @DoNotStrip public fun fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(): Boolean @DoNotStrip public fun forceBatchingMountItemsOnAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp index ab08744f65e..13124ea84be 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp @@ -540,7 +540,7 @@ void FabricMountingManager::executeMount( if (mountItem.newChildShadowView.state != nullptr) { javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); StateWrapperImpl* cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = mountItem.newChildShadowView.state; + cStateWrapper->setState(mountItem.newChildShadowView.state); } // Do not hold a reference to javaEventEmitter from the C++ side. @@ -615,7 +615,7 @@ void FabricMountingManager::executeMount( if (state != nullptr) { javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); StateWrapperImpl* cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = state; + cStateWrapper->setState(state); } (*objBufferArray)[objBufferPosition++] = @@ -823,7 +823,7 @@ void FabricMountingManager::preallocateShadowView( if (shadowView.state != nullptr) { javaStateWrapper = StateWrapperImpl::newObjectJavaArgs(); StateWrapperImpl* cStateWrapper = cthis(javaStateWrapper); - cStateWrapper->state_ = shadowView.state; + cStateWrapper->setState(shadowView.state); } // Do not hold a reference to javaEventEmitter from the C++ side. diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.cpp index bf5c18e5dd9..6ea03ff22e8 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.cpp @@ -7,6 +7,7 @@ #include "StateWrapperImpl.h" #include +#include #include #include #include @@ -25,30 +26,65 @@ jni::local_ref StateWrapperImpl::initHybrid( jni::local_ref StateWrapperImpl::getStateDataImpl() { - if (auto state = state_.lock()) { - folly::dynamic map = state->getDynamic(); - return ReadableNativeMap::newObjectCxxArgs(std::move(map)); + if (ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid()) { + if (state_) { + folly::dynamic map = state_->getDynamic(); + return ReadableNativeMap::newObjectCxxArgs(std::move(map)); + } else { + return nullptr; + } } else { - return nullptr; + if (auto state = weakState_.lock()) { + folly::dynamic map = state->getDynamic(); + return ReadableNativeMap::newObjectCxxArgs(std::move(map)); + } else { + return nullptr; + } } } jni::local_ref StateWrapperImpl::getStateMapBufferDataImpl() { - if (auto state = state_.lock()) { - MapBuffer map = state->getMapBuffer(); - return JReadableMapBuffer::createWithContents(std::move(map)); + if (ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid()) { + if (state_) { + MapBuffer map = state_->getMapBuffer(); + return JReadableMapBuffer::createWithContents(std::move(map)); + } else { + return nullptr; + } } else { - return nullptr; + if (auto state = weakState_.lock()) { + MapBuffer map = state->getMapBuffer(); + return JReadableMapBuffer::createWithContents(std::move(map)); + } else { + return nullptr; + } } } void StateWrapperImpl::updateStateImpl(NativeMap* map) { - if (auto state = state_.lock()) { - // Get folly::dynamic from map - auto dynamicMap = map->consume(); - // Set state - state->updateState(std::move(dynamicMap)); + if (ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid()) { + if (state_) { + // Get folly::dynamic from map + auto dynamicMap = map->consume(); + // Set state + state_->updateState(std::move(dynamicMap)); + } + } else { + if (auto state = weakState_.lock()) { + // Get folly::dynamic from map + auto dynamicMap = map->consume(); + // Set state + state->updateState(std::move(dynamicMap)); + } + } +} + +void StateWrapperImpl::setState(std::shared_ptr state) { + if (ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid()) { + state_ = state; + } else { + weakState_ = state; } } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.h index 3aac2e0a7e9..542f78c69ae 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/StateWrapperImpl.h @@ -28,11 +28,12 @@ class StateWrapperImpl : public jni::HybridClass { jni::local_ref getStateMapBufferDataImpl(); jni::local_ref getStateDataImpl(); void updateStateImpl(NativeMap* map); - - std::weak_ptr state_; + void setState(std::shared_ptr state); private: jni::alias_ref jhybridobject_; + std::weak_ptr weakState_; + std::shared_ptr state_; static jni::local_ref initHybrid(jni::alias_ref); }; 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 d5ad1a7f1c8..864feb38252 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<<88f5b83b8a3d7902eaab333246b59ed3>> + * @generated SignedSource<> */ /** @@ -111,6 +111,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool fixMissedFabricStateUpdatesOnAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fixMissedFabricStateUpdatesOnAndroid"); + return method(javaProvider_); + } + bool fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak"); @@ -253,6 +259,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabri return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(); } +bool JReactNativeFeatureFlagsCxxInterop::fixMissedFabricStateUpdatesOnAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(); @@ -371,6 +382,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "fixMappingOfEventPrioritiesBetweenFabricAndReact", JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact), + makeNativeMethod( + "fixMissedFabricStateUpdatesOnAndroid", + JReactNativeFeatureFlagsCxxInterop::fixMissedFabricStateUpdatesOnAndroid), makeNativeMethod( "fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak", JReactNativeFeatureFlagsCxxInterop::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak), 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 f9e1381671a..c56d27ac581 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<<66b47850d8e211f78b3e3dd40b6cc37e>> + * @generated SignedSource<<7e9c48995f6a5f4e0a56c0f2ec784def>> */ /** @@ -66,6 +66,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool fixMappingOfEventPrioritiesBetweenFabricAndReact( facebook::jni::alias_ref); + static bool fixMissedFabricStateUpdatesOnAndroid( + facebook::jni::alias_ref); + static bool fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak( 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 ce3f391dffb..0e5aa5d0d3c 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<<91a56189178d461874bfff11b14ca318>> */ /** @@ -69,6 +69,10 @@ bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact() return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact(); } +bool ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid() { + return getAccessor().fixMissedFabricStateUpdatesOnAndroid(); +} + bool ReactNativeFeatureFlags::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak() { return getAccessor().fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 014c5142092..6d285f927f7 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<> + * @generated SignedSource<<6683bcf499d88e4449f570f38b7d8b2f>> */ /** @@ -97,6 +97,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(); + /** + * Enables a fix to prevent the possibility of state updates in Fabric being missed due to race conditions with previous state updates. + */ + RN_EXPORT static bool fixMissedFabricStateUpdatesOnAndroid(); + /** * Fixes a leak in SurfaceMountingManager.mRemoveDeleteTreeUIFrameCallback */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 179d23099e1..88938c188ba 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<<44087cc6e946a05884c8761987878183>> + * @generated SignedSource<> */ /** @@ -245,6 +245,24 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::fixMissedFabricStateUpdatesOnAndroid() { + auto flagValue = fixMissedFabricStateUpdatesOnAndroid_.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(12, "fixMissedFabricStateUpdatesOnAndroid"); + + flagValue = currentProvider_->fixMissedFabricStateUpdatesOnAndroid(); + fixMissedFabricStateUpdatesOnAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak() { auto flagValue = fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak_.load(); @@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::fixStoppedSurfaceRemoveDeleteTreeUIFrameCa // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(12, "fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak"); + markFlagAsAccessed(13, "fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak"); flagValue = currentProvider_->fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(); fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak_ = flagValue; @@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::forceBatchingMountItemsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(13, "forceBatchingMountItemsOnAndroid"); + markFlagAsAccessed(14, "forceBatchingMountItemsOnAndroid"); flagValue = currentProvider_->forceBatchingMountItemsOnAndroid(); forceBatchingMountItemsOnAndroid_ = flagValue; @@ -290,7 +308,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(14, "fuseboxEnabledDebug"); + markFlagAsAccessed(15, "fuseboxEnabledDebug"); flagValue = currentProvider_->fuseboxEnabledDebug(); fuseboxEnabledDebug_ = flagValue; @@ -308,7 +326,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(15, "fuseboxEnabledRelease"); + markFlagAsAccessed(16, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -326,7 +344,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(16, "lazyAnimationCallbacks"); + markFlagAsAccessed(17, "lazyAnimationCallbacks"); flagValue = currentProvider_->lazyAnimationCallbacks(); lazyAnimationCallbacks_ = flagValue; @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::preventDoubleTextMeasure() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "preventDoubleTextMeasure"); + markFlagAsAccessed(18, "preventDoubleTextMeasure"); flagValue = currentProvider_->preventDoubleTextMeasure(); preventDoubleTextMeasure_ = flagValue; @@ -362,7 +380,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(18, "setAndroidLayoutDirection"); + markFlagAsAccessed(19, "setAndroidLayoutDirection"); flagValue = currentProvider_->setAndroidLayoutDirection(); setAndroidLayoutDirection_ = flagValue; @@ -380,7 +398,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(19, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(20, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "useModernRuntimeScheduler"); + markFlagAsAccessed(21, "useModernRuntimeScheduler"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -416,7 +434,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(21, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(22, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -434,7 +452,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(22, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(23, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "useRuntimeShadowNodeReferenceUpdateOnLayout"); + markFlagAsAccessed(24, "useRuntimeShadowNodeReferenceUpdateOnLayout"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdateOnLayout(); useRuntimeShadowNodeReferenceUpdateOnLayout_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "useStateAlignmentMechanism"); + markFlagAsAccessed(25, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 092b983a4ce..15292516808 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<<45258896e458cce165b403e043356eb9>> + * @generated SignedSource<> */ /** @@ -43,6 +43,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableSynchronousStateUpdates(); bool enableUIConsistency(); bool fixMappingOfEventPrioritiesBetweenFabricAndReact(); + bool fixMissedFabricStateUpdatesOnAndroid(); bool fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(); bool forceBatchingMountItemsOnAndroid(); bool fuseboxEnabledDebug(); @@ -66,7 +67,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 25> accessedFeatureFlags_; + std::array, 26> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowCollapsableChildren_; @@ -80,6 +81,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableSynchronousStateUpdates_; std::atomic> enableUIConsistency_; std::atomic> fixMappingOfEventPrioritiesBetweenFabricAndReact_; + std::atomic> fixMissedFabricStateUpdatesOnAndroid_; std::atomic> fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak_; std::atomic> forceBatchingMountItemsOnAndroid_; std::atomic> fuseboxEnabledDebug_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 8af86e0d738..71f85258530 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<<0f6ca616cc516096ec250d61be440952>> + * @generated SignedSource<> */ /** @@ -75,6 +75,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool fixMissedFabricStateUpdatesOnAndroid() override { + return false; + } + bool fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 87d9985fa20..c1d0b662c97 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<<3da295d89796e905588eb863a51c2054>> + * @generated SignedSource<<57b9e4f683df27a7a5770633e88f8b22>> */ /** @@ -37,6 +37,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableSynchronousStateUpdates() = 0; virtual bool enableUIConsistency() = 0; virtual bool fixMappingOfEventPrioritiesBetweenFabricAndReact() = 0; + virtual bool fixMissedFabricStateUpdatesOnAndroid() = 0; virtual bool fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak() = 0; virtual bool forceBatchingMountItemsOnAndroid() = 0; virtual bool fuseboxEnabledDebug() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 6951bd60316..7dab29de35d 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<<82399968da4f450b87c908031466a38a>> + * @generated SignedSource<<58b0c0c49dc6cf2ecdbc2c3038395e4e>> */ /** @@ -97,6 +97,11 @@ bool NativeReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndR return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(); } +bool NativeReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::fixMissedFabricStateUpdatesOnAndroid(); +} + bool NativeReactNativeFeatureFlags::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 2266b229203..01e02bc2063 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<<6b8504e42d72611f4d4b6606d69c42aa>> + * @generated SignedSource<> */ /** @@ -59,6 +59,8 @@ class NativeReactNativeFeatureFlags bool fixMappingOfEventPrioritiesBetweenFabricAndReact(jsi::Runtime& runtime); + bool fixMissedFabricStateUpdatesOnAndroid(jsi::Runtime& runtime); + bool fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak(jsi::Runtime& runtime); bool forceBatchingMountItemsOnAndroid(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 79884fb2b25..8ae591d78f3 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -93,6 +93,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.', }, + fixMissedFabricStateUpdatesOnAndroid: { + defaultValue: false, + description: + 'Enables a fix to prevent the possibility of state updates in Fabric being missed due to race conditions with previous state updates.', + }, fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak: { defaultValue: false, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 56cb85ac155..0e252406c64 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<<522f11a571457cb932f451cf81bd384a>> + * @generated SignedSource<<95a692d91055d05301a06fd33636d3fa>> * @flow strict-local */ @@ -53,6 +53,7 @@ export type ReactNativeFeatureFlags = { enableSynchronousStateUpdates: Getter, enableUIConsistency: Getter, fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter, + fixMissedFabricStateUpdatesOnAndroid: Getter, fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak: Getter, forceBatchingMountItemsOnAndroid: Getter, fuseboxEnabledDebug: Getter, @@ -161,6 +162,10 @@ export const enableUIConsistency: Getter = createNativeFlagGetter('enab * Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React. */ export const fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter = createNativeFlagGetter('fixMappingOfEventPrioritiesBetweenFabricAndReact', false); +/** + * Enables a fix to prevent the possibility of state updates in Fabric being missed due to race conditions with previous state updates. + */ +export const fixMissedFabricStateUpdatesOnAndroid: Getter = createNativeFlagGetter('fixMissedFabricStateUpdatesOnAndroid', false); /** * Fixes a leak in SurfaceMountingManager.mRemoveDeleteTreeUIFrameCallback */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 475f8fd79cf..c826385896b 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<<6715ba4954b31464c591597c53a2a0de>> + * @generated SignedSource<> * @flow strict-local */ @@ -35,6 +35,7 @@ export interface Spec extends TurboModule { +enableSynchronousStateUpdates?: () => boolean; +enableUIConsistency?: () => boolean; +fixMappingOfEventPrioritiesBetweenFabricAndReact?: () => boolean; + +fixMissedFabricStateUpdatesOnAndroid?: () => boolean; +fixStoppedSurfaceRemoveDeleteTreeUIFrameCallbackLeak?: () => boolean; +forceBatchingMountItemsOnAndroid?: () => boolean; +fuseboxEnabledDebug?: () => boolean;