diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 5e35b0404a32e..c2dc019d0234b 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -30,6 +30,7 @@ import { enableSchedulingProfiler, enableScopeAPI, skipUnmountedBoundaries, + disableSchedulerTimeoutInWorkLoop, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import invariant from 'shared/invariant'; @@ -750,7 +751,7 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { // This is the entry point for every concurrent task, i.e. anything that // goes through Scheduler. -function performConcurrentWorkOnRoot(root) { +function performConcurrentWorkOnRoot(root, didTimeout) { // Since we know we're in a React event, we can clear the current // event time. The next update will compute a new event time. currentEventTime = NoTimestamp; @@ -790,6 +791,18 @@ function performConcurrentWorkOnRoot(root) { return null; } + // TODO: We only check `didTimeout` defensively, to account for a Scheduler + // bug we're still investigating. Once the bug in Scheduler is fixed, + // we can remove this, since we track expiration ourselves. + if (!disableSchedulerTimeoutInWorkLoop && didTimeout) { + // Something expired. Flush synchronously until there's no expired + // work left. + markRootExpired(root, lanes); + // This will schedule a synchronous callback. + ensureRootIsScheduled(root, now()); + return null; + } + let exitStatus = renderRootConcurrent(root, lanes); if ( diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index a60e8a58f56da..97514adb03a99 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -30,6 +30,7 @@ import { enableSchedulingProfiler, enableScopeAPI, skipUnmountedBoundaries, + disableSchedulerTimeoutInWorkLoop, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import invariant from 'shared/invariant'; @@ -738,7 +739,7 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { // This is the entry point for every concurrent task, i.e. anything that // goes through Scheduler. -function performConcurrentWorkOnRoot(root) { +function performConcurrentWorkOnRoot(root, didTimeout) { // Since we know we're in a React event, we can clear the current // event time. The next update will compute a new event time. currentEventTime = NoTimestamp; @@ -778,6 +779,18 @@ function performConcurrentWorkOnRoot(root) { return null; } + // TODO: We only check `didTimeout` defensively, to account for a Scheduler + // bug we're still investigating. Once the bug in Scheduler is fixed, + // we can remove this, since we track expiration ourselves. + if (!disableSchedulerTimeoutInWorkLoop && didTimeout) { + // Something expired. Flush synchronously until there's no expired + // work left. + markRootExpired(root, lanes); + // This will schedule a synchronous callback. + ensureRootIsScheduled(root, now()); + return null; + } + let exitStatus = renderRootConcurrent(root, lanes); if ( diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index bbf0498fd6b49..d60b00a009ea1 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -138,3 +138,5 @@ export const enablePassiveEventIntervention = true; export const disableOnScrollBubbling = true; export const enableEagerRootListeners = true; + +export const disableSchedulerTimeoutInWorkLoop = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 6db4a39e8f80b..5443099b1e6b6 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -52,6 +52,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 5e7d0e48b0700..07c1b90f11f89 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -51,6 +51,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index fe5b969035af7..a8e7e0b92ed22 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -51,6 +51,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index a353dafcf7dc4..57ae05f300a90 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 3b8939884ccf8..e9690a314b4ec 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -51,6 +51,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index f6e17c5ded055..12540afa51c22 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -51,6 +51,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 43c5570857ad1..926a56596a6e4 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -51,6 +51,7 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = true; export const enablePassiveEventIntervention = true; export const enableEagerRootListeners = true; +export const disableSchedulerTimeoutInWorkLoop = false; // Flow magic to verify the exports of this file match the original version. // eslint-disable-next-line no-unused-vars diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 088422d4059e3..64f127859b354 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -49,3 +49,4 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; // to __VARIANT__. export const enableTrustedTypesIntegration = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; +export const disableSchedulerTimeoutInWorkLoop = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index dbbaf0c70daab..1aeccfc226664 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -30,6 +30,7 @@ export const { enablePassiveEventIntervention, disableOnScrollBubbling, enableEagerRootListeners, + disableSchedulerTimeoutInWorkLoop, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build.