Skip to content

Commit e2e5a56

Browse files
RSNarafacebook-github-bot
authored andcommitted
Introduce feature flag for safer main queue sync dispatch
Summary: Gating for "safer main queue sync dispatch": D74769326 Changelog: [Internal] Differential Revision: D74940621
1 parent 1ec1b5d commit e2e5a56

20 files changed

+148
-31
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<e9a109fd77667dd0cb945ef6ef9737f2>>
7+
* @generated SignedSource<<29fc8306d84a015708ae3b7cdc2b3fa6>>
88
*/
99

1010
/**
@@ -252,6 +252,12 @@ public object ReactNativeFeatureFlags {
252252
@JvmStatic
253253
public fun incorporateMaxLinesDuringAndroidLayout(): Boolean = accessor.incorporateMaxLinesDuringAndroidLayout()
254254

255+
/**
256+
* Main queue sync dispatch that won't deadlock with sync render/events
257+
*/
258+
@JvmStatic
259+
public fun saferMainQueueSyncDispatch(): Boolean = accessor.saferMainQueueSyncDispatch()
260+
255261
/**
256262
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
257263
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<1a5cd689229d4e0c31070123045e84da>>
7+
* @generated SignedSource<<2b8cd50c252f01a177bd8e6aadb801e8>>
88
*/
99

1010
/**
@@ -57,6 +57,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
5757
private var fuseboxEnabledReleaseCache: Boolean? = null
5858
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
5959
private var incorporateMaxLinesDuringAndroidLayoutCache: Boolean? = null
60+
private var saferMainQueueSyncDispatchCache: Boolean? = null
6061
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
6162
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
6263
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
@@ -402,6 +403,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
402403
return cached
403404
}
404405

406+
override fun saferMainQueueSyncDispatch(): Boolean {
407+
var cached = saferMainQueueSyncDispatchCache
408+
if (cached == null) {
409+
cached = ReactNativeFeatureFlagsCxxInterop.saferMainQueueSyncDispatch()
410+
saferMainQueueSyncDispatchCache = cached
411+
}
412+
return cached
413+
}
414+
405415
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
406416
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
407417
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<c39d0c1834797b3309b4fc5ce814280c>>
7+
* @generated SignedSource<<2965b940a2c335ca4d71aceb00f853bd>>
88
*/
99

1010
/**
@@ -102,6 +102,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
102102

103103
@DoNotStrip @JvmStatic public external fun incorporateMaxLinesDuringAndroidLayout(): Boolean
104104

105+
@DoNotStrip @JvmStatic public external fun saferMainQueueSyncDispatch(): Boolean
106+
105107
@DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
106108

107109
@DoNotStrip @JvmStatic public external fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<7f357475254104729cc7910c14e1c1fb>>
7+
* @generated SignedSource<<513bc3cc259131f9957f220b6847373e>>
88
*/
99

1010
/**
@@ -97,6 +97,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
9797

9898
override fun incorporateMaxLinesDuringAndroidLayout(): Boolean = true
9999

100+
override fun saferMainQueueSyncDispatch(): Boolean = false
101+
100102
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
101103

102104
override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<14cd1a58bd153dedda045a72c1494caa>>
7+
* @generated SignedSource<<4022c8d62266f4bcc649022d1be2d7fe>>
88
*/
99

1010
/**
@@ -61,6 +61,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
6161
private var fuseboxEnabledReleaseCache: Boolean? = null
6262
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
6363
private var incorporateMaxLinesDuringAndroidLayoutCache: Boolean? = null
64+
private var saferMainQueueSyncDispatchCache: Boolean? = null
6465
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
6566
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
6667
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
@@ -443,6 +444,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
443444
return cached
444445
}
445446

447+
override fun saferMainQueueSyncDispatch(): Boolean {
448+
var cached = saferMainQueueSyncDispatchCache
449+
if (cached == null) {
450+
cached = currentProvider.saferMainQueueSyncDispatch()
451+
accessedFeatureFlags.add("saferMainQueueSyncDispatch")
452+
saferMainQueueSyncDispatchCache = cached
453+
}
454+
return cached
455+
}
456+
446457
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
447458
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
448459
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<593b1d64dc31038140032a6b0a439700>>
7+
* @generated SignedSource<<d25fe7e78767e4088e6d541236953137>>
88
*/
99

1010
/**
@@ -97,6 +97,8 @@ public interface ReactNativeFeatureFlagsProvider {
9797

9898
@DoNotStrip public fun incorporateMaxLinesDuringAndroidLayout(): Boolean
9999

100+
@DoNotStrip public fun saferMainQueueSyncDispatch(): Boolean
101+
100102
@DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
101103

102104
@DoNotStrip public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<d74dc8182a14fddbd7118149298515bd>>
7+
* @generated SignedSource<<5fb05a9e1d08a9cc92d315154db3db08>>
88
*/
99

1010
/**
@@ -261,6 +261,12 @@ class ReactNativeFeatureFlagsJavaProvider
261261
return method(javaProvider_);
262262
}
263263

264+
bool saferMainQueueSyncDispatch() override {
265+
static const auto method =
266+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("saferMainQueueSyncDispatch");
267+
return method(javaProvider_);
268+
}
269+
264270
bool traceTurboModulePromiseRejectionsOnAndroid() override {
265271
static const auto method =
266272
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("traceTurboModulePromiseRejectionsOnAndroid");
@@ -516,6 +522,11 @@ bool JReactNativeFeatureFlagsCxxInterop::incorporateMaxLinesDuringAndroidLayout(
516522
return ReactNativeFeatureFlags::incorporateMaxLinesDuringAndroidLayout();
517523
}
518524

525+
bool JReactNativeFeatureFlagsCxxInterop::saferMainQueueSyncDispatch(
526+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
527+
return ReactNativeFeatureFlags::saferMainQueueSyncDispatch();
528+
}
529+
519530
bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid(
520531
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
521532
return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid();
@@ -713,6 +724,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
713724
makeNativeMethod(
714725
"incorporateMaxLinesDuringAndroidLayout",
715726
JReactNativeFeatureFlagsCxxInterop::incorporateMaxLinesDuringAndroidLayout),
727+
makeNativeMethod(
728+
"saferMainQueueSyncDispatch",
729+
JReactNativeFeatureFlagsCxxInterop::saferMainQueueSyncDispatch),
716730
makeNativeMethod(
717731
"traceTurboModulePromiseRejectionsOnAndroid",
718732
JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid),

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<a5ab0022f6a01bd6e929d36d9d87db10>>
7+
* @generated SignedSource<<d09a95dbb9417485fc538fc329c4ebd9>>
88
*/
99

1010
/**
@@ -141,6 +141,9 @@ class JReactNativeFeatureFlagsCxxInterop
141141
static bool incorporateMaxLinesDuringAndroidLayout(
142142
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
143143

144+
static bool saferMainQueueSyncDispatch(
145+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
146+
144147
static bool traceTurboModulePromiseRejectionsOnAndroid(
145148
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
146149

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<88fdbea2f97f628187164a47a9737da0>>
7+
* @generated SignedSource<<27d178af8531a2726bef81d8bb0ccbb1>>
88
*/
99

1010
/**
@@ -174,6 +174,10 @@ bool ReactNativeFeatureFlags::incorporateMaxLinesDuringAndroidLayout() {
174174
return getAccessor().incorporateMaxLinesDuringAndroidLayout();
175175
}
176176

177+
bool ReactNativeFeatureFlags::saferMainQueueSyncDispatch() {
178+
return getAccessor().saferMainQueueSyncDispatch();
179+
}
180+
177181
bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() {
178182
return getAccessor().traceTurboModulePromiseRejectionsOnAndroid();
179183
}

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<a4123bc6f44835c022a1a5238908674c>>
7+
* @generated SignedSource<<a0756c45d8e968e76f5d36393840ca42>>
88
*/
99

1010
/**
@@ -224,6 +224,11 @@ class ReactNativeFeatureFlags {
224224
*/
225225
RN_EXPORT static bool incorporateMaxLinesDuringAndroidLayout();
226226

227+
/**
228+
* Main queue sync dispatch that won't deadlock with sync render/events
229+
*/
230+
RN_EXPORT static bool saferMainQueueSyncDispatch();
231+
227232
/**
228233
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
229234
*/

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<0c768a58bd488ed1f3061cb4c13ef78d>>
7+
* @generated SignedSource<<0e5cf9a97bbd8675555bf2cb4c10cc08>>
88
*/
99

1010
/**
@@ -695,6 +695,24 @@ bool ReactNativeFeatureFlagsAccessor::incorporateMaxLinesDuringAndroidLayout() {
695695
return flagValue.value();
696696
}
697697

698+
bool ReactNativeFeatureFlagsAccessor::saferMainQueueSyncDispatch() {
699+
auto flagValue = saferMainQueueSyncDispatch_.load();
700+
701+
if (!flagValue.has_value()) {
702+
// This block is not exclusive but it is not necessary.
703+
// If multiple threads try to initialize the feature flag, we would only
704+
// be accessing the provider multiple times but the end state of this
705+
// instance and the returned flag value would be the same.
706+
707+
markFlagAsAccessed(37, "saferMainQueueSyncDispatch");
708+
709+
flagValue = currentProvider_->saferMainQueueSyncDispatch();
710+
saferMainQueueSyncDispatch_ = flagValue;
711+
}
712+
713+
return flagValue.value();
714+
}
715+
698716
bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid() {
699717
auto flagValue = traceTurboModulePromiseRejectionsOnAndroid_.load();
700718

@@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
704722
// be accessing the provider multiple times but the end state of this
705723
// instance and the returned flag value would be the same.
706724

707-
markFlagAsAccessed(37, "traceTurboModulePromiseRejectionsOnAndroid");
725+
markFlagAsAccessed(38, "traceTurboModulePromiseRejectionsOnAndroid");
708726

709727
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
710728
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit(
722740
// be accessing the provider multiple times but the end state of this
723741
// instance and the returned flag value would be the same.
724742

725-
markFlagAsAccessed(38, "updateRuntimeShadowNodeReferencesOnCommit");
743+
markFlagAsAccessed(39, "updateRuntimeShadowNodeReferencesOnCommit");
726744

727745
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
728746
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
740758
// be accessing the provider multiple times but the end state of this
741759
// instance and the returned flag value would be the same.
742760

743-
markFlagAsAccessed(39, "useAlwaysAvailableJSErrorHandling");
761+
markFlagAsAccessed(40, "useAlwaysAvailableJSErrorHandling");
744762

745763
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
746764
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::useAndroidTextLayoutWidthDirectly() {
758776
// be accessing the provider multiple times but the end state of this
759777
// instance and the returned flag value would be the same.
760778

761-
markFlagAsAccessed(40, "useAndroidTextLayoutWidthDirectly");
779+
markFlagAsAccessed(41, "useAndroidTextLayoutWidthDirectly");
762780

763781
flagValue = currentProvider_->useAndroidTextLayoutWidthDirectly();
764782
useAndroidTextLayoutWidthDirectly_ = flagValue;
@@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
776794
// be accessing the provider multiple times but the end state of this
777795
// instance and the returned flag value would be the same.
778796

779-
markFlagAsAccessed(41, "useFabricInterop");
797+
markFlagAsAccessed(42, "useFabricInterop");
780798

781799
flagValue = currentProvider_->useFabricInterop();
782800
useFabricInterop_ = flagValue;
@@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
794812
// be accessing the provider multiple times but the end state of this
795813
// instance and the returned flag value would be the same.
796814

797-
markFlagAsAccessed(42, "useNativeViewConfigsInBridgelessMode");
815+
markFlagAsAccessed(43, "useNativeViewConfigsInBridgelessMode");
798816

799817
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
800818
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
812830
// be accessing the provider multiple times but the end state of this
813831
// instance and the returned flag value would be the same.
814832

815-
markFlagAsAccessed(43, "useOptimizedEventBatchingOnAndroid");
833+
markFlagAsAccessed(44, "useOptimizedEventBatchingOnAndroid");
816834

817835
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
818836
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
830848
// be accessing the provider multiple times but the end state of this
831849
// instance and the returned flag value would be the same.
832850

833-
markFlagAsAccessed(44, "useRawPropsJsiValue");
851+
markFlagAsAccessed(45, "useRawPropsJsiValue");
834852

835853
flagValue = currentProvider_->useRawPropsJsiValue();
836854
useRawPropsJsiValue_ = flagValue;
@@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
848866
// be accessing the provider multiple times but the end state of this
849867
// instance and the returned flag value would be the same.
850868

851-
markFlagAsAccessed(45, "useShadowNodeStateOnClone");
869+
markFlagAsAccessed(46, "useShadowNodeStateOnClone");
852870

853871
flagValue = currentProvider_->useShadowNodeStateOnClone();
854872
useShadowNodeStateOnClone_ = flagValue;
@@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
866884
// be accessing the provider multiple times but the end state of this
867885
// instance and the returned flag value would be the same.
868886

869-
markFlagAsAccessed(46, "useTurboModuleInterop");
887+
markFlagAsAccessed(47, "useTurboModuleInterop");
870888

871889
flagValue = currentProvider_->useTurboModuleInterop();
872890
useTurboModuleInterop_ = flagValue;
@@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
884902
// be accessing the provider multiple times but the end state of this
885903
// instance and the returned flag value would be the same.
886904

887-
markFlagAsAccessed(47, "useTurboModules");
905+
markFlagAsAccessed(48, "useTurboModules");
888906

889907
flagValue = currentProvider_->useTurboModules();
890908
useTurboModules_ = flagValue;

0 commit comments

Comments
 (0)