Skip to content

Commit

Permalink
Add infinite update loop detection (#28279)
Browse files Browse the repository at this point in the history
This is a partial redo of #26625.
Since that was unlanded due to some detected breakages. This now
includes a feature flag to be careful in rolling this out.

DiffTrain build for commit d8c1fa6.
  • Loading branch information
kassens committed Feb 9, 2024
1 parent 2ad4616 commit 55213cc
Show file tree
Hide file tree
Showing 13 changed files with 509 additions and 244 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<491e433a3e81760180afd88ca49b58ec>>
* @generated SignedSource<<1aed0b993a4c164c11bd44255ec4ddac>>
*/

"use strict";
Expand Down Expand Up @@ -1728,7 +1728,7 @@ if (__DEV__) {

return laneMap;
}
function markRootUpdated(root, updateLane) {
function markRootUpdated$1(root, updateLane) {
root.pendingLanes |= updateLane; // If there are any suspended transitions, it's possible this new update
// could unblock them. Clear the suspended lanes so that we can try rendering
// them again.
Expand Down Expand Up @@ -1765,7 +1765,7 @@ if (__DEV__) {
markSpawnedDeferredLane(root, spawnedLane, suspendedLanes);
}
}
function markRootPinged(root, pingedLanes) {
function markRootPinged$1(root, pingedLanes) {
root.pingedLanes |= root.suspendedLanes & pingedLanes;
}
function markRootFinished(root, remainingLanes, spawnedLane) {
Expand Down Expand Up @@ -21608,7 +21608,9 @@ if (__DEV__) {
var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
// We will log them once the tree commits.

var workInProgressRootRecoverableErrors = null; // The most recent time we either committed a fallback, or when a fallback was
var workInProgressRootRecoverableErrors = null; // Tracks when an update occurs during the render phase.

var workInProgressRootDidIncludeRecursiveRenderUpdate = false; // Thacks when an update occurs during the commit phase. It's a separate
// filled in with the resolved UI. This lets us throttle the appearance of new
// content as it streams in, to minimize jank.
// TODO: Think of a better name for this variable?
Expand Down Expand Up @@ -22118,6 +22120,7 @@ if (__DEV__) {
root,
workInProgressRootRecoverableErrors,
workInProgressTransitions,
workInProgressRootDidIncludeRecursiveRenderUpdate,
workInProgressDeferredLane
);
} else {
Expand Down Expand Up @@ -22148,6 +22151,7 @@ if (__DEV__) {
finishedWork,
workInProgressRootRecoverableErrors,
workInProgressTransitions,
workInProgressRootDidIncludeRecursiveRenderUpdate,
lanes,
workInProgressDeferredLane
),
Expand All @@ -22162,6 +22166,7 @@ if (__DEV__) {
finishedWork,
workInProgressRootRecoverableErrors,
workInProgressTransitions,
workInProgressRootDidIncludeRecursiveRenderUpdate,
lanes,
workInProgressDeferredLane
);
Expand All @@ -22173,6 +22178,7 @@ if (__DEV__) {
finishedWork,
recoverableErrors,
transitions,
didIncludeRenderPhaseUpdate,
lanes,
spawnedLane
) {
Expand All @@ -22197,14 +22203,26 @@ if (__DEV__) {
// us that it's ready. This will be canceled if we start work on the
// root again.
root.cancelPendingCommit = schedulePendingCommit(
commitRoot.bind(null, root, recoverableErrors, transitions)
commitRoot.bind(
null,
root,
recoverableErrors,
transitions,
didIncludeRenderPhaseUpdate
)
);
markRootSuspended(root, lanes, spawnedLane);
return;
}
} // Otherwise, commit immediately.

commitRoot(root, recoverableErrors, transitions, spawnedLane);
commitRoot(
root,
recoverableErrors,
transitions,
didIncludeRenderPhaseUpdate,
spawnedLane
);
}

function isRenderConsistentWithExternalStores(finishedWork) {
Expand Down Expand Up @@ -22267,13 +22285,23 @@ if (__DEV__) {
// eslint-disable-next-line no-unreachable

return true;
} // The extra indirections around markRootUpdated and markRootSuspended is
// needed to avoid a circular dependency between this module and
// ReactFiberLane. There's probably a better way to split up these modules and
// avoid this problem. Perhaps all the root-marking functions should move into
// the work loop.

function markRootUpdated(root, updatedLanes) {
markRootUpdated$1(root, updatedLanes);
}

function markRootPinged(root, pingedLanes) {
markRootPinged$1(root, pingedLanes);
}

function markRootSuspended(root, suspendedLanes, spawnedLane) {
// When suspending, we should always exclude lanes that were pinged or (more
// rarely, since we try to avoid it) updated during the render phase.
// TODO: Lol maybe there's a better way to factor this besides this
// obnoxiously named function :)
suspendedLanes = removeLanes(
suspendedLanes,
workInProgressRootPingedLanes
Expand All @@ -22282,6 +22310,7 @@ if (__DEV__) {
suspendedLanes,
workInProgressRootInterleavedUpdatedLanes
);

markRootSuspended$1(root, suspendedLanes, spawnedLane);
} // This is the entry point for synchronous tasks that don't go
// through Scheduler
Expand Down Expand Up @@ -22356,6 +22385,7 @@ if (__DEV__) {
root,
workInProgressRootRecoverableErrors,
workInProgressTransitions,
workInProgressRootDidIncludeRecursiveRenderUpdate,
workInProgressDeferredLane
); // Before exiting, make sure there's a callback scheduled for the next
// pending level.
Expand Down Expand Up @@ -22500,7 +22530,8 @@ if (__DEV__) {
workInProgressRootPingedLanes = NoLanes;
workInProgressDeferredLane = NoLane;
workInProgressRootConcurrentErrors = null;
workInProgressRootRecoverableErrors = null; // Get the lanes that are entangled with whatever we're about to render. We
workInProgressRootRecoverableErrors = null;
workInProgressRootDidIncludeRecursiveRenderUpdate = false; // Get the lanes that are entangled with whatever we're about to render. We
// track these separately so we can distinguish the priority of the render
// task from the priority of the lanes it is entangled with. For example, a
// transition may not be allowed to finish unless it includes the Sync lane,
Expand Down Expand Up @@ -23450,7 +23481,13 @@ if (__DEV__) {
workInProgress = null;
}

function commitRoot(root, recoverableErrors, transitions, spawnedLane) {
function commitRoot(
root,
recoverableErrors,
transitions,
didIncludeRenderPhaseUpdate,
spawnedLane
) {
// TODO: This no longer makes any sense. We already wrap the mutation and
// layout phases. Should be able to remove.
var previousUpdateLanePriority = getCurrentUpdatePriority();
Expand All @@ -23463,6 +23500,7 @@ if (__DEV__) {
root,
recoverableErrors,
transitions,
didIncludeRenderPhaseUpdate,
previousUpdateLanePriority,
spawnedLane
);
Expand All @@ -23478,6 +23516,7 @@ if (__DEV__) {
root,
recoverableErrors,
transitions,
didIncludeRenderPhaseUpdate,
renderPriorityLevel,
spawnedLane
) {
Expand Down Expand Up @@ -23537,7 +23576,7 @@ if (__DEV__) {

var concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
markRootFinished(root, remainingLanes, spawnedLane);
markRootFinished(root, remainingLanes, spawnedLane); // Reset this before firing side effects so we can detect recursive updates.

if (root === workInProgressRoot) {
// We can reset these now that they are finished.
Expand Down Expand Up @@ -23725,6 +23764,9 @@ if (__DEV__) {
// hydration is conceptually not an update.

if (
// Check if there was a recursive update spawned by this render, in either
// the render phase or the commit phase. We track these explicitly because
// we can't infer from the remaining lanes alone.
// Was the finished render the result of an update (not hydration)?
includesSomeLane(lanes, UpdateLanes) && // Did it schedule a sync update?
includesSomeLane(remainingLanes, SyncUpdateLanes)
Expand Down Expand Up @@ -24172,6 +24214,7 @@ if (__DEV__) {
nestedPassiveUpdateCount = 0;
rootWithNestedUpdates = null;
rootWithPassiveNestedUpdates = null;

throw new Error(
"Maximum update depth exceeded. This can happen when a component " +
"repeatedly calls setState inside componentWillUpdate or " +
Expand Down Expand Up @@ -25678,7 +25721,7 @@ if (__DEV__) {
return root;
}

var ReactVersion = "18.3.0-canary-03d6f7cf0-20240209";
var ReactVersion = "18.3.0-canary-d8c1fa6b0-20240209";

// Might add PROFILE later.

Expand Down
Loading

0 comments on commit 55213cc

Please sign in to comment.