Bridgeless: Implement main queue setup api in turbo modules (#50040)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50040

This diff implements main queue module setup.

Sometimes, people need to capture uikit things, and use them from javascript. In those cases, people can write main queue modules. These modules will be eagerly initialized on the main queue, during react native init.

## On Necessity
**Sync** dispatches to the main thread from the js thread can deadlock react native. And **async** dispatches to the main thread from the js thread sometimes might not be enough: it could lead to flickery rendering. So, we need to allow people to capture ui thread things, before any js executes.

## Caveat
This api is dangerous and discouraged. All react native surfaces will pay the cost of one surface introducing a main queue module. It could also slow down common/critical interactions in your app, if you're not careful.

We will introduce performance logging for this infrastructure. So that we can monitor and file tasks, when main queue module init starts taking "too long."

Changelog: [General][Breaking]: Introduce beforeload callback arg into ReactInstance::loadScript

Reviewed By: mdvacca

Differential Revision: D71084243

fbshipit-source-id: 8fdb84761ac69468afc428f4f79eff6322449e3c
This commit is contained in:
Ramanpreet Nara
2025-03-19 21:16:05 -07:00
committed by Facebook GitHub Bot
parent 636665c1c2
commit 061174c150
25 changed files with 217 additions and 56 deletions
@@ -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<<df008a78acc9b0104c54e06849d5e9b9>>
* @generated SignedSource<<49a61b2330d748fc05bd5e8ce042bf72>>
*/
/**
@@ -118,6 +118,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableLongTaskAPI(): Boolean = accessor.enableLongTaskAPI()
/**
* Makes modules requiring main queue setup initialize on the main thread, during React Native init.
*/
@JvmStatic
public fun enableMainQueueModulesOnIOS(): Boolean = accessor.enableMainQueueModulesOnIOS()
/**
* Parse CSS strings using the Fabric CSS parser instead of ViewConfig processing
*/
@@ -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<<9813fd15c7f54d69d3de6084f733c3a6>>
* @generated SignedSource<<86fe1d1217f974eb9cbe2fc6b556b6f3>>
*/
/**
@@ -35,6 +35,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
private var enableLongTaskAPICache: Boolean? = null
private var enableMainQueueModulesOnIOSCache: Boolean? = null
private var enableNativeCSSParsingCache: Boolean? = null
private var enableNewBackgroundAndBorderDrawablesCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
@@ -198,6 +199,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableMainQueueModulesOnIOS(): Boolean {
var cached = enableMainQueueModulesOnIOSCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableMainQueueModulesOnIOS()
enableMainQueueModulesOnIOSCache = cached
}
return cached
}
override fun enableNativeCSSParsing(): Boolean {
var cached = enableNativeCSSParsingCache
if (cached == null) {
@@ -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<<06c5ef75b624d953e7a8d9297feaf816>>
* @generated SignedSource<<e77961bfb13e7465783760d67c60c698>>
*/
/**
@@ -58,6 +58,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableLongTaskAPI(): Boolean
@DoNotStrip @JvmStatic public external fun enableMainQueueModulesOnIOS(): Boolean
@DoNotStrip @JvmStatic public external fun enableNativeCSSParsing(): Boolean
@DoNotStrip @JvmStatic public external fun enableNewBackgroundAndBorderDrawables(): Boolean
@@ -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<<cbe1d77424b561149b0bda39955dffac>>
* @generated SignedSource<<f6242ae2989dd2da5ad73861933787f2>>
*/
/**
@@ -53,6 +53,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableLongTaskAPI(): Boolean = false
override fun enableMainQueueModulesOnIOS(): Boolean = false
override fun enableNativeCSSParsing(): Boolean = false
override fun enableNewBackgroundAndBorderDrawables(): Boolean = false
@@ -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<<0fb76e329360157ea9772a0d8bebc873>>
* @generated SignedSource<<5fc2a365b51c5e14f99ec1d3b162f4e3>>
*/
/**
@@ -39,6 +39,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
private var enableLayoutAnimationsOnIOSCache: Boolean? = null
private var enableLongTaskAPICache: Boolean? = null
private var enableMainQueueModulesOnIOSCache: Boolean? = null
private var enableNativeCSSParsingCache: Boolean? = null
private var enableNewBackgroundAndBorderDrawablesCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
@@ -217,6 +218,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableMainQueueModulesOnIOS(): Boolean {
var cached = enableMainQueueModulesOnIOSCache
if (cached == null) {
cached = currentProvider.enableMainQueueModulesOnIOS()
accessedFeatureFlags.add("enableMainQueueModulesOnIOS")
enableMainQueueModulesOnIOSCache = cached
}
return cached
}
override fun enableNativeCSSParsing(): Boolean {
var cached = enableNativeCSSParsingCache
if (cached == null) {
@@ -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<<0c16f7af743a6c4d7aaabf010c1c7327>>
* @generated SignedSource<<1db9978c9096c9775fd7cd7d37637c8c>>
*/
/**
@@ -53,6 +53,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableLongTaskAPI(): Boolean
@DoNotStrip public fun enableMainQueueModulesOnIOS(): Boolean
@DoNotStrip public fun enableNativeCSSParsing(): Boolean
@DoNotStrip public fun enableNewBackgroundAndBorderDrawables(): Boolean
@@ -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<<c866e69278d249dd79cb2fc193d1a7e2>>
* @generated SignedSource<<696c541ad3725d2d39eaaf8b9b4a317c>>
*/
/**
@@ -129,6 +129,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool enableMainQueueModulesOnIOS() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableMainQueueModulesOnIOS");
return method(javaProvider_);
}
bool enableNativeCSSParsing() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableNativeCSSParsing");
@@ -370,6 +376,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableLongTaskAPI(
return ReactNativeFeatureFlags::enableLongTaskAPI();
}
bool JReactNativeFeatureFlagsCxxInterop::enableMainQueueModulesOnIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableMainQueueModulesOnIOS();
}
bool JReactNativeFeatureFlagsCxxInterop::enableNativeCSSParsing(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableNativeCSSParsing();
@@ -581,6 +592,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableLongTaskAPI",
JReactNativeFeatureFlagsCxxInterop::enableLongTaskAPI),
makeNativeMethod(
"enableMainQueueModulesOnIOS",
JReactNativeFeatureFlagsCxxInterop::enableMainQueueModulesOnIOS),
makeNativeMethod(
"enableNativeCSSParsing",
JReactNativeFeatureFlagsCxxInterop::enableNativeCSSParsing),
@@ -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<<ee83dc14463351d400cd75624c803176>>
* @generated SignedSource<<e66d1a86ca5ae4c2ce4ec7ca20390edb>>
*/
/**
@@ -75,6 +75,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableLongTaskAPI(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableMainQueueModulesOnIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableNativeCSSParsing(
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<<4d47f61435ba3a74e84752a02def81c2>>
* @generated SignedSource<<07e6cf68cec4f7c7d61de4958b11ff4f>>
*/
/**
@@ -86,6 +86,10 @@ bool ReactNativeFeatureFlags::enableLongTaskAPI() {
return getAccessor().enableLongTaskAPI();
}
bool ReactNativeFeatureFlags::enableMainQueueModulesOnIOS() {
return getAccessor().enableMainQueueModulesOnIOS();
}
bool ReactNativeFeatureFlags::enableNativeCSSParsing() {
return getAccessor().enableNativeCSSParsing();
}
@@ -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<<29cdbefbe02064df4fb65388e64f95a4>>
* @generated SignedSource<<ef74757e57be86fbc860f5fd02973885>>
*/
/**
@@ -114,6 +114,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableLongTaskAPI();
/**
* Makes modules requiring main queue setup initialize on the main thread, during React Native init.
*/
RN_EXPORT static bool enableMainQueueModulesOnIOS();
/**
* Parse CSS strings using the Fabric CSS parser instead of ViewConfig processing
*/
@@ -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<<353677101629176192534f1f234e2de4>>
* @generated SignedSource<<40daa9d9fc429b26ec6c178a6d9f6c5d>>
*/
/**
@@ -299,6 +299,24 @@ bool ReactNativeFeatureFlagsAccessor::enableLongTaskAPI() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableMainQueueModulesOnIOS() {
auto flagValue = enableMainQueueModulesOnIOS_.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(15, "enableMainQueueModulesOnIOS");
flagValue = currentProvider_->enableMainQueueModulesOnIOS();
enableMainQueueModulesOnIOS_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() {
auto flagValue = enableNativeCSSParsing_.load();
@@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(15, "enableNativeCSSParsing");
markFlagAsAccessed(16, "enableNativeCSSParsing");
flagValue = currentProvider_->enableNativeCSSParsing();
enableNativeCSSParsing_ = flagValue;
@@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(16, "enableNewBackgroundAndBorderDrawables");
markFlagAsAccessed(17, "enableNewBackgroundAndBorderDrawables");
flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables();
enableNewBackgroundAndBorderDrawables_ = flagValue;
@@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(17, "enablePropsUpdateReconciliationAndroid");
markFlagAsAccessed(18, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::enableReportEventPaintTime() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(18, "enableReportEventPaintTime");
markFlagAsAccessed(19, "enableReportEventPaintTime");
flagValue = currentProvider_->enableReportEventPaintTime();
enableReportEventPaintTime_ = flagValue;
@@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(19, "enableSynchronousStateUpdates");
markFlagAsAccessed(20, "enableSynchronousStateUpdates");
flagValue = currentProvider_->enableSynchronousStateUpdates();
enableSynchronousStateUpdates_ = flagValue;
@@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableUIConsistency() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(20, "enableUIConsistency");
markFlagAsAccessed(21, "enableUIConsistency");
flagValue = currentProvider_->enableUIConsistency();
enableUIConsistency_ = flagValue;
@@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(21, "enableViewCulling");
markFlagAsAccessed(22, "enableViewCulling");
flagValue = currentProvider_->enableViewCulling();
enableViewCulling_ = flagValue;
@@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(22, "enableViewRecycling");
markFlagAsAccessed(23, "enableViewRecycling");
flagValue = currentProvider_->enableViewRecycling();
enableViewRecycling_ = flagValue;
@@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(23, "enableViewRecyclingForText");
markFlagAsAccessed(24, "enableViewRecyclingForText");
flagValue = currentProvider_->enableViewRecyclingForText();
enableViewRecyclingForText_ = flagValue;
@@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(24, "enableViewRecyclingForView");
markFlagAsAccessed(25, "enableViewRecyclingForView");
flagValue = currentProvider_->enableViewRecyclingForView();
enableViewRecyclingForView_ = flagValue;
@@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorEmittingUpdatesWithWrongP
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(25, "fixDifferentiatorEmittingUpdatesWithWrongParentTag");
markFlagAsAccessed(26, "fixDifferentiatorEmittingUpdatesWithWrongParentTag");
flagValue = currentProvider_->fixDifferentiatorEmittingUpdatesWithWrongParentTag();
fixDifferentiatorEmittingUpdatesWithWrongParentTag_ = flagValue;
@@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(26, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(27, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMountingCoordinatorReportedPendingTrans
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(27, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid");
markFlagAsAccessed(28, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid");
flagValue = currentProvider_->fixMountingCoordinatorReportedPendingTransactionsOnAndroid();
fixMountingCoordinatorReportedPendingTransactionsOnAndroid_ = flagValue;
@@ -542,7 +560,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(28, "fuseboxEnabledRelease");
markFlagAsAccessed(29, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(29, "fuseboxNetworkInspectionEnabled");
markFlagAsAccessed(30, "fuseboxNetworkInspectionEnabled");
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
fuseboxNetworkInspectionEnabled_ = flagValue;
@@ -578,7 +596,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(30, "lazyAnimationCallbacks");
markFlagAsAccessed(31, "lazyAnimationCallbacks");
flagValue = currentProvider_->lazyAnimationCallbacks();
lazyAnimationCallbacks_ = flagValue;
@@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::removeTurboModuleManagerDelegateMutex() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(31, "removeTurboModuleManagerDelegateMutex");
markFlagAsAccessed(32, "removeTurboModuleManagerDelegateMutex");
flagValue = currentProvider_->removeTurboModuleManagerDelegateMutex();
removeTurboModuleManagerDelegateMutex_ = flagValue;
@@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::throwExceptionInsteadOfDeadlockOnTurboModu
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(32, "throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS");
markFlagAsAccessed(33, "throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS");
flagValue = currentProvider_->throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS();
throwExceptionInsteadOfDeadlockOnTurboModuleSetupDuringSyncRenderIOS_ = flagValue;
@@ -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,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<<00f623d5f3920895eb2c1b530dce6a7d>>
* @generated SignedSource<<54364d504177fce95cc2cbbc2b55cb65>>
*/
/**
@@ -47,6 +47,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableLayoutAnimationsOnAndroid();
bool enableLayoutAnimationsOnIOS();
bool enableLongTaskAPI();
bool enableMainQueueModulesOnIOS();
bool enableNativeCSSParsing();
bool enableNewBackgroundAndBorderDrawables();
bool enablePropsUpdateReconciliationAndroid();
@@ -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>> animatedShouldSignalBatch_;
@@ -102,6 +103,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableLayoutAnimationsOnAndroid_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnIOS_;
std::atomic<std::optional<bool>> enableLongTaskAPI_;
std::atomic<std::optional<bool>> enableMainQueueModulesOnIOS_;
std::atomic<std::optional<bool>> enableNativeCSSParsing_;
std::atomic<std::optional<bool>> enableNewBackgroundAndBorderDrawables_;
std::atomic<std::optional<bool>> enablePropsUpdateReconciliationAndroid_;
@@ -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<<140e6580100dab5806194e780610f122>>
* @generated SignedSource<<e3a8b03ca156d28f227f00c5507adb5f>>
*/
/**
@@ -87,6 +87,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableMainQueueModulesOnIOS() override {
return false;
}
bool enableNativeCSSParsing() override {
return false;
}
@@ -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<<b8be2ef692b468cdfee6d88c091abec3>>
* @generated SignedSource<<28915a06a3c24b532384b481cc91d863>>
*/
/**
@@ -180,6 +180,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableLongTaskAPI();
}
bool enableMainQueueModulesOnIOS() override {
auto value = values_["enableMainQueueModulesOnIOS"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableMainQueueModulesOnIOS();
}
bool enableNativeCSSParsing() override {
auto value = values_["enableNativeCSSParsing"];
if (!value.isNull()) {
@@ -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<<f694a97b02b01d4b56b5eac7f2a348e5>>
* @generated SignedSource<<53c858fa9ec8c4e3b9849f7513fdb441>>
*/
/**
@@ -100,6 +100,8 @@ class ReactNativeFeatureFlagsOverridesOSSCanary : public ReactNativeFeatureFlags
@@ -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<<ddf338308d0390152ddbec172c0ad40e>>
* @generated SignedSource<<c19b9e1f879cc986fe114829c3a79df5>>
*/
/**
@@ -107,6 +107,8 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
@@ -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<<0845c9b1618ac1e18ce4aeb5d03a70f5>>
* @generated SignedSource<<3d6eb54a5f64b0d5c72dae44e669052e>>
*/
/**
@@ -40,6 +40,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableLayoutAnimationsOnAndroid() = 0;
virtual bool enableLayoutAnimationsOnIOS() = 0;
virtual bool enableLongTaskAPI() = 0;
virtual bool enableMainQueueModulesOnIOS() = 0;
virtual bool enableNativeCSSParsing() = 0;
virtual bool enableNewBackgroundAndBorderDrawables() = 0;
virtual bool enablePropsUpdateReconciliationAndroid() = 0;
@@ -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<<7493403280d2ac7ec348eb3d78d915c2>>
* @generated SignedSource<<d6f2e867002cef542b6915dc4b8a7131>>
*/
/**
@@ -119,6 +119,11 @@ bool NativeReactNativeFeatureFlags::enableLongTaskAPI(
return ReactNativeFeatureFlags::enableLongTaskAPI();
}
bool NativeReactNativeFeatureFlags::enableMainQueueModulesOnIOS(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableMainQueueModulesOnIOS();
}
bool NativeReactNativeFeatureFlags::enableNativeCSSParsing(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableNativeCSSParsing();
@@ -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<<36b55f5cfd86d9aaad26c760dec7c875>>
* @generated SignedSource<<a1d9ca51c1318434affebf7e01a3cd39>>
*/
/**
@@ -67,6 +67,8 @@ class NativeReactNativeFeatureFlags
bool enableLongTaskAPI(jsi::Runtime& runtime);
bool enableMainQueueModulesOnIOS(jsi::Runtime& runtime);
bool enableNativeCSSParsing(jsi::Runtime& runtime);
bool enableNewBackgroundAndBorderDrawables(jsi::Runtime& runtime);
@@ -215,7 +215,8 @@ std::string simpleBasename(const std::string& path) {
void ReactInstance::loadScript(
std::unique_ptr<const JSBigString> script,
const std::string& sourceURL,
std::function<void(jsi::Runtime& runtime)>&& completion) {
std::function<void(jsi::Runtime& runtime)>&& beforeLoad,
std::function<void(jsi::Runtime& runtime)>&& afterLoad) {
auto buffer = std::make_shared<BigStringBuffer>(std::move(script));
std::string scriptName = simpleBasename(sourceURL);
@@ -226,7 +227,11 @@ void ReactInstance::loadScript(
weakBufferedRuntimeExecuter =
std::weak_ptr<BufferedRuntimeExecutor>(
bufferedRuntimeExecutor_),
completion](jsi::Runtime& runtime) {
beforeLoad,
afterLoad](jsi::Runtime& runtime) {
if (beforeLoad) {
beforeLoad(runtime);
}
TraceSection s("ReactInstance::loadScript");
bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);
if (hasLogger) {
@@ -255,8 +260,8 @@ void ReactInstance::loadScript(
weakBufferedRuntimeExecuter.lock()) {
strongBufferedRuntimeExecuter->flush();
}
if (completion) {
completion(runtime);
if (afterLoad) {
afterLoad(runtime);
}
});
}
@@ -50,7 +50,8 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
void loadScript(
std::unique_ptr<const JSBigString> script,
const std::string& sourceURL,
std::function<void(jsi::Runtime& runtime)>&& completion = nullptr);
std::function<void(jsi::Runtime& runtime)>&& beforeLoad = nullptr,
std::function<void(jsi::Runtime& runtime)>&& afterLoad = nullptr);
void registerSegment(uint32_t segmentId, const std::string& segmentPath);
@@ -85,6 +85,7 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
std::atomic<bool> _valid;
RCTJSThreadManager *_jsThreadManager;
NSDictionary *_launchOptions;
void (^_waitUntilModuleSetupComplete)();
// APIs supporting interop with native modules and view managers
RCTBridgeModuleDecorator *_bridgeModuleDecorator;
@@ -314,6 +315,33 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
// Initialize RCTModuleRegistry so that TurboModules can require other TurboModules.
[_bridgeModuleDecorator.moduleRegistry setTurboModuleRegistry:_turboModuleManager];
if (ReactNativeFeatureFlags::enableMainQueueModulesOnIOS()) {
/**
* Some native modules need to capture uikit objects on the main thread.
* Start initializing those modules on the main queue here. The JavaScript thread
* will wait until this module init finishes, before executing the js bundle.
*/
NSArray<NSString *> *modulesRequiringMainQueueSetup = [_delegate unstableModulesRequiringMainQueueSetup];
if ([modulesRequiringMainQueueSetup count] > 0) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
_waitUntilModuleSetupComplete = ^{
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC))) {
RCTLogError(
@"Timed out waiting for native modules to be setup on the main queue: %@",
modulesRequiringMainQueueSetup);
};
};
// TODO(T218039767): Integrate perf logging into main queue module init
RCTExecuteOnMainQueue(^{
for (NSString *moduleName in modulesRequiringMainQueueSetup) {
[self->_bridgeModuleDecorator.moduleRegistry moduleForName:[moduleName UTF8String]];
}
dispatch_semaphore_signal(semaphore);
});
}
}
RCTLogSetBridgelessModuleRegistry(_bridgeModuleDecorator.moduleRegistry);
RCTLogSetBridgelessCallableJSModules(_bridgeModuleDecorator.callableJSModules);
@@ -496,9 +524,16 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
auto script = std::make_unique<NSDataBigString>(source.data);
const auto *url = deriveSourceURL(source.url).UTF8String;
_reactInstance->loadScript(std::move(script), url, [](jsi::Runtime &_) {
auto beforeLoad = [waitUntilModuleSetupComplete = self->_waitUntilModuleSetupComplete](jsi::Runtime &_) {
if (waitUntilModuleSetupComplete) {
waitUntilModuleSetupComplete();
}
};
auto afterLoad = [](jsi::Runtime &_) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil];
});
};
_reactInstance->loadScript(std::move(script), url, beforeLoad, afterLoad);
}
- (void)_handleJSError:(const JsErrorHandler::ProcessedError &)error withRuntime:(jsi::Runtime &)runtime
@@ -202,6 +202,16 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
enableMainQueueModulesOnIOS: {
defaultValue: false,
metadata: {
description:
'Makes modules requiring main queue setup initialize on the main thread, during React Native init.',
expectedReleaseValue: true,
purpose: 'release',
},
ossReleaseStage: 'none',
},
enableNativeCSSParsing: {
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<<e76d584cb67e2feae209795beb2009d6>>
* @generated SignedSource<<8e4f25dbb96b6c7a9fe645d67172d0ab>>
* @flow strict
*/
@@ -62,6 +62,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
enableLayoutAnimationsOnAndroid: Getter<boolean>,
enableLayoutAnimationsOnIOS: Getter<boolean>,
enableLongTaskAPI: Getter<boolean>,
enableMainQueueModulesOnIOS: Getter<boolean>,
enableNativeCSSParsing: Getter<boolean>,
enableNewBackgroundAndBorderDrawables: Getter<boolean>,
enablePropsUpdateReconciliationAndroid: Getter<boolean>,
@@ -220,6 +221,10 @@ export const enableLayoutAnimationsOnIOS: Getter<boolean> = createNativeFlagGett
* Enables the reporting of long tasks through `PerformanceObserver`. Only works if the event loop is enabled.
*/
export const enableLongTaskAPI: Getter<boolean> = createNativeFlagGetter('enableLongTaskAPI', false);
/**
* Makes modules requiring main queue setup initialize on the main thread, during React Native init.
*/
export const enableMainQueueModulesOnIOS: Getter<boolean> = createNativeFlagGetter('enableMainQueueModulesOnIOS', false);
/**
* Parse CSS strings using the Fabric CSS parser instead of ViewConfig processing
*/
@@ -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<<22d8e7623a2eee5182c786f2ec914401>>
* @generated SignedSource<<b305837c79b4099dad5dac1431ec27a0>>
* @flow strict
*/
@@ -39,6 +39,7 @@ export interface Spec extends TurboModule {
+enableLayoutAnimationsOnAndroid?: () => boolean;
+enableLayoutAnimationsOnIOS?: () => boolean;
+enableLongTaskAPI?: () => boolean;
+enableMainQueueModulesOnIOS?: () => boolean;
+enableNativeCSSParsing?: () => boolean;
+enableNewBackgroundAndBorderDrawables?: () => boolean;
+enablePropsUpdateReconciliationAndroid?: () => boolean;