diff --git a/packages/react-native/Libraries/Core/setUpTimers.js b/packages/react-native/Libraries/Core/setUpTimers.js index 87704779078..7dc59d9e1fb 100644 --- a/packages/react-native/Libraries/Core/setUpTimers.js +++ b/packages/react-native/Libraries/Core/setUpTimers.js @@ -10,6 +10,9 @@ 'use strict'; +const ReactNativeFeatureFlags = require('../../src/private/featureflags/ReactNativeFeatureFlags'); +const NativeReactNativeFeatureFlags = + require('../../src/private/featureflags/specs/NativeReactNativeFeatureFlags').default; const {polyfillGlobal} = require('../Utilities/PolyfillFunctions'); if (__DEV__) { @@ -18,46 +21,19 @@ if (__DEV__) { } } +const isEventLoopEnabled = (() => { + if (NativeReactNativeFeatureFlags == null) { + return false; + } + + return ( + ReactNativeFeatureFlags.enableBridgelessArchitecture() && + !ReactNativeFeatureFlags.disableEventLoopOnBridgeless() + ); +})(); + // In bridgeless mode, timers are host functions installed from cpp. -if (global.RN$Bridgeless === true) { - polyfillGlobal( - 'requestIdleCallback', - () => - require('../../src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks') - .default.requestIdleCallback, - ); - - polyfillGlobal( - 'cancelIdleCallback', - () => - require('../../src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks') - .default.cancelIdleCallback, - ); - - // This is the flag that tells React to use `queueMicrotask` to batch state - // updates, instead of using the scheduler to schedule a regular task. - // We use a global variable because we don't currently have any other - // mechanism to pass feature flags from RN to React in OSS. - global.RN$enableMicrotasksInReact = true; - - polyfillGlobal( - 'queueMicrotask', - () => - require('../../src/private/webapis/microtasks/specs/NativeMicrotasks') - .default.queueMicrotask, - ); - - // We shim the immediate APIs via `queueMicrotask` to maintain the backward - // compatibility. - polyfillGlobal( - 'setImmediate', - () => require('./Timers/immediateShim').setImmediate, - ); - polyfillGlobal( - 'clearImmediate', - () => require('./Timers/immediateShim').clearImmediate, - ); -} else { +if (global.RN$Bridgeless !== true) { /** * Set up timers. * You can use this module directly, or just require InitializeCore. @@ -83,7 +59,50 @@ if (global.RN$Bridgeless === true) { defineLazyTimer('cancelAnimationFrame'); defineLazyTimer('requestIdleCallback'); defineLazyTimer('cancelIdleCallback'); +} else if (isEventLoopEnabled) { + polyfillGlobal( + 'requestIdleCallback', + () => + require('../../src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks') + .default.requestIdleCallback, + ); + polyfillGlobal( + 'cancelIdleCallback', + () => + require('../../src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks') + .default.cancelIdleCallback, + ); +} + +// We need to check if the native module is available before accessing the +// feature flag, because otherwise the API would throw an error in the legacy +// architecture in OSS, where the native module isn't available. +if (isEventLoopEnabled) { + // This is the flag that tells React to use `queueMicrotask` to batch state + // updates, instead of using the scheduler to schedule a regular task. + // We use a global variable because we don't currently have any other + // mechanism to pass feature flags from RN to React in OSS. + global.RN$enableMicrotasksInReact = true; + + polyfillGlobal( + 'queueMicrotask', + () => + require('../../src/private/webapis/microtasks/specs/NativeMicrotasks') + .default.queueMicrotask, + ); + + // We shim the immediate APIs via `queueMicrotask` to maintain the backward + // compatibility. + polyfillGlobal( + 'setImmediate', + () => require('./Timers/immediateShim').setImmediate, + ); + polyfillGlobal( + 'clearImmediate', + () => require('./Timers/immediateShim').clearImmediate, + ); +} else { // Polyfill it with promise (regardless it's polyfilled or native) otherwise. polyfillGlobal( 'queueMicrotask', @@ -93,12 +112,14 @@ if (global.RN$Bridgeless === true) { // When promise was polyfilled hence is queued to the RN microtask queue, // we polyfill the immediate APIs as aliases to the ReactNativeMicrotask APIs. // Note that in bridgeless mode, immediate APIs are installed from cpp. - polyfillGlobal( - 'setImmediate', - () => require('./Timers/JSTimers').queueReactNativeMicrotask, - ); - polyfillGlobal( - 'clearImmediate', - () => require('./Timers/JSTimers').clearReactNativeMicrotask, - ); + if (global.RN$Bridgeless !== true) { + polyfillGlobal( + 'setImmediate', + () => require('./Timers/JSTimers').queueReactNativeMicrotask, + ); + polyfillGlobal( + 'clearImmediate', + () => require('./Timers/JSTimers').clearReactNativeMicrotask, + ); + } } diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index b7bf397faf5..9659db98d6a 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9e4ac7a270dd74f6978374d8e2c05013>> + * @generated SignedSource<<70ce0689b8452f99877fa46b9f6b6215>> */ /** @@ -54,6 +54,13 @@ bool NativeReactNativeFeatureFlags::completeReactInstanceCreationOnBgThreadOnAnd return ReactNativeFeatureFlags::completeReactInstanceCreationOnBgThreadOnAndroid(); } +bool NativeReactNativeFeatureFlags::disableEventLoopOnBridgeless( + jsi::Runtime& /*runtime*/) { + // This flag is configured with `skipNativeAPI: true`. + // TODO(T204838867): Implement support for optional methods in C++ TM codegen and remove the method definition altogether. + return false; +} + bool NativeReactNativeFeatureFlags::disableMountItemReorderingAndroid( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::disableMountItemReorderingAndroid(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 3d7d7cc4095..37520742510 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<3aadd0d890f3dac9fee5bc465d759203>> */ /** @@ -41,6 +41,8 @@ class NativeReactNativeFeatureFlags bool completeReactInstanceCreationOnBgThreadOnAndroid(jsi::Runtime& runtime); + bool disableEventLoopOnBridgeless(jsi::Runtime& runtime); + bool disableMountItemReorderingAndroid(jsi::Runtime& runtime); bool enableAccumulatedUpdatesInRawPropsAndroid(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 4e757703050..c1ddd41d73b 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -66,6 +66,16 @@ const definitions: FeatureFlagDefinitions = { purpose: 'release', }, }, + disableEventLoopOnBridgeless: { + defaultValue: false, + metadata: { + description: + 'The bridgeless architecture enables the event loop by default. This feature flag allows us to force disabling it in specific instances.', + expectedReleaseValue: true, + purpose: 'release', + }, + skipNativeAPI: true, + }, disableMountItemReorderingAndroid: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 64e4d92a8bb..80a5cbd0f22 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<811a054f0ada56d8cc1ed21c6930fc52>> + * @generated SignedSource<<43c428552b2f180bccddcfe62fc36697>> * @flow strict */ @@ -49,6 +49,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ commonTestFlag: Getter, commonTestFlagWithoutNativeImplementation: Getter, completeReactInstanceCreationOnBgThreadOnAndroid: Getter, + disableEventLoopOnBridgeless: Getter, disableMountItemReorderingAndroid: Getter, enableAccumulatedUpdatesInRawPropsAndroid: Getter, enableBridgelessArchitecture: Getter, @@ -172,6 +173,10 @@ export const commonTestFlagWithoutNativeImplementation: Getter = create * Do not wait for a main-thread dispatch to complete init to start executing work on the JS thread on Android */ export const completeReactInstanceCreationOnBgThreadOnAndroid: Getter = createNativeFlagGetter('completeReactInstanceCreationOnBgThreadOnAndroid', true); +/** + * The bridgeless architecture enables the event loop by default. This feature flag allows us to force disabling it in specific instances. + */ +export const disableEventLoopOnBridgeless: Getter = createNativeFlagGetter('disableEventLoopOnBridgeless', false); /** * Prevent FabricMountingManager from reordering mountitems, which may lead to invalid state on the UI thread */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index f484ac9ec45..a8be93684d4 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<2dbeae49bc79bdbbf06c54273b2e2b9b>> * @flow strict */ @@ -26,6 +26,7 @@ export interface Spec extends TurboModule { +commonTestFlag?: () => boolean; +commonTestFlagWithoutNativeImplementation?: () => boolean; +completeReactInstanceCreationOnBgThreadOnAndroid?: () => boolean; + +disableEventLoopOnBridgeless?: () => boolean; +disableMountItemReorderingAndroid?: () => boolean; +enableAccumulatedUpdatesInRawPropsAndroid?: () => boolean; +enableBridgelessArchitecture?: () => boolean;