Skip to content

Commit

Permalink
Lane enableTransitionEntanglement flag (facebook#20775)
Browse files Browse the repository at this point in the history
  • Loading branch information
acdlite authored and koto committed Jun 15, 2021
1 parent 2c9e5c3 commit b61dfa8
Show file tree
Hide file tree
Showing 17 changed files with 84 additions and 353 deletions.
109 changes: 24 additions & 85 deletions packages/react-reconciler/src/ReactFiberLane.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export type LaneMap<T> = Array<T>;
import invariant from 'shared/invariant';
import {
enableCache,
enableTransitionEntanglement,
enableNonInterruptingNormalPri,
} from 'shared/ReactFeatureFlags';

Expand Down Expand Up @@ -509,93 +508,33 @@ export function isTransitionLane(lane: Lane) {

// To ensure consistency across multiple updates in the same event, this should
// be a pure function, so that it always returns the same lane for given inputs.
export function findUpdateLane(
lanePriority: LanePriority,
wipLanes: Lanes,
): Lane {
if (enableTransitionEntanglement) {
// Ignore wipLanes. Always assign to the same bit per priority.
switch (lanePriority) {
case NoLanePriority:
break;
case SyncLanePriority:
return SyncLane;
case SyncBatchedLanePriority:
return SyncBatchedLane;
case InputDiscreteLanePriority: {
return pickArbitraryLane(InputDiscreteLanes);
}
case InputContinuousLanePriority: {
return pickArbitraryLane(InputContinuousLanes);
}
case DefaultLanePriority: {
return pickArbitraryLane(DefaultLanes);
}
case TransitionPriority: // Should be handled by findTransitionLane instead
case RetryLanePriority: // Should be handled by findRetryLane instead
break;
case IdleLanePriority:
return pickArbitraryLane(IdleLanes);
default:
// The remaining priorities are not valid for updates
break;
export function findUpdateLane(lanePriority: LanePriority): Lane {
switch (lanePriority) {
case NoLanePriority:
break;
case SyncLanePriority:
return SyncLane;
case SyncBatchedLanePriority:
return SyncBatchedLane;
case InputDiscreteLanePriority: {
return pickArbitraryLane(InputDiscreteLanes);
}
} else {
// Old behavior that uses wipLanes to shift interleaved updates into a
// separate lane. This is no longer needed because we put interleaved
// updates on a special queue.
switch (lanePriority) {
case NoLanePriority:
break;
case SyncLanePriority:
return SyncLane;
case SyncBatchedLanePriority:
return SyncBatchedLane;
case InputDiscreteLanePriority: {
const lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);
if (lane === NoLane) {
// Shift to the next priority level
return findUpdateLane(InputContinuousLanePriority, wipLanes);
}
return lane;
}
case InputContinuousLanePriority: {
const lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);
if (lane === NoLane) {
// Shift to the next priority level
return findUpdateLane(DefaultLanePriority, wipLanes);
}
return lane;
}
case DefaultLanePriority: {
let lane = pickArbitraryLane(DefaultLanes & ~wipLanes);
if (lane === NoLane) {
// If all the default lanes are already being worked on, look for a
// lane in the transition range.
lane = pickArbitraryLane(TransitionLanes & ~wipLanes);
if (lane === NoLane) {
// All the transition lanes are taken, too. This should be very
// rare, but as a last resort, pick a default lane. This will have
// the effect of interrupting the current work-in-progress render.
lane = pickArbitraryLane(DefaultLanes);
}
}
return lane;
}
case TransitionPriority: // Should be handled by findTransitionLane instead
case RetryLanePriority: // Should be handled by findRetryLane instead
break;
case IdleLanePriority:
let lane = pickArbitraryLane(IdleLanes & ~wipLanes);
if (lane === NoLane) {
lane = pickArbitraryLane(IdleLanes);
}
return lane;
default:
// The remaining priorities are not valid for updates
break;
case InputContinuousLanePriority: {
return pickArbitraryLane(InputContinuousLanes);
}
case DefaultLanePriority: {
return pickArbitraryLane(DefaultLanes);
}
case TransitionPriority: // Should be handled by findTransitionLane instead
case RetryLanePriority: // Should be handled by findRetryLane instead
break;
case IdleLanePriority:
return pickArbitraryLane(IdleLanes);
default:
// The remaining priorities are not valid for updates
break;
}

invariant(
false,
'Invalid update priority: %s. This is a bug in React.',
Expand Down
109 changes: 24 additions & 85 deletions packages/react-reconciler/src/ReactFiberLane.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export type LaneMap<T> = Array<T>;
import invariant from 'shared/invariant';
import {
enableCache,
enableTransitionEntanglement,
enableNonInterruptingNormalPri,
} from 'shared/ReactFeatureFlags';

Expand Down Expand Up @@ -509,93 +508,33 @@ export function isTransitionLane(lane: Lane) {

// To ensure consistency across multiple updates in the same event, this should
// be a pure function, so that it always returns the same lane for given inputs.
export function findUpdateLane(
lanePriority: LanePriority,
wipLanes: Lanes,
): Lane {
if (enableTransitionEntanglement) {
// Ignore wipLanes. Always assign to the same bit per priority.
switch (lanePriority) {
case NoLanePriority:
break;
case SyncLanePriority:
return SyncLane;
case SyncBatchedLanePriority:
return SyncBatchedLane;
case InputDiscreteLanePriority: {
return pickArbitraryLane(InputDiscreteLanes);
}
case InputContinuousLanePriority: {
return pickArbitraryLane(InputContinuousLanes);
}
case DefaultLanePriority: {
return pickArbitraryLane(DefaultLanes);
}
case TransitionPriority: // Should be handled by findTransitionLane instead
case RetryLanePriority: // Should be handled by findRetryLane instead
break;
case IdleLanePriority:
return pickArbitraryLane(IdleLanes);
default:
// The remaining priorities are not valid for updates
break;
export function findUpdateLane(lanePriority: LanePriority): Lane {
switch (lanePriority) {
case NoLanePriority:
break;
case SyncLanePriority:
return SyncLane;
case SyncBatchedLanePriority:
return SyncBatchedLane;
case InputDiscreteLanePriority: {
return pickArbitraryLane(InputDiscreteLanes);
}
} else {
// Old behavior that uses wipLanes to shift interleaved updates into a
// separate lane. This is no longer needed because we put interleaved
// updates on a special queue.
switch (lanePriority) {
case NoLanePriority:
break;
case SyncLanePriority:
return SyncLane;
case SyncBatchedLanePriority:
return SyncBatchedLane;
case InputDiscreteLanePriority: {
const lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);
if (lane === NoLane) {
// Shift to the next priority level
return findUpdateLane(InputContinuousLanePriority, wipLanes);
}
return lane;
}
case InputContinuousLanePriority: {
const lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);
if (lane === NoLane) {
// Shift to the next priority level
return findUpdateLane(DefaultLanePriority, wipLanes);
}
return lane;
}
case DefaultLanePriority: {
let lane = pickArbitraryLane(DefaultLanes & ~wipLanes);
if (lane === NoLane) {
// If all the default lanes are already being worked on, look for a
// lane in the transition range.
lane = pickArbitraryLane(TransitionLanes & ~wipLanes);
if (lane === NoLane) {
// All the transition lanes are taken, too. This should be very
// rare, but as a last resort, pick a default lane. This will have
// the effect of interrupting the current work-in-progress render.
lane = pickArbitraryLane(DefaultLanes);
}
}
return lane;
}
case TransitionPriority: // Should be handled by findTransitionLane instead
case RetryLanePriority: // Should be handled by findRetryLane instead
break;
case IdleLanePriority:
let lane = pickArbitraryLane(IdleLanes & ~wipLanes);
if (lane === NoLane) {
lane = pickArbitraryLane(IdleLanes);
}
return lane;
default:
// The remaining priorities are not valid for updates
break;
case InputContinuousLanePriority: {
return pickArbitraryLane(InputContinuousLanes);
}
case DefaultLanePriority: {
return pickArbitraryLane(DefaultLanes);
}
case TransitionPriority: // Should be handled by findTransitionLane instead
case RetryLanePriority: // Should be handled by findRetryLane instead
break;
case IdleLanePriority:
return pickArbitraryLane(IdleLanes);
default:
// The remaining priorities are not valid for updates
break;
}

invariant(
false,
'Invalid update priority: %s. This is a bug in React.',
Expand Down
44 changes: 5 additions & 39 deletions packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
disableSchedulerTimeoutInWorkLoop,
enableDoubleInvokingEffects,
skipUnmountedBoundaries,
enableTransitionEntanglement,
enableNativeEventPriorityInference,
} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand Down Expand Up @@ -455,22 +454,22 @@ export function requestUpdateLane(fiber: Fiber): Lane {
(executionContext & DiscreteEventContext) !== NoContext &&
schedulerPriority === UserBlockingSchedulerPriority
) {
lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes);
lane = findUpdateLane(InputDiscreteLanePriority);
} else if (
decoupleUpdatePriorityFromScheduler &&
getCurrentUpdateLanePriority() !== NoLanePriority
) {
const currentLanePriority = getCurrentUpdateLanePriority();
lane = findUpdateLane(currentLanePriority, currentEventWipLanes);
lane = findUpdateLane(currentLanePriority);
} else {
if (enableNativeEventPriorityInference) {
const eventLanePriority = getCurrentEventPriority();
lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
lane = findUpdateLane(eventLanePriority);
} else {
const schedulerLanePriority = schedulerPriorityToLanePriority(
schedulerPriority,
);
lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
lane = findUpdateLane(schedulerLanePriority);
}
}

Expand Down Expand Up @@ -837,22 +836,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
}

let exitStatus = renderRootConcurrent(root, lanes);

if (
!enableTransitionEntanglement &&
includesSomeLane(
workInProgressRootIncludedLanes,
workInProgressRootUpdatedLanes,
)
) {
// The render included lanes that were updated during the render phase.
// For example, when unhiding a hidden tree, we include all the lanes
// that were previously skipped when the tree was hidden. That set of
// lanes is a superset of the lanes we started rendering with.
//
// So we'll throw out the current work and restart.
prepareFreshStack(root, NoLanes);
} else if (exitStatus !== RootIncomplete) {
if (exitStatus !== RootIncomplete) {
if (exitStatus === RootErrored) {
executionContext |= RetryAfterError;

Expand Down Expand Up @@ -1044,24 +1028,6 @@ function performSyncWorkOnRoot(root) {
// rendering it before rendering the rest of the expired work.
lanes = workInProgressRootRenderLanes;
exitStatus = renderRootSync(root, lanes);
if (
!enableTransitionEntanglement &&
includesSomeLane(
workInProgressRootIncludedLanes,
workInProgressRootUpdatedLanes,
)
) {
// The render included lanes that were updated during the render phase.
// For example, when unhiding a hidden tree, we include all the lanes
// that were previously skipped when the tree was hidden. That set of
// lanes is a superset of the lanes we started rendering with.
//
// Note that this only happens when part of the tree is rendered
// concurrently. If the whole tree is rendered synchronously, then there
// are no interleaved events.
lanes = getNextLanes(root, lanes);
exitStatus = renderRootSync(root, lanes);
}
} else {
lanes = getNextLanes(root, NoLanes);
exitStatus = renderRootSync(root, lanes);
Expand Down
Loading

0 comments on commit b61dfa8

Please sign in to comment.