mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement solution for ShadowTree commmit exhaustion using recursive locks (behind a flag) (#52795)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52795 Changelog: [internal] This is another attempt to fix https://github.com/facebook/react-native/issues/51870, inspired by https://github.com/facebook/react-native/pull/52314 but gated behind a feature flag until we've tested it carefully. Reviewed By: sammy-SC Differential Revision: D78817100 fbshipit-source-id: 45e6cae019b212528f2b2e74b9f52fe43d07f537
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c6c7c3720f
commit
2de4984970
+7
-1
@@ -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<<a5908495c093849007082e6297647f8a>>
|
||||
* @generated SignedSource<<92170b32c13dc405a81b4a5cf5677afb>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -312,6 +312,12 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun preparedTextCacheSize(): Double = accessor.preparedTextCacheSize()
|
||||
|
||||
/**
|
||||
* Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun preventShadowTreeCommitExhaustion(): Boolean = accessor.preventShadowTreeCommitExhaustion()
|
||||
|
||||
/**
|
||||
* Releases the cached image data when it is consumed by the observers.
|
||||
*/
|
||||
|
||||
+11
-1
@@ -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<<d4f92fba9ba8b4d457587446ce069d32>>
|
||||
* @generated SignedSource<<34c0a7def872e270cd542785d9bfde40>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -67,6 +67,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
|
||||
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
|
||||
private var preparedTextCacheSizeCache: Double? = null
|
||||
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
|
||||
private var releaseImageDataWhenConsumedCache: Boolean? = null
|
||||
private var shouldPressibilityUseW3CPointerEventsForHoverCache: Boolean? = null
|
||||
private var skipActivityIdentityAssertionOnHostPauseCache: Boolean? = null
|
||||
@@ -507,6 +508,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun preventShadowTreeCommitExhaustion(): Boolean {
|
||||
var cached = preventShadowTreeCommitExhaustionCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.preventShadowTreeCommitExhaustion()
|
||||
preventShadowTreeCommitExhaustionCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun releaseImageDataWhenConsumed(): Boolean {
|
||||
var cached = releaseImageDataWhenConsumedCache
|
||||
if (cached == null) {
|
||||
|
||||
+3
-1
@@ -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<<568d3a6628dd6cc9b32a77c590210730>>
|
||||
* @generated SignedSource<<3c9a5f37b4d141ffcf9a2ec152f70563>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -122,6 +122,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun preparedTextCacheSize(): Double
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun preventShadowTreeCommitExhaustion(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun releaseImageDataWhenConsumed(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun shouldPressibilityUseW3CPointerEventsForHover(): Boolean
|
||||
|
||||
+3
-1
@@ -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<<276706dd4eef0370e61fc36e3420f0aa>>
|
||||
* @generated SignedSource<<728ae563ec31340ccc8c4c593d566c64>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -117,6 +117,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun preparedTextCacheSize(): Double = 200.0
|
||||
|
||||
override fun preventShadowTreeCommitExhaustion(): Boolean = false
|
||||
|
||||
override fun releaseImageDataWhenConsumed(): Boolean = false
|
||||
|
||||
override fun shouldPressibilityUseW3CPointerEventsForHover(): Boolean = false
|
||||
|
||||
+12
-1
@@ -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<<c387f3ce74df2fc0120f5abd0dcb2f9b>>
|
||||
* @generated SignedSource<<9a5b0bdc0bd9ab0889e548b25cfe5371>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -71,6 +71,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
|
||||
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
|
||||
private var preparedTextCacheSizeCache: Double? = null
|
||||
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
|
||||
private var releaseImageDataWhenConsumedCache: Boolean? = null
|
||||
private var shouldPressibilityUseW3CPointerEventsForHoverCache: Boolean? = null
|
||||
private var skipActivityIdentityAssertionOnHostPauseCache: Boolean? = null
|
||||
@@ -558,6 +559,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun preventShadowTreeCommitExhaustion(): Boolean {
|
||||
var cached = preventShadowTreeCommitExhaustionCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.preventShadowTreeCommitExhaustion()
|
||||
accessedFeatureFlags.add("preventShadowTreeCommitExhaustion")
|
||||
preventShadowTreeCommitExhaustionCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun releaseImageDataWhenConsumed(): Boolean {
|
||||
var cached = releaseImageDataWhenConsumedCache
|
||||
if (cached == null) {
|
||||
|
||||
+3
-1
@@ -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<<636ec2163060c2ef6fdbc5e27ef41716>>
|
||||
* @generated SignedSource<<0bafb0a2fb79c4220d21f1736894af14>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -23,6 +23,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
|
||||
// We could use JNI to get the defaults from C++,
|
||||
// but that is more expensive than just duplicating the defaults here.
|
||||
|
||||
override fun preventShadowTreeCommitExhaustion(): Boolean = true
|
||||
|
||||
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = true
|
||||
|
||||
override fun useNativeTransformHelperAndroid(): Boolean = true
|
||||
|
||||
+3
-1
@@ -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<<68ec2f18b3554bcd6e3e10c530d8cca0>>
|
||||
* @generated SignedSource<<ee4525af457a5ad08f506cf8447a4156>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -117,6 +117,8 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun preparedTextCacheSize(): Double
|
||||
|
||||
@DoNotStrip public fun preventShadowTreeCommitExhaustion(): Boolean
|
||||
|
||||
@DoNotStrip public fun releaseImageDataWhenConsumed(): Boolean
|
||||
|
||||
@DoNotStrip public fun shouldPressibilityUseW3CPointerEventsForHover(): Boolean
|
||||
|
||||
+15
-1
@@ -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<<c00ad0b31911831b23dd4c9ebab1c494>>
|
||||
* @generated SignedSource<<fa975c22ecec781f4621cad8b90ea290>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -321,6 +321,12 @@ class ReactNativeFeatureFlagsJavaProvider
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool preventShadowTreeCommitExhaustion() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("preventShadowTreeCommitExhaustion");
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool releaseImageDataWhenConsumed() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("releaseImageDataWhenConsumed");
|
||||
@@ -656,6 +662,11 @@ double JReactNativeFeatureFlagsCxxInterop::preparedTextCacheSize(
|
||||
return ReactNativeFeatureFlags::preparedTextCacheSize();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::preventShadowTreeCommitExhaustion(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::releaseImageDataWhenConsumed(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::releaseImageDataWhenConsumed();
|
||||
@@ -908,6 +919,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
|
||||
makeNativeMethod(
|
||||
"preparedTextCacheSize",
|
||||
JReactNativeFeatureFlagsCxxInterop::preparedTextCacheSize),
|
||||
makeNativeMethod(
|
||||
"preventShadowTreeCommitExhaustion",
|
||||
JReactNativeFeatureFlagsCxxInterop::preventShadowTreeCommitExhaustion),
|
||||
makeNativeMethod(
|
||||
"releaseImageDataWhenConsumed",
|
||||
JReactNativeFeatureFlagsCxxInterop::releaseImageDataWhenConsumed),
|
||||
|
||||
+4
-1
@@ -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<<9ad2d870c21a193730e937d566aae5dc>>
|
||||
* @generated SignedSource<<3e83546899ed1194fe03f143865ba97a>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -171,6 +171,9 @@ class JReactNativeFeatureFlagsCxxInterop
|
||||
static double preparedTextCacheSize(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool preventShadowTreeCommitExhaustion(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool releaseImageDataWhenConsumed(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
|
||||
@@ -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<<4ee4c8163356e7777ab080a9364a5492>>
|
||||
* @generated SignedSource<<21d92927b8b282b29ed9ff5795c60f8a>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -214,6 +214,10 @@ double ReactNativeFeatureFlags::preparedTextCacheSize() {
|
||||
return getAccessor().preparedTextCacheSize();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion() {
|
||||
return getAccessor().preventShadowTreeCommitExhaustion();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::releaseImageDataWhenConsumed() {
|
||||
return getAccessor().releaseImageDataWhenConsumed();
|
||||
}
|
||||
|
||||
@@ -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<<0bbb98a247e0cb3d1a54228507f98b49>>
|
||||
* @generated SignedSource<<4e6e5bfa892fcfc1ad67d625a6b58ce6>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -274,6 +274,11 @@ class ReactNativeFeatureFlags {
|
||||
*/
|
||||
RN_EXPORT static double preparedTextCacheSize();
|
||||
|
||||
/**
|
||||
* Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.
|
||||
*/
|
||||
RN_EXPORT static bool preventShadowTreeCommitExhaustion();
|
||||
|
||||
/**
|
||||
* Releases the cached image data when it is consumed by the observers.
|
||||
*/
|
||||
|
||||
+35
-17
@@ -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<<a03ba90ac1fc3573fdacd287051bb7cf>>
|
||||
* @generated SignedSource<<e3663b1990ce3e0f459a9cb202ad24ee>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -875,6 +875,24 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() {
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() {
|
||||
auto flagValue = preventShadowTreeCommitExhaustion_.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(47, "preventShadowTreeCommitExhaustion");
|
||||
|
||||
flagValue = currentProvider_->preventShadowTreeCommitExhaustion();
|
||||
preventShadowTreeCommitExhaustion_ = flagValue;
|
||||
}
|
||||
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
|
||||
auto flagValue = releaseImageDataWhenConsumed_.load();
|
||||
|
||||
@@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(47, "releaseImageDataWhenConsumed");
|
||||
markFlagAsAccessed(48, "releaseImageDataWhenConsumed");
|
||||
|
||||
flagValue = currentProvider_->releaseImageDataWhenConsumed();
|
||||
releaseImageDataWhenConsumed_ = flagValue;
|
||||
@@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(48, "shouldPressibilityUseW3CPointerEventsForHover");
|
||||
markFlagAsAccessed(49, "shouldPressibilityUseW3CPointerEventsForHover");
|
||||
|
||||
flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover();
|
||||
shouldPressibilityUseW3CPointerEventsForHover_ = flagValue;
|
||||
@@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause()
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(49, "skipActivityIdentityAssertionOnHostPause");
|
||||
markFlagAsAccessed(50, "skipActivityIdentityAssertionOnHostPause");
|
||||
|
||||
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
|
||||
skipActivityIdentityAssertionOnHostPause_ = flagValue;
|
||||
@@ -938,7 +956,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(50, "traceTurboModulePromiseRejectionsOnAndroid");
|
||||
markFlagAsAccessed(51, "traceTurboModulePromiseRejectionsOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
|
||||
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
|
||||
@@ -956,7 +974,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(51, "updateRuntimeShadowNodeReferencesOnCommit");
|
||||
markFlagAsAccessed(52, "updateRuntimeShadowNodeReferencesOnCommit");
|
||||
|
||||
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
|
||||
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
|
||||
@@ -974,7 +992,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(52, "useAlwaysAvailableJSErrorHandling");
|
||||
markFlagAsAccessed(53, "useAlwaysAvailableJSErrorHandling");
|
||||
|
||||
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
|
||||
useAlwaysAvailableJSErrorHandling_ = flagValue;
|
||||
@@ -992,7 +1010,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(53, "useFabricInterop");
|
||||
markFlagAsAccessed(54, "useFabricInterop");
|
||||
|
||||
flagValue = currentProvider_->useFabricInterop();
|
||||
useFabricInterop_ = flagValue;
|
||||
@@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(54, "useNativeEqualsInNativeReadableArrayAndroid");
|
||||
markFlagAsAccessed(55, "useNativeEqualsInNativeReadableArrayAndroid");
|
||||
|
||||
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
|
||||
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
|
||||
@@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(55, "useNativeTransformHelperAndroid");
|
||||
markFlagAsAccessed(56, "useNativeTransformHelperAndroid");
|
||||
|
||||
flagValue = currentProvider_->useNativeTransformHelperAndroid();
|
||||
useNativeTransformHelperAndroid_ = flagValue;
|
||||
@@ -1046,7 +1064,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(56, "useNativeViewConfigsInBridgelessMode");
|
||||
markFlagAsAccessed(57, "useNativeViewConfigsInBridgelessMode");
|
||||
|
||||
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
|
||||
useNativeViewConfigsInBridgelessMode_ = flagValue;
|
||||
@@ -1064,7 +1082,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(57, "useOptimizedEventBatchingOnAndroid");
|
||||
markFlagAsAccessed(58, "useOptimizedEventBatchingOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
|
||||
useOptimizedEventBatchingOnAndroid_ = flagValue;
|
||||
@@ -1082,7 +1100,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(58, "useRawPropsJsiValue");
|
||||
markFlagAsAccessed(59, "useRawPropsJsiValue");
|
||||
|
||||
flagValue = currentProvider_->useRawPropsJsiValue();
|
||||
useRawPropsJsiValue_ = flagValue;
|
||||
@@ -1100,7 +1118,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(59, "useShadowNodeStateOnClone");
|
||||
markFlagAsAccessed(60, "useShadowNodeStateOnClone");
|
||||
|
||||
flagValue = currentProvider_->useShadowNodeStateOnClone();
|
||||
useShadowNodeStateOnClone_ = flagValue;
|
||||
@@ -1118,7 +1136,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(60, "useTurboModuleInterop");
|
||||
markFlagAsAccessed(61, "useTurboModuleInterop");
|
||||
|
||||
flagValue = currentProvider_->useTurboModuleInterop();
|
||||
useTurboModuleInterop_ = flagValue;
|
||||
@@ -1136,7 +1154,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(61, "useTurboModules");
|
||||
markFlagAsAccessed(62, "useTurboModules");
|
||||
|
||||
flagValue = currentProvider_->useTurboModules();
|
||||
useTurboModules_ = flagValue;
|
||||
@@ -1154,7 +1172,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(62, "virtualViewPrerenderRatio");
|
||||
markFlagAsAccessed(63, "virtualViewPrerenderRatio");
|
||||
|
||||
flagValue = currentProvider_->virtualViewPrerenderRatio();
|
||||
virtualViewPrerenderRatio_ = flagValue;
|
||||
|
||||
+4
-2
@@ -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<<84fc9cb4ade1c9a321e7d739b403fa72>>
|
||||
* @generated SignedSource<<ddda049798e510fb51f84f7d396192e2>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -79,6 +79,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
bool fuseboxNetworkInspectionEnabled();
|
||||
bool hideOffscreenVirtualViewsOnIOS();
|
||||
double preparedTextCacheSize();
|
||||
bool preventShadowTreeCommitExhaustion();
|
||||
bool releaseImageDataWhenConsumed();
|
||||
bool shouldPressibilityUseW3CPointerEventsForHover();
|
||||
bool skipActivityIdentityAssertionOnHostPause();
|
||||
@@ -106,7 +107,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
|
||||
bool wasOverridden_;
|
||||
|
||||
std::array<std::atomic<const char*>, 63> accessedFeatureFlags_;
|
||||
std::array<std::atomic<const char*>, 64> accessedFeatureFlags_;
|
||||
|
||||
std::atomic<std::optional<bool>> commonTestFlag_;
|
||||
std::atomic<std::optional<bool>> cxxNativeAnimatedEnabled_;
|
||||
@@ -155,6 +156,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::atomic<std::optional<bool>> fuseboxNetworkInspectionEnabled_;
|
||||
std::atomic<std::optional<bool>> hideOffscreenVirtualViewsOnIOS_;
|
||||
std::atomic<std::optional<double>> preparedTextCacheSize_;
|
||||
std::atomic<std::optional<bool>> preventShadowTreeCommitExhaustion_;
|
||||
std::atomic<std::optional<bool>> releaseImageDataWhenConsumed_;
|
||||
std::atomic<std::optional<bool>> shouldPressibilityUseW3CPointerEventsForHover_;
|
||||
std::atomic<std::optional<bool>> skipActivityIdentityAssertionOnHostPause_;
|
||||
|
||||
+5
-1
@@ -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<<4933ca80972665b1ac6e358e626d57ac>>
|
||||
* @generated SignedSource<<4c386a379665e7c983db3da799f7c902>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -215,6 +215,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
|
||||
return 200.0;
|
||||
}
|
||||
|
||||
bool preventShadowTreeCommitExhaustion() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool releaseImageDataWhenConsumed() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
+10
-1
@@ -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<<72907a7058f38e80b54947d321b0062d>>
|
||||
* @generated SignedSource<<c7559d37ffcb752e95b8313dcd1ee681>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -468,6 +468,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
|
||||
return ReactNativeFeatureFlagsDefaults::preparedTextCacheSize();
|
||||
}
|
||||
|
||||
bool preventShadowTreeCommitExhaustion() override {
|
||||
auto value = values_["preventShadowTreeCommitExhaustion"];
|
||||
if (!value.isNull()) {
|
||||
return value.getBool();
|
||||
}
|
||||
|
||||
return ReactNativeFeatureFlagsDefaults::preventShadowTreeCommitExhaustion();
|
||||
}
|
||||
|
||||
bool releaseImageDataWhenConsumed() override {
|
||||
auto value = values_["releaseImageDataWhenConsumed"];
|
||||
if (!value.isNull()) {
|
||||
|
||||
+5
-1
@@ -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<<c35e74abcb9b95a9c31854a32c282edd>>
|
||||
* @generated SignedSource<<a045579d42e45fa80831856734a063aa>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -27,6 +27,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
|
||||
public:
|
||||
ReactNativeFeatureFlagsOverridesOSSExperimental() = default;
|
||||
|
||||
bool preventShadowTreeCommitExhaustion() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool useNativeEqualsInNativeReadableArrayAndroid() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
+2
-1
@@ -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<<87afa2a91688f00bc5843cc13a8b72f1>>
|
||||
* @generated SignedSource<<96ec4b7e248bbde00b52f09779e8a051>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -72,6 +72,7 @@ class ReactNativeFeatureFlagsProvider {
|
||||
virtual bool fuseboxNetworkInspectionEnabled() = 0;
|
||||
virtual bool hideOffscreenVirtualViewsOnIOS() = 0;
|
||||
virtual double preparedTextCacheSize() = 0;
|
||||
virtual bool preventShadowTreeCommitExhaustion() = 0;
|
||||
virtual bool releaseImageDataWhenConsumed() = 0;
|
||||
virtual bool shouldPressibilityUseW3CPointerEventsForHover() = 0;
|
||||
virtual bool skipActivityIdentityAssertionOnHostPause() = 0;
|
||||
|
||||
+6
-1
@@ -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<<c000bcb7b4e0cac836a708e509f59b86>>
|
||||
* @generated SignedSource<<a7a1684e83d7a0d96e9ff5623d74916a>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -279,6 +279,11 @@ double NativeReactNativeFeatureFlags::preparedTextCacheSize(
|
||||
return ReactNativeFeatureFlags::preparedTextCacheSize();
|
||||
}
|
||||
|
||||
bool NativeReactNativeFeatureFlags::preventShadowTreeCommitExhaustion(
|
||||
jsi::Runtime& /*runtime*/) {
|
||||
return ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion();
|
||||
}
|
||||
|
||||
bool NativeReactNativeFeatureFlags::releaseImageDataWhenConsumed(
|
||||
jsi::Runtime& /*runtime*/) {
|
||||
return ReactNativeFeatureFlags::releaseImageDataWhenConsumed();
|
||||
|
||||
+3
-1
@@ -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<<dc6fc103b2ecf2a64f229f5f6333c313>>
|
||||
* @generated SignedSource<<008cdb8915d0252d2c714529fb177245>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -130,6 +130,8 @@ class NativeReactNativeFeatureFlags
|
||||
|
||||
double preparedTextCacheSize(jsi::Runtime& runtime);
|
||||
|
||||
bool preventShadowTreeCommitExhaustion(jsi::Runtime& runtime);
|
||||
|
||||
bool releaseImageDataWhenConsumed(jsi::Runtime& runtime);
|
||||
|
||||
bool shouldPressibilityUseW3CPointerEventsForHover(jsi::Runtime& runtime);
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
namespace {
|
||||
const int MAX_COMMIT_ATTEMPTS_BEFORE_LOCKING = 3;
|
||||
} // namespace
|
||||
|
||||
using CommitStatus = ShadowTree::CommitStatus;
|
||||
using CommitMode = ShadowTree::CommitMode;
|
||||
|
||||
@@ -210,7 +214,8 @@ void ShadowTree::setCommitMode(CommitMode commitMode) const {
|
||||
auto revision = ShadowTreeRevision{};
|
||||
|
||||
{
|
||||
std::unique_lock lock(commitMutex_);
|
||||
ShadowTree::UniqueLock lock = uniqueCommitLock();
|
||||
|
||||
if (commitMode_ == commitMode) {
|
||||
return;
|
||||
}
|
||||
@@ -227,7 +232,7 @@ void ShadowTree::setCommitMode(CommitMode commitMode) const {
|
||||
}
|
||||
|
||||
CommitMode ShadowTree::getCommitMode() const {
|
||||
std::shared_lock lock(commitMutex_);
|
||||
SharedLock lock = sharedCommitLock();
|
||||
return commitMode_;
|
||||
}
|
||||
|
||||
@@ -241,17 +246,32 @@ CommitStatus ShadowTree::commit(
|
||||
const CommitOptions& commitOptions) const {
|
||||
[[maybe_unused]] int attempts = 0;
|
||||
|
||||
while (true) {
|
||||
attempts++;
|
||||
|
||||
auto status = tryCommit(transaction, commitOptions);
|
||||
if (status != CommitStatus::Failed) {
|
||||
return status;
|
||||
if (ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion()) {
|
||||
while (attempts < MAX_COMMIT_ATTEMPTS_BEFORE_LOCKING) {
|
||||
auto status = tryCommit(transaction, commitOptions);
|
||||
if (status != CommitStatus::Failed) {
|
||||
return status;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// After multiple attempts, we failed to commit the transaction.
|
||||
// Something internally went terribly wrong.
|
||||
react_native_assert(attempts < 1024);
|
||||
{
|
||||
std::unique_lock lock(commitMutexRecursive_);
|
||||
return tryCommit(transaction, commitOptions);
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
attempts++;
|
||||
|
||||
auto status = tryCommit(transaction, commitOptions);
|
||||
if (status != CommitStatus::Failed) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// After multiple attempts, we failed to commit the transaction.
|
||||
// Something internally went terribly wrong.
|
||||
react_native_assert(attempts < 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +289,7 @@ CommitStatus ShadowTree::tryCommit(
|
||||
|
||||
{
|
||||
// Reading `currentRevision_` in shared manner.
|
||||
std::shared_lock lock(commitMutex_);
|
||||
SharedLock lock = sharedCommitLock();
|
||||
commitMode = commitMode_;
|
||||
oldRevision = currentRevision_;
|
||||
}
|
||||
@@ -310,7 +330,7 @@ CommitStatus ShadowTree::tryCommit(
|
||||
|
||||
{
|
||||
// Updating `currentRevision_` in unique manner if it hasn't changed.
|
||||
std::unique_lock lock(commitMutex_);
|
||||
UniqueLock lock = uniqueCommitLock();
|
||||
|
||||
if (currentRevision_.number != oldRevision.number) {
|
||||
return CommitStatus::Failed;
|
||||
@@ -349,7 +369,7 @@ CommitStatus ShadowTree::tryCommit(
|
||||
}
|
||||
|
||||
ShadowTreeRevision ShadowTree::getCurrentRevision() const {
|
||||
std::shared_lock lock(commitMutex_);
|
||||
SharedLock lock = sharedCommitLock();
|
||||
return currentRevision_;
|
||||
}
|
||||
|
||||
@@ -397,4 +417,20 @@ void ShadowTree::notifyDelegatesOfUpdates() const {
|
||||
delegate_.shadowTreeDidFinishTransaction(mountingCoordinator_, true);
|
||||
}
|
||||
|
||||
inline ShadowTree::UniqueLock ShadowTree::uniqueCommitLock() const {
|
||||
if (ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion()) {
|
||||
return std::unique_lock{commitMutexRecursive_};
|
||||
} else {
|
||||
return std::unique_lock{commitMutex_};
|
||||
}
|
||||
}
|
||||
|
||||
inline ShadowTree::SharedLock ShadowTree::sharedCommitLock() const {
|
||||
if (ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion()) {
|
||||
return std::unique_lock{commitMutexRecursive_};
|
||||
} else {
|
||||
return std::shared_lock{commitMutex_};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <react/renderer/components/root/RootShadowNode.h>
|
||||
#include <react/renderer/core/LayoutConstraints.h>
|
||||
@@ -150,10 +152,21 @@ class ShadowTree final {
|
||||
const SurfaceId surfaceId_;
|
||||
const ShadowTreeDelegate& delegate_;
|
||||
mutable std::shared_mutex commitMutex_;
|
||||
mutable std::recursive_mutex commitMutexRecursive_;
|
||||
mutable CommitMode commitMode_{
|
||||
CommitMode::Normal}; // Protected by `commitMutex_`.
|
||||
mutable ShadowTreeRevision currentRevision_; // Protected by `commitMutex_`.
|
||||
std::shared_ptr<const MountingCoordinator> mountingCoordinator_;
|
||||
|
||||
using UniqueLock = std::variant<
|
||||
std::unique_lock<std::shared_mutex>,
|
||||
std::unique_lock<std::recursive_mutex>>;
|
||||
using SharedLock = std::variant<
|
||||
std::shared_lock<std::shared_mutex>,
|
||||
std::unique_lock<std::recursive_mutex>>;
|
||||
|
||||
inline UniqueLock uniqueCommitLock() const;
|
||||
inline SharedLock sharedCommitLock() const;
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -547,6 +547,17 @@ const definitions: FeatureFlagDefinitions = {
|
||||
},
|
||||
ossReleaseStage: 'none',
|
||||
},
|
||||
preventShadowTreeCommitExhaustion: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
dateAdded: '2025-07-23',
|
||||
description:
|
||||
'Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.',
|
||||
expectedReleaseValue: true,
|
||||
purpose: 'experimentation',
|
||||
},
|
||||
ossReleaseStage: 'experimental',
|
||||
},
|
||||
releaseImageDataWhenConsumed: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
|
||||
@@ -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<<4b5f7535318d03c648dd81ca9a81277c>>
|
||||
* @generated SignedSource<<6a5b796b26a689b4b8ebaa101c60b5c8>>
|
||||
* @flow strict
|
||||
* @noformat
|
||||
*/
|
||||
@@ -93,6 +93,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
|
||||
fuseboxNetworkInspectionEnabled: Getter<boolean>,
|
||||
hideOffscreenVirtualViewsOnIOS: Getter<boolean>,
|
||||
preparedTextCacheSize: Getter<number>,
|
||||
preventShadowTreeCommitExhaustion: Getter<boolean>,
|
||||
releaseImageDataWhenConsumed: Getter<boolean>,
|
||||
shouldPressibilityUseW3CPointerEventsForHover: Getter<boolean>,
|
||||
skipActivityIdentityAssertionOnHostPause: Getter<boolean>,
|
||||
@@ -358,6 +359,10 @@ export const hideOffscreenVirtualViewsOnIOS: Getter<boolean> = createNativeFlagG
|
||||
* Number cached PreparedLayouts in TextLayoutManager cache
|
||||
*/
|
||||
export const preparedTextCacheSize: Getter<number> = createNativeFlagGetter('preparedTextCacheSize', 200);
|
||||
/**
|
||||
* Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.
|
||||
*/
|
||||
export const preventShadowTreeCommitExhaustion: Getter<boolean> = createNativeFlagGetter('preventShadowTreeCommitExhaustion', false);
|
||||
/**
|
||||
* Releases the cached image data when it is consumed by the observers.
|
||||
*/
|
||||
|
||||
+2
-1
@@ -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<<2b9624673a7fd8b4f820921ba0a088d6>>
|
||||
* @generated SignedSource<<119985fbbfbc9ace52b26a7fb3b91f03>>
|
||||
* @flow strict
|
||||
* @noformat
|
||||
*/
|
||||
@@ -72,6 +72,7 @@ export interface Spec extends TurboModule {
|
||||
+fuseboxNetworkInspectionEnabled?: () => boolean;
|
||||
+hideOffscreenVirtualViewsOnIOS?: () => boolean;
|
||||
+preparedTextCacheSize?: () => number;
|
||||
+preventShadowTreeCommitExhaustion?: () => boolean;
|
||||
+releaseImageDataWhenConsumed?: () => boolean;
|
||||
+shouldPressibilityUseW3CPointerEventsForHover?: () => boolean;
|
||||
+skipActivityIdentityAssertionOnHostPause?: () => boolean;
|
||||
|
||||
Reference in New Issue
Block a user