mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Throw exception with name of TurboModule instead of deadlock during sync rendering (#49509)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49509 Currently if we hit a deadlock in sync rendering due to a TurboModule initialization that requires main queue setup we don't get any information about which TurboModule caused the issue. To help us know which TurboModules we need to fix, this instead will crash with the name of the TurboModule. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D69805065 fbshipit-source-id: f75df44f9a603a5f53a008382d32b2b5285c1162
This commit is contained in:
committed by
Facebook GitHub Bot
parent
778382ad3d
commit
5e63f34ab0
+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<<6e8144a48ab65f3b5ad8dd601cee2114>>
|
||||
* @generated SignedSource<<99417ea22d1bf969d71388c600d42e85>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -226,6 +226,12 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun lazyAnimationCallbacks(): Boolean = accessor.lazyAnimationCallbacks()
|
||||
|
||||
/**
|
||||
* Throw an exception instead of deadlocking when a TurboModule that requires main queue setup is initialized during a synchronous render on iOS.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(): Boolean = accessor.throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS()
|
||||
|
||||
/**
|
||||
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
|
||||
*/
|
||||
|
||||
+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<<dd2fa7d4fb722350d77094f8bcc38872>>
|
||||
* @generated SignedSource<<3ede8912be1a2874050bab4c88a0a416>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -53,6 +53,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var fuseboxEnabledReleaseCache: Boolean? = null
|
||||
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
|
||||
private var lazyAnimationCallbacksCache: Boolean? = null
|
||||
private var throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache: Boolean? = null
|
||||
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
|
||||
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
|
||||
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
|
||||
@@ -360,6 +361,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(): Boolean {
|
||||
var cached = throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS()
|
||||
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
|
||||
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
|
||||
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<<eb52a136ba489460c2131105f4cc5b3f>>
|
||||
* @generated SignedSource<<33adb23bfdb99c531c674f4c6d09ddbd>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -94,6 +94,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun lazyAnimationCallbacks(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): 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<<5315bd93234b8c00b7d029081231eeeb>>
|
||||
* @generated SignedSource<<94ffd31d788dae979b92c7a347f2ad40>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -89,6 +89,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun lazyAnimationCallbacks(): Boolean = false
|
||||
|
||||
override fun throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(): Boolean = false
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
|
||||
|
||||
override fun useAlwaysAvailableJSErrorHandling(): 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<<a97afc0515bcfc2e4ed60565af08ce8a>>
|
||||
* @generated SignedSource<<15176fdf014740aa34039cfd82943ae2>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -57,6 +57,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var fuseboxEnabledReleaseCache: Boolean? = null
|
||||
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
|
||||
private var lazyAnimationCallbacksCache: Boolean? = null
|
||||
private var throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache: Boolean? = null
|
||||
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
|
||||
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
|
||||
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
|
||||
@@ -397,6 +398,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(): Boolean {
|
||||
var cached = throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS()
|
||||
accessedFeatureFlags.add("throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS")
|
||||
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOSCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
|
||||
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
|
||||
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<<51121a77716b7748f993496eff5f6bfb>>
|
||||
* @generated SignedSource<<98ad5281b51418af43620a89a40a3c45>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -89,6 +89,8 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun lazyAnimationCallbacks(): Boolean
|
||||
|
||||
@DoNotStrip public fun throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(): Boolean
|
||||
|
||||
@DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
|
||||
|
||||
@DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): 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<<b0dab4690bff769baf4e83f508a6e514>>
|
||||
* @generated SignedSource<<b02f04930c4c998668d6ad522717f914>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -237,6 +237,12 @@ class ReactNativeFeatureFlagsProviderHolder
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS");
|
||||
return method(javaProvider_);
|
||||
}
|
||||
|
||||
bool traceTurboModulePromiseRejectionsOnAndroid() override {
|
||||
static const auto method =
|
||||
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("traceTurboModulePromiseRejectionsOnAndroid");
|
||||
@@ -460,6 +466,11 @@ bool JReactNativeFeatureFlagsCxxInterop::lazyAnimationCallbacks(
|
||||
return ReactNativeFeatureFlags::lazyAnimationCallbacks();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
}
|
||||
|
||||
bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
|
||||
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
|
||||
@@ -635,6 +646,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
|
||||
makeNativeMethod(
|
||||
"lazyAnimationCallbacks",
|
||||
JReactNativeFeatureFlagsCxxInterop::lazyAnimationCallbacks),
|
||||
makeNativeMethod(
|
||||
"throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS",
|
||||
JReactNativeFeatureFlagsCxxInterop::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS),
|
||||
makeNativeMethod(
|
||||
"traceTurboModulePromiseRejectionsOnAndroid",
|
||||
JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid),
|
||||
|
||||
+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<<0bf1c23eda1c41a60eaae19e87c541e9>>
|
||||
* @generated SignedSource<<a34eb273660e3fecbc6d9fba66176462>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -129,6 +129,9 @@ class JReactNativeFeatureFlagsCxxInterop
|
||||
static bool lazyAnimationCallbacks(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(
|
||||
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
|
||||
|
||||
static bool traceTurboModulePromiseRejectionsOnAndroid(
|
||||
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<<0a3119b99e455127d8a404963a23eb96>>
|
||||
* @generated SignedSource<<5ea68e0bf3cbf7822d7a3e80f7dffc72>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -158,6 +158,10 @@ bool ReactNativeFeatureFlags::lazyAnimationCallbacks() {
|
||||
return getAccessor().lazyAnimationCallbacks();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() {
|
||||
return getAccessor().throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() {
|
||||
return getAccessor().traceTurboModulePromiseRejectionsOnAndroid();
|
||||
}
|
||||
|
||||
@@ -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<<b354e1b9ffc58e74ffff200c14728d1d>>
|
||||
* @generated SignedSource<<9be3692ca63703f32de54e24d4ceb6e9>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -204,6 +204,11 @@ class ReactNativeFeatureFlags {
|
||||
*/
|
||||
RN_EXPORT static bool lazyAnimationCallbacks();
|
||||
|
||||
/**
|
||||
* Throw an exception instead of deadlocking when a TurboModule that requires main queue setup is initialized during a synchronous render on iOS.
|
||||
*/
|
||||
RN_EXPORT static bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
|
||||
/**
|
||||
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
|
||||
*/
|
||||
|
||||
+28
-10
@@ -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<<41f3b407b98e268ba80f8cd8bb960321>>
|
||||
* @generated SignedSource<<12be545f9fb06b76cb6c521e1d381bf1>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -623,6 +623,24 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() {
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() {
|
||||
auto flagValue = throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS_.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(33, "throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS");
|
||||
|
||||
flagValue = currentProvider_->throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS_ = flagValue;
|
||||
}
|
||||
|
||||
return flagValue.value();
|
||||
}
|
||||
|
||||
bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid() {
|
||||
auto flagValue = traceTurboModulePromiseRejectionsOnAndroid_.load();
|
||||
|
||||
@@ -632,7 +650,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(33, "traceTurboModulePromiseRejectionsOnAndroid");
|
||||
markFlagAsAccessed(34, "traceTurboModulePromiseRejectionsOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
|
||||
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
|
||||
@@ -650,7 +668,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(34, "useAlwaysAvailableJSErrorHandling");
|
||||
markFlagAsAccessed(35, "useAlwaysAvailableJSErrorHandling");
|
||||
|
||||
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
|
||||
useAlwaysAvailableJSErrorHandling_ = flagValue;
|
||||
@@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::useEditTextStockAndroidFocusBehavior() {
|
||||
// be accessing the provider multiple times but the end state of this
|
||||
// instance and the returned flag value would be the same.
|
||||
|
||||
markFlagAsAccessed(35, "useEditTextStockAndroidFocusBehavior");
|
||||
markFlagAsAccessed(36, "useEditTextStockAndroidFocusBehavior");
|
||||
|
||||
flagValue = currentProvider_->useEditTextStockAndroidFocusBehavior();
|
||||
useEditTextStockAndroidFocusBehavior_ = flagValue;
|
||||
@@ -686,7 +704,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(36, "useFabricInterop");
|
||||
markFlagAsAccessed(37, "useFabricInterop");
|
||||
|
||||
flagValue = currentProvider_->useFabricInterop();
|
||||
useFabricInterop_ = flagValue;
|
||||
@@ -704,7 +722,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(37, "useNativeViewConfigsInBridgelessMode");
|
||||
markFlagAsAccessed(38, "useNativeViewConfigsInBridgelessMode");
|
||||
|
||||
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
|
||||
useNativeViewConfigsInBridgelessMode_ = flagValue;
|
||||
@@ -722,7 +740,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(38, "useOptimizedEventBatchingOnAndroid");
|
||||
markFlagAsAccessed(39, "useOptimizedEventBatchingOnAndroid");
|
||||
|
||||
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
|
||||
useOptimizedEventBatchingOnAndroid_ = flagValue;
|
||||
@@ -740,7 +758,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(39, "useRawPropsJsiValue");
|
||||
markFlagAsAccessed(40, "useRawPropsJsiValue");
|
||||
|
||||
flagValue = currentProvider_->useRawPropsJsiValue();
|
||||
useRawPropsJsiValue_ = flagValue;
|
||||
@@ -758,7 +776,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(40, "useTurboModuleInterop");
|
||||
markFlagAsAccessed(41, "useTurboModuleInterop");
|
||||
|
||||
flagValue = currentProvider_->useTurboModuleInterop();
|
||||
useTurboModuleInterop_ = flagValue;
|
||||
@@ -776,7 +794,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(41, "useTurboModules");
|
||||
markFlagAsAccessed(42, "useTurboModules");
|
||||
|
||||
flagValue = currentProvider_->useTurboModules();
|
||||
useTurboModules_ = 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<<cf03a6e938df9988c00249dc78576cbf>>
|
||||
* @generated SignedSource<<65caac2e749261ca919780dff302ef57>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -65,6 +65,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
bool fuseboxEnabledRelease();
|
||||
bool fuseboxNetworkInspectionEnabled();
|
||||
bool lazyAnimationCallbacks();
|
||||
bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
bool traceTurboModulePromiseRejectionsOnAndroid();
|
||||
bool useAlwaysAvailableJSErrorHandling();
|
||||
bool useEditTextStockAndroidFocusBehavior();
|
||||
@@ -85,7 +86,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
|
||||
bool wasOverridden_;
|
||||
|
||||
std::array<std::atomic<const char*>, 42> accessedFeatureFlags_;
|
||||
std::array<std::atomic<const char*>, 43> accessedFeatureFlags_;
|
||||
|
||||
std::atomic<std::optional<bool>> commonTestFlag_;
|
||||
std::atomic<std::optional<bool>> disableMountItemReorderingAndroid_;
|
||||
@@ -120,6 +121,7 @@ class ReactNativeFeatureFlagsAccessor {
|
||||
std::atomic<std::optional<bool>> fuseboxEnabledRelease_;
|
||||
std::atomic<std::optional<bool>> fuseboxNetworkInspectionEnabled_;
|
||||
std::atomic<std::optional<bool>> lazyAnimationCallbacks_;
|
||||
std::atomic<std::optional<bool>> throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS_;
|
||||
std::atomic<std::optional<bool>> traceTurboModulePromiseRejectionsOnAndroid_;
|
||||
std::atomic<std::optional<bool>> useAlwaysAvailableJSErrorHandling_;
|
||||
std::atomic<std::optional<bool>> useEditTextStockAndroidFocusBehavior_;
|
||||
|
||||
+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<<1ade51e6e5c82275c5c01b72df630eee>>
|
||||
* @generated SignedSource<<acc0b73730fe5e0a8e329735481bc579>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -159,6 +159,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool traceTurboModulePromiseRejectionsOnAndroid() 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<<95aa462c1a48b2299c2a82adf74c58df>>
|
||||
* @generated SignedSource<<c36182568c56dd82ef777bc477a9a4fe>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -342,6 +342,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
|
||||
return ReactNativeFeatureFlagsDefaults::lazyAnimationCallbacks();
|
||||
}
|
||||
|
||||
bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() override {
|
||||
auto value = values_["throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS"];
|
||||
if (!value.isNull()) {
|
||||
return value.getBool();
|
||||
}
|
||||
|
||||
return ReactNativeFeatureFlagsDefaults::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
}
|
||||
|
||||
bool traceTurboModulePromiseRejectionsOnAndroid() override {
|
||||
auto value = values_["traceTurboModulePromiseRejectionsOnAndroid"];
|
||||
if (!value.isNull()) {
|
||||
|
||||
+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<<f3241de0ced5e6ea54e6debf2a453c36>>
|
||||
* @generated SignedSource<<716d5779133c4ee8c9d20e4a71065d59>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -58,6 +58,7 @@ class ReactNativeFeatureFlagsProvider {
|
||||
virtual bool fuseboxEnabledRelease() = 0;
|
||||
virtual bool fuseboxNetworkInspectionEnabled() = 0;
|
||||
virtual bool lazyAnimationCallbacks() = 0;
|
||||
virtual bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS() = 0;
|
||||
virtual bool traceTurboModulePromiseRejectionsOnAndroid() = 0;
|
||||
virtual bool useAlwaysAvailableJSErrorHandling() = 0;
|
||||
virtual bool useEditTextStockAndroidFocusBehavior() = 0;
|
||||
|
||||
+2
-1
@@ -34,7 +34,7 @@ Pod::Spec.new do |s|
|
||||
s.platforms = min_supported_versions
|
||||
s.source = source
|
||||
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
|
||||
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fast_float/include\" \"$(PODS_ROOT)/fmt/include\" \"$(PODS_ROOT)/Headers/Private/React-Core\"",
|
||||
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fast_float/include\" \"$(PODS_ROOT)/fmt/include\" \"$(PODS_ROOT)/Headers/Private/React-Core\" \"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_featureflags.framework/Headers\"",
|
||||
"USE_HEADERMAP" => "YES",
|
||||
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
|
||||
"GCC_WARN_PEDANTIC" => "YES" }
|
||||
@@ -51,6 +51,7 @@ Pod::Spec.new do |s|
|
||||
s.dependency "React-Core"
|
||||
s.dependency "React-cxxreact"
|
||||
s.dependency "React-jsi"
|
||||
s.dependency "React-featureflags"
|
||||
s.dependency "React-runtimeexecutor"
|
||||
add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern')
|
||||
|
||||
|
||||
+31
-1
@@ -29,9 +29,11 @@
|
||||
#import <React/RCTUtils.h>
|
||||
#import <ReactCommon/CxxTurboModuleUtils.h>
|
||||
#import <ReactCommon/RCTTurboModuleWithJSIBindings.h>
|
||||
#import <ReactCommon/RuntimeExecutor.h>
|
||||
#import <ReactCommon/TurboCxxModule.h>
|
||||
#import <ReactCommon/TurboModulePerfLogger.h>
|
||||
#import <ReactCommon/TurboModuleUtils.h>
|
||||
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
|
||||
using namespace facebook;
|
||||
using namespace facebook::react;
|
||||
@@ -574,7 +576,35 @@ typedef struct {
|
||||
};
|
||||
|
||||
if ([self _requiresMainQueueSetup:moduleClass]) {
|
||||
RCTUnsafeExecuteOnMainQueueSync(work);
|
||||
if (ReactNativeFeatureFlags::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS()) {
|
||||
static int32_t modulesSettingUpOnMainQueueCount = 0;
|
||||
bool needsUnlock = NO;
|
||||
|
||||
// This function can be recursive. If it's called recursively, we need to skip the lock.
|
||||
// We can't use a recursive mutex instead because the lock is needed on multiple threads.
|
||||
if (modulesSettingUpOnMainQueueCount == 0) {
|
||||
if (!facebook::react::getMainThreadMutex()->try_lock()) {
|
||||
NSString *reason = [NSString
|
||||
stringWithFormat:
|
||||
@"TurboModule %@ which requires main queue setup is initializing during sync rendering. This would have caused a deadlock. Please fix this by avoiding main queue setup or eager initializing this TurboModule.",
|
||||
NSStringFromClass(moduleClass)];
|
||||
NSException *exception = [NSException exceptionWithName:@"UnsafeTurboModuleException"
|
||||
reason:reason
|
||||
userInfo:nil];
|
||||
@throw exception;
|
||||
}
|
||||
needsUnlock = true;
|
||||
}
|
||||
modulesSettingUpOnMainQueueCount++;
|
||||
RCTUnsafeExecuteOnMainQueueSync(work);
|
||||
modulesSettingUpOnMainQueueCount--;
|
||||
if (needsUnlock) {
|
||||
facebook::react::getMainThreadMutex()->unlock();
|
||||
}
|
||||
} else {
|
||||
RCTUnsafeExecuteOnMainQueueSync(work);
|
||||
}
|
||||
|
||||
} else {
|
||||
work();
|
||||
}
|
||||
|
||||
+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<<9a4f0e72b7e6518347f65253c018220a>>
|
||||
* @generated SignedSource<<f6c8f26ac2c00989c489cde342384a99>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -209,6 +209,11 @@ bool NativeReactNativeFeatureFlags::lazyAnimationCallbacks(
|
||||
return ReactNativeFeatureFlags::lazyAnimationCallbacks();
|
||||
}
|
||||
|
||||
bool NativeReactNativeFeatureFlags::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(
|
||||
jsi::Runtime& /*runtime*/) {
|
||||
return ReactNativeFeatureFlags::throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
|
||||
}
|
||||
|
||||
bool NativeReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(
|
||||
jsi::Runtime& /*runtime*/) {
|
||||
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
|
||||
|
||||
+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<<16deece14930c9b259f6f7594127fe55>>
|
||||
* @generated SignedSource<<805e70470028c1a98ef49e6aaf18fd03>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -103,6 +103,8 @@ class NativeReactNativeFeatureFlags
|
||||
|
||||
bool lazyAnimationCallbacks(jsi::Runtime& runtime);
|
||||
|
||||
bool throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS(jsi::Runtime& runtime);
|
||||
|
||||
bool traceTurboModulePromiseRejectionsOnAndroid(jsi::Runtime& runtime);
|
||||
|
||||
bool useAlwaysAvailableJSErrorHandling(jsi::Runtime& runtime);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 "RuntimeExecutor.h"
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
std::mutex* getMainThreadMutex() {
|
||||
static std::mutex mainThreadMutex;
|
||||
return &mainThreadMutex;
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
std::mutex* getMainThreadMutex();
|
||||
|
||||
/*
|
||||
* Takes a function and calls it with a reference to a Runtime. The function
|
||||
* will be called when it is safe to do so (i.e. it ensures non-concurrent
|
||||
@@ -31,6 +33,16 @@ using RuntimeExecutor =
|
||||
* Use this method when the caller needs to *be blocked* by executing the
|
||||
* `callback` and requires that the callback will be executed on the same
|
||||
* thread.
|
||||
* Example order of events (when not a sync call in runtimeExecutor callback):
|
||||
* - [UI thread] Lock all mutexes at start
|
||||
* - [UI thread] mutex1.lock before callback
|
||||
* - [JS thread] Set runtimePtr in runtimeExecutor callback
|
||||
* - [JS thread] mutex1.unlock in runtimeExecutor callback
|
||||
* - [UI thread] Call callback
|
||||
* - [JS thread] mutex2.lock in runtimeExecutor callback
|
||||
* - [UI thread] mutex2.unlock after callback
|
||||
* - [UI thread] mutex3.lock after callback
|
||||
* - [JS thread] mutex3.unlock in runtimeExecutor callback
|
||||
*/
|
||||
inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
|
||||
const RuntimeExecutor& runtimeExecutor,
|
||||
@@ -42,6 +54,7 @@ inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
|
||||
std::mutex mutex2;
|
||||
std::mutex mutex3;
|
||||
|
||||
getMainThreadMutex()->lock();
|
||||
mutex1.lock();
|
||||
mutex2.lock();
|
||||
mutex3.lock();
|
||||
@@ -70,6 +83,7 @@ inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
|
||||
callback(*runtimePtr);
|
||||
mutex2.unlock();
|
||||
mutex3.lock();
|
||||
getMainThreadMutex()->unlock();
|
||||
}
|
||||
|
||||
template <typename DataT>
|
||||
|
||||
@@ -398,6 +398,17 @@ const definitions: FeatureFlagDefinitions = {
|
||||
},
|
||||
ossReleaseStage: 'none',
|
||||
},
|
||||
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS: {
|
||||
defaultValue: false,
|
||||
metadata: {
|
||||
dateAdded: '2025-02-18',
|
||||
description:
|
||||
'Throw an exception instead of deadlocking when a TurboModule that requires main queue setup is initialized during a synchronous render on iOS.',
|
||||
expectedReleaseValue: true,
|
||||
purpose: 'experimentation',
|
||||
},
|
||||
ossReleaseStage: 'none',
|
||||
},
|
||||
traceTurboModulePromiseRejectionsOnAndroid: {
|
||||
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<<4bdedb6bbe2199cb99e72e86bfffe372>>
|
||||
* @generated SignedSource<<a8dd33d3160b800bc7afa18045e3ac7a>>
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
@@ -82,6 +82,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
|
||||
fuseboxEnabledRelease: Getter<boolean>,
|
||||
fuseboxNetworkInspectionEnabled: Getter<boolean>,
|
||||
lazyAnimationCallbacks: Getter<boolean>,
|
||||
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS: Getter<boolean>,
|
||||
traceTurboModulePromiseRejectionsOnAndroid: Getter<boolean>,
|
||||
useAlwaysAvailableJSErrorHandling: Getter<boolean>,
|
||||
useEditTextStockAndroidFocusBehavior: Getter<boolean>,
|
||||
@@ -304,6 +305,10 @@ export const fuseboxNetworkInspectionEnabled: Getter<boolean> = createNativeFlag
|
||||
* Only enqueue Choreographer calls if there is an ongoing animation, instead of enqueueing every frame.
|
||||
*/
|
||||
export const lazyAnimationCallbacks: Getter<boolean> = createNativeFlagGetter('lazyAnimationCallbacks', false);
|
||||
/**
|
||||
* Throw an exception instead of deadlocking when a TurboModule that requires main queue setup is initialized during a synchronous render on iOS.
|
||||
*/
|
||||
export const throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS: Getter<boolean> = createNativeFlagGetter('throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS', false);
|
||||
/**
|
||||
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
|
||||
*/
|
||||
|
||||
+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<<06a03507366a38dfc43868e94d75fcf1>>
|
||||
* @generated SignedSource<<f6bbbe19810cafad5142bf528f04165a>>
|
||||
* @flow strict
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,7 @@ export interface Spec extends TurboModule {
|
||||
+fuseboxEnabledRelease?: () => boolean;
|
||||
+fuseboxNetworkInspectionEnabled?: () => boolean;
|
||||
+lazyAnimationCallbacks?: () => boolean;
|
||||
+throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS?: () => boolean;
|
||||
+traceTurboModulePromiseRejectionsOnAndroid?: () => boolean;
|
||||
+useAlwaysAvailableJSErrorHandling?: () => boolean;
|
||||
+useEditTextStockAndroidFocusBehavior?: () => boolean;
|
||||
|
||||
Reference in New Issue
Block a user