mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[Fizz] add additional task reentrancy protections (#36291)
The prior fix for finishedTask reentrancy solved an observed failure. This change adds a bit of defensive bookeeping to protect against other theoretical reentrant task finishing that might fail in simlar ways but where we don't have a clear demonstration of the bug.
This commit is contained in:
+18
-5
@@ -4438,9 +4438,15 @@ function erroredTask(
|
||||
const boundaryRow = boundary.row;
|
||||
if (boundaryRow !== null) {
|
||||
// Unblock the SuspenseListRow that was blocked by this boundary.
|
||||
// finishSuspenseListRow → unblockSuspenseListRow → finishedTask reenters
|
||||
// and decrements allPendingTasks. Pin the counter above zero so those
|
||||
// nested calls can't trip completeAll before this outer frame's own
|
||||
// zero check at the end.
|
||||
request.allPendingTasks++;
|
||||
if (--boundaryRow.pendingTasks === 0) {
|
||||
finishSuspenseListRow(request, boundaryRow);
|
||||
}
|
||||
request.allPendingTasks--;
|
||||
}
|
||||
|
||||
// Regardless of what happens next, this boundary won't be displayed,
|
||||
@@ -4955,20 +4961,21 @@ function finishedTask(
|
||||
hoistHoistables(boundaryRow.hoistables, boundary.contentState);
|
||||
}
|
||||
if (!isEligibleForOutlining(request, boundary)) {
|
||||
// abortTaskSoft reenters finishedTask for each aborted task, which
|
||||
// decrements allPendingTasks. Ensure that these reentrant finsihedTask
|
||||
// calls do not call `completeAll` too early by forcing the task counter
|
||||
// above zero for their duration.
|
||||
// abortTaskSoft (below) and finishSuspenseListRow → unblockSuspenseListRow
|
||||
// → finishedTask (further below) both reenter finishedTask and decrement
|
||||
// allPendingTasks. Pin the counter above zero for the duration of these
|
||||
// fan-outs so a nested finishedTask can't observe 0 and call completeAll
|
||||
// before this outer call reaches its own zero check.
|
||||
request.allPendingTasks++;
|
||||
boundary.fallbackAbortableTasks.forEach(abortTaskSoft, request);
|
||||
boundary.fallbackAbortableTasks.clear();
|
||||
request.allPendingTasks--;
|
||||
if (boundaryRow !== null) {
|
||||
// If we aren't eligible for outlining, we don't have to wait until we flush it.
|
||||
if (--boundaryRow.pendingTasks === 0) {
|
||||
finishSuspenseListRow(request, boundaryRow);
|
||||
}
|
||||
}
|
||||
request.allPendingTasks--;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -4994,11 +5001,17 @@ function finishedTask(
|
||||
boundaryRow.next,
|
||||
);
|
||||
}
|
||||
// finishSuspenseListRow → unblockSuspenseListRow → finishedTask reenters
|
||||
// and decrements allPendingTasks. Pin the counter above zero so those
|
||||
// nested calls can't trip completeAll before this outer frame's own
|
||||
// zero check at the end.
|
||||
request.allPendingTasks++;
|
||||
if (--boundaryRow.pendingTasks === 0) {
|
||||
// This is really unnecessary since we've already postponed the boundaries but
|
||||
// for pairity with other track+finish paths. We might end up using the hoisting.
|
||||
finishSuspenseListRow(request, boundaryRow);
|
||||
}
|
||||
request.allPendingTasks--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user