diff --git a/packages/react-reconciler/src/ReactFiberLane.new.js b/packages/react-reconciler/src/ReactFiberLane.new.js index 878fc433f3c53..c5aef527a422a 100644 --- a/packages/react-reconciler/src/ReactFiberLane.new.js +++ b/packages/react-reconciler/src/ReactFiberLane.new.js @@ -11,6 +11,25 @@ import type {FiberRoot} from './ReactInternalTypes'; // TODO: Ideally these types would be opaque but that doesn't work well with // our reconciler fork infra, since these leak into non-reconciler packages. +export type LanePriority = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17; export type Lanes = number; export type Lane = number; @@ -24,6 +43,28 @@ import { } from 'shared/ReactFeatureFlags'; import {isDevToolsPresent} from './ReactFiberDevToolsHook.new'; +export const SyncLanePriority: LanePriority = 12; + +const InputContinuousHydrationLanePriority: LanePriority = 11; +export const InputContinuousLanePriority: LanePriority = 10; + +const DefaultHydrationLanePriority: LanePriority = 9; +export const DefaultLanePriority: LanePriority = 8; + +const TransitionHydrationPriority: LanePriority = 7; +export const TransitionPriority: LanePriority = 6; + +const RetryLanePriority: LanePriority = 5; + +const SelectiveHydrationLanePriority: LanePriority = 4; + +const IdleHydrationLanePriority: LanePriority = 3; +export const IdleLanePriority: LanePriority = 2; + +const OffscreenLanePriority: LanePriority = 1; + +export const NoLanePriority: LanePriority = 0; + // Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler. // If those values are changed that package should be rebuilt and redeployed. @@ -127,19 +168,29 @@ export const NoTimestamp = -1; let nextTransitionLane: Lane = TransitionLane1; let nextRetryLane: Lane = RetryLane1; +// "Registers" used to "return" multiple values +// Used by getHighestPriorityLanes and getNextLanes: +let return_highestLanePriority: LanePriority = DefaultLanePriority; + function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { switch (getHighestPriorityLane(lanes)) { case SyncLane: + return_highestLanePriority = SyncLanePriority; return SyncLane; case InputContinuousHydrationLane: + return_highestLanePriority = InputContinuousHydrationLanePriority; return InputContinuousHydrationLane; case InputContinuousLane: + return_highestLanePriority = InputContinuousLanePriority; return InputContinuousLane; case DefaultHydrationLane: + return_highestLanePriority = DefaultHydrationLanePriority; return DefaultHydrationLane; case DefaultLane: + return_highestLanePriority = DefaultLanePriority; return DefaultLane; case TransitionHydrationLane: + return_highestLanePriority = TransitionHydrationPriority; return TransitionHydrationLane; case TransitionLane1: case TransitionLane2: @@ -157,20 +208,26 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { case TransitionLane14: case TransitionLane15: case TransitionLane16: + return_highestLanePriority = TransitionPriority; return lanes & TransitionLanes; case RetryLane1: case RetryLane2: case RetryLane3: case RetryLane4: case RetryLane5: + return_highestLanePriority = RetryLanePriority; return lanes & RetryLanes; case SelectiveHydrationLane: + return_highestLanePriority = SelectiveHydrationLanePriority; return SelectiveHydrationLane; case IdleHydrationLane: + return_highestLanePriority = IdleHydrationLanePriority; return IdleHydrationLane; case IdleLane: + return_highestLanePriority = IdleLanePriority; return IdleLane; case OffscreenLane: + return_highestLanePriority = OffscreenLanePriority; return OffscreenLane; default: if (__DEV__) { @@ -179,6 +236,7 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { ); } // This shouldn't be reachable, but as a fallback, return the entire bitmask. + return_highestLanePriority = DefaultLanePriority; return lanes; } } @@ -187,10 +245,12 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { // Early bailout if there's no pending work left. const pendingLanes = root.pendingLanes; if (pendingLanes === NoLanes) { + return_highestLanePriority = NoLanePriority; return NoLanes; } let nextLanes = NoLanes; + let nextLanePriority = NoLanePriority; const suspendedLanes = root.suspendedLanes; const pingedLanes = root.pingedLanes; @@ -202,10 +262,12 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; if (nonIdleUnblockedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + nextLanePriority = return_highestLanePriority; } else { const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; if (nonIdlePingedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); + nextLanePriority = return_highestLanePriority; } } } else { @@ -213,9 +275,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { const unblockedLanes = pendingLanes & ~suspendedLanes; if (unblockedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(unblockedLanes); + nextLanePriority = return_highestLanePriority; } else { if (pingedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(pingedLanes); + nextLanePriority = return_highestLanePriority; } } } @@ -249,6 +313,8 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { ) { // Keep working on the existing in-progress tree. Do not interrupt. return wipLanes; + } else { + return_highestLanePriority = nextLanePriority; } } @@ -443,6 +509,9 @@ export function getLanesToRetrySynchronouslyOnError(root: FiberRoot): Lanes { return NoLanes; } +export function returnNextLanesPriority() { + return return_highestLanePriority; +} export function includesNonIdleWork(lanes: Lanes) { return (lanes & NonIdleLanes) !== NoLanes; } @@ -548,6 +617,13 @@ export function higherPriorityLane(a: Lane, b: Lane) { return a !== NoLane && a < b ? a : b; } +export function higherLanePriority( + a: LanePriority, + b: LanePriority, +): LanePriority { + return a !== NoLanePriority && a > b ? a : b; +} + export function createLaneMap(initial: T): LaneMap { // Intentionally pushing one by one. // https://v8.dev/blog/elements-kinds#avoid-creating-holes diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js index d4010468f0d74..926a3da32cbab 100644 --- a/packages/react-reconciler/src/ReactFiberLane.old.js +++ b/packages/react-reconciler/src/ReactFiberLane.old.js @@ -11,6 +11,25 @@ import type {FiberRoot} from './ReactInternalTypes'; // TODO: Ideally these types would be opaque but that doesn't work well with // our reconciler fork infra, since these leak into non-reconciler packages. +export type LanePriority = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17; export type Lanes = number; export type Lane = number; @@ -24,6 +43,28 @@ import { } from 'shared/ReactFeatureFlags'; import {isDevToolsPresent} from './ReactFiberDevToolsHook.old'; +export const SyncLanePriority: LanePriority = 12; + +const InputContinuousHydrationLanePriority: LanePriority = 11; +export const InputContinuousLanePriority: LanePriority = 10; + +const DefaultHydrationLanePriority: LanePriority = 9; +export const DefaultLanePriority: LanePriority = 8; + +const TransitionHydrationPriority: LanePriority = 7; +export const TransitionPriority: LanePriority = 6; + +const RetryLanePriority: LanePriority = 5; + +const SelectiveHydrationLanePriority: LanePriority = 4; + +const IdleHydrationLanePriority: LanePriority = 3; +export const IdleLanePriority: LanePriority = 2; + +const OffscreenLanePriority: LanePriority = 1; + +export const NoLanePriority: LanePriority = 0; + // Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler. // If those values are changed that package should be rebuilt and redeployed. @@ -127,19 +168,29 @@ export const NoTimestamp = -1; let nextTransitionLane: Lane = TransitionLane1; let nextRetryLane: Lane = RetryLane1; +// "Registers" used to "return" multiple values +// Used by getHighestPriorityLanes and getNextLanes: +let return_highestLanePriority: LanePriority = DefaultLanePriority; + function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { switch (getHighestPriorityLane(lanes)) { case SyncLane: + return_highestLanePriority = SyncLanePriority; return SyncLane; case InputContinuousHydrationLane: + return_highestLanePriority = InputContinuousHydrationLanePriority; return InputContinuousHydrationLane; case InputContinuousLane: + return_highestLanePriority = InputContinuousLanePriority; return InputContinuousLane; case DefaultHydrationLane: + return_highestLanePriority = DefaultHydrationLanePriority; return DefaultHydrationLane; case DefaultLane: + return_highestLanePriority = DefaultLanePriority; return DefaultLane; case TransitionHydrationLane: + return_highestLanePriority = TransitionHydrationPriority; return TransitionHydrationLane; case TransitionLane1: case TransitionLane2: @@ -157,20 +208,26 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { case TransitionLane14: case TransitionLane15: case TransitionLane16: + return_highestLanePriority = TransitionPriority; return lanes & TransitionLanes; case RetryLane1: case RetryLane2: case RetryLane3: case RetryLane4: case RetryLane5: + return_highestLanePriority = RetryLanePriority; return lanes & RetryLanes; case SelectiveHydrationLane: + return_highestLanePriority = SelectiveHydrationLanePriority; return SelectiveHydrationLane; case IdleHydrationLane: + return_highestLanePriority = IdleHydrationLanePriority; return IdleHydrationLane; case IdleLane: + return_highestLanePriority = IdleLanePriority; return IdleLane; case OffscreenLane: + return_highestLanePriority = OffscreenLanePriority; return OffscreenLane; default: if (__DEV__) { @@ -179,6 +236,7 @@ function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { ); } // This shouldn't be reachable, but as a fallback, return the entire bitmask. + return_highestLanePriority = DefaultLanePriority; return lanes; } } @@ -187,10 +245,12 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { // Early bailout if there's no pending work left. const pendingLanes = root.pendingLanes; if (pendingLanes === NoLanes) { + return_highestLanePriority = NoLanePriority; return NoLanes; } let nextLanes = NoLanes; + let nextLanePriority = NoLanePriority; const suspendedLanes = root.suspendedLanes; const pingedLanes = root.pingedLanes; @@ -202,10 +262,12 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; if (nonIdleUnblockedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + nextLanePriority = return_highestLanePriority; } else { const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; if (nonIdlePingedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); + nextLanePriority = return_highestLanePriority; } } } else { @@ -213,9 +275,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { const unblockedLanes = pendingLanes & ~suspendedLanes; if (unblockedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(unblockedLanes); + nextLanePriority = return_highestLanePriority; } else { if (pingedLanes !== NoLanes) { nextLanes = getHighestPriorityLanes(pingedLanes); + nextLanePriority = return_highestLanePriority; } } } @@ -249,6 +313,8 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { ) { // Keep working on the existing in-progress tree. Do not interrupt. return wipLanes; + } else { + return_highestLanePriority = nextLanePriority; } } @@ -443,6 +509,9 @@ export function getLanesToRetrySynchronouslyOnError(root: FiberRoot): Lanes { return NoLanes; } +export function returnNextLanesPriority() { + return return_highestLanePriority; +} export function includesNonIdleWork(lanes: Lanes) { return (lanes & NonIdleLanes) !== NoLanes; } @@ -548,6 +617,13 @@ export function higherPriorityLane(a: Lane, b: Lane) { return a !== NoLane && a < b ? a : b; } +export function higherLanePriority( + a: LanePriority, + b: LanePriority, +): LanePriority { + return a !== NoLanePriority && a > b ? a : b; +} + export function createLaneMap(initial: T): LaneMap { // Intentionally pushing one by one. // https://v8.dev/blog/elements-kinds#avoid-creating-holes