@samitouri / QOS-React-2 / commits / 348ed0e93f

Don't return from perform/completeUnitOfWork (#18566)

* Remove unnecessary workInProgress line * Mutate workInProgress instead of returning We were ambivalent about this before. * Make handleError a void method too

Sebastian Markbåge committed Apr 9, 2020 at 17:12 UTC 348ed0e93f1c9402538fa1cde980b3cce4ec69c4
2 files changed +92 -82
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+46 -41
@@ -1200,7 +1200,7 @@ function prepareFreshStack(root, expirationTime) {
1200 }
1201 }
1202
1203 -function handleError(root, thrownValue) {
1203 +function handleError(root, thrownValue): void {
1204 do {
1205 try {
1206 // Reset module-level state that was set during the render phase.
@@ -1225,7 +1225,7 @@ function handleError(root, thrownValue) {
1225 // interntionally not calling those, we need set it here.
1226 // TODO: Consider calling `unwindWork` to pop the contexts.
1227 workInProgress = null;
1228 - return null;
1228 + return;
1229 }
1230
1231 if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
@@ -1242,7 +1242,7 @@ function handleError(root, thrownValue) {
1242 thrownValue,
1243 renderExpirationTime,
1244 );
1245 - workInProgress = completeUnitOfWork(workInProgress);
1245 + completeUnitOfWork(workInProgress);
1246 } catch (yetAnotherThrownValue) {
1247 // Something in the return path also threw.
1248 thrownValue = yetAnotherThrownValue;
@@ -1423,7 +1423,7 @@ function renderRootSync(root, expirationTime) {
1423 function workLoopSync() {
1424 // Already timed out, so perform work without checking if we need to yield.
1425 while (workInProgress !== null) {
1426 - workInProgress = performUnitOfWork(workInProgress);
1426 + performUnitOfWork(workInProgress);
1427 }
1428 }
1429
@@ -1474,11 +1474,11 @@ function renderRootConcurrent(root, expirationTime) {
1474 function workLoopConcurrent() {
1475 // Perform work until Scheduler asks us to yield
1476 while (workInProgress !== null && !shouldYield()) {
1477 - workInProgress = performUnitOfWork(workInProgress);
1477 + performUnitOfWork(workInProgress);
1478 }
1479 }
1480
1481 -function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
1481 +function performUnitOfWork(unitOfWork: Fiber): void {
1482 // The current, flushed, state of this fiber is the alternate. Ideally
1483 // nothing should rely on this, but relying on it here means that we don't
1484 // need an additional field on the work in progress.
@@ -1498,45 +1498,47 @@ function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
1498 unitOfWork.memoizedProps = unitOfWork.pendingProps;
1499 if (next === null) {
1500 // If this doesn't spawn new work, complete the current work.
1501 - next = completeUnitOfWork(unitOfWork);
1501 + completeUnitOfWork(unitOfWork);
1502 + } else {
1503 + workInProgress = next;
1504 }
1505
1506 ReactCurrentOwner.current = null;
1505 - return next;
1507 }
1508
1508 -function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1509 +function completeUnitOfWork(unitOfWork: Fiber): void {
1510 // Attempt to complete the current unit of work, then move to the next
1511 // sibling. If there are no more siblings, return to the parent fiber.
1511 - workInProgress = unitOfWork;
1512 + let completedWork = unitOfWork;
1513 do {
1514 // The current, flushed, state of this fiber is the alternate. Ideally
1515 // nothing should rely on this, but relying on it here means that we don't
1516 // need an additional field on the work in progress.
1516 - const current = workInProgress.alternate;
1517 - const returnFiber = workInProgress.return;
1517 + const current = completedWork.alternate;
1518 + const returnFiber = completedWork.return;
1519
1520 // Check if the work completed or if something threw.
1520 - if ((workInProgress.effectTag & Incomplete) === NoEffect) {
1521 - setCurrentDebugFiberInDEV(workInProgress);
1521 + if ((completedWork.effectTag & Incomplete) === NoEffect) {
1522 + setCurrentDebugFiberInDEV(completedWork);
1523 let next;
1524 if (
1525 !enableProfilerTimer ||
1525 - (workInProgress.mode & ProfileMode) === NoMode
1526 + (completedWork.mode & ProfileMode) === NoMode
1527 ) {
1527 - next = completeWork(current, workInProgress, renderExpirationTime);
1528 + next = completeWork(current, completedWork, renderExpirationTime);
1529 } else {
1529 - startProfilerTimer(workInProgress);
1530 - next = completeWork(current, workInProgress, renderExpirationTime);
1530 + startProfilerTimer(completedWork);
1531 + next = completeWork(current, completedWork, renderExpirationTime);
1532 // Update render duration assuming we didn't error.
1532 - stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
1533 + stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
1534 }
1535 resetCurrentDebugFiberInDEV();
1535 - resetChildExpirationTime(workInProgress);
1536 + resetChildExpirationTime(completedWork);
1537
1538 if (next !== null) {
1539 // Completing this fiber spawned new work. Work on that next.
1539 - return next;
1540 + workInProgress = next;
1541 + return;
1542 }
1543
1544 if (
@@ -1548,13 +1550,13 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1550 // list of the parent. The completion order of the children affects the
1551 // side-effect order.
1552 if (returnFiber.firstEffect === null) {
1551 - returnFiber.firstEffect = workInProgress.firstEffect;
1553 + returnFiber.firstEffect = completedWork.firstEffect;
1554 }
1553 - if (workInProgress.lastEffect !== null) {
1555 + if (completedWork.lastEffect !== null) {
1556 if (returnFiber.lastEffect !== null) {
1555 - returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
1557 + returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
1558 }
1557 - returnFiber.lastEffect = workInProgress.lastEffect;
1559 + returnFiber.lastEffect = completedWork.lastEffect;
1560 }
1561
1562 // If this fiber had side-effects, we append it AFTER the children's
@@ -1563,43 +1565,43 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1565 // schedule our own side-effect on our own list because if end up
1566 // reusing children we'll schedule this effect onto itself since we're
1567 // at the end.
1566 - const effectTag = workInProgress.effectTag;
1568 + const effectTag = completedWork.effectTag;
1569
1570 // Skip both NoWork and PerformedWork tags when creating the effect
1571 // list. PerformedWork effect is read by React DevTools but shouldn't be
1572 // committed.
1573 if (effectTag > PerformedWork) {
1574 if (returnFiber.lastEffect !== null) {
1573 - returnFiber.lastEffect.nextEffect = workInProgress;
1575 + returnFiber.lastEffect.nextEffect = completedWork;
1576 } else {
1575 - returnFiber.firstEffect = workInProgress;
1577 + returnFiber.firstEffect = completedWork;
1578 }
1577 - returnFiber.lastEffect = workInProgress;
1579 + returnFiber.lastEffect = completedWork;
1580 }
1581 }
1582 } else {
1583 // This fiber did not complete because something threw. Pop values off
1584 // the stack without entering the complete phase. If this is a boundary,
1585 // capture values if possible.
1584 - const next = unwindWork(workInProgress, renderExpirationTime);
1586 + const next = unwindWork(completedWork, renderExpirationTime);
1587
1588 // Because this fiber did not complete, don't reset its expiration time.
1589
1590 if (
1591 enableProfilerTimer &&
1590 - (workInProgress.mode & ProfileMode) !== NoMode
1592 + (completedWork.mode & ProfileMode) !== NoMode
1593 ) {
1594 // Record the render duration for the fiber that errored.
1593 - stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
1595 + stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
1596
1597 // Include the time spent working on failed children before continuing.
1596 - let actualDuration = workInProgress.actualDuration;
1597 - let child = workInProgress.child;
1598 + let actualDuration = completedWork.actualDuration;
1599 + let child = completedWork.child;
1600 while (child !== null) {
1601 actualDuration += child.actualDuration;
1602 child = child.sibling;
1603 }
1602 - workInProgress.actualDuration = actualDuration;
1604 + completedWork.actualDuration = actualDuration;
1605 }
1606
1607 if (next !== null) {
@@ -1608,7 +1610,8 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1610 // Since we're restarting, remove anything that is not a host effect
1611 // from the effect tag.
1612 next.effectTag &= HostEffectMask;
1611 - return next;
1613 + workInProgress = next;
1614 + return;
1615 }
1616
1617 if (returnFiber !== null) {
@@ -1618,20 +1621,22 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1621 }
1622 }
1623
1621 - const siblingFiber = workInProgress.sibling;
1624 + const siblingFiber = completedWork.sibling;
1625 if (siblingFiber !== null) {
1626 // If there is more work to do in this returnFiber, do that next.
1624 - return siblingFiber;
1627 + workInProgress = siblingFiber;
1628 + return;
1629 }
1630 // Otherwise, return to the parent
1627 - workInProgress = returnFiber;
1628 - } while (workInProgress !== null);
1631 + completedWork = returnFiber;
1632 + // Update the next thing we're working on in case something throws.
1633 + workInProgress = completedWork;
1634 + } while (completedWork !== null);
1635
1636 // We've reached the root.
1637 if (workInProgressRootExitStatus === RootIncomplete) {
1638 workInProgressRootExitStatus = RootCompleted;
1639 }
1634 - return null;
1640 }
1641
1642 function getRemainingExpirationTime(fiber: Fiber) {
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+46 -41
@@ -1208,7 +1208,7 @@ function prepareFreshStack(root, expirationTime) {
1208 }
1209 }
1210
1211 -function handleError(root, thrownValue) {
1211 +function handleError(root, thrownValue): void {
1212 do {
1213 try {
1214 // Reset module-level state that was set during the render phase.
@@ -1233,7 +1233,7 @@ function handleError(root, thrownValue) {
1233 // interntionally not calling those, we need set it here.
1234 // TODO: Consider calling `unwindWork` to pop the contexts.
1235 workInProgress = null;
1236 - return null;
1236 + return;
1237 }
1238
1239 if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
@@ -1250,7 +1250,7 @@ function handleError(root, thrownValue) {
1250 thrownValue,
1251 renderExpirationTime,
1252 );
1253 - workInProgress = completeUnitOfWork(workInProgress);
1253 + completeUnitOfWork(workInProgress);
1254 } catch (yetAnotherThrownValue) {
1255 // Something in the return path also threw.
1256 thrownValue = yetAnotherThrownValue;
@@ -1442,7 +1442,7 @@ function renderRootSync(root, expirationTime) {
1442 function workLoopSync() {
1443 // Already timed out, so perform work without checking if we need to yield.
1444 while (workInProgress !== null) {
1445 - workInProgress = performUnitOfWork(workInProgress);
1445 + performUnitOfWork(workInProgress);
1446 }
1447 }
1448
@@ -1493,11 +1493,11 @@ function renderRootConcurrent(root, expirationTime) {
1493 function workLoopConcurrent() {
1494 // Perform work until Scheduler asks us to yield
1495 while (workInProgress !== null && !shouldYield()) {
1496 - workInProgress = performUnitOfWork(workInProgress);
1496 + performUnitOfWork(workInProgress);
1497 }
1498 }
1499
1500 -function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
1500 +function performUnitOfWork(unitOfWork: Fiber): void {
1501 // The current, flushed, state of this fiber is the alternate. Ideally
1502 // nothing should rely on this, but relying on it here means that we don't
1503 // need an additional field on the work in progress.
@@ -1517,45 +1517,47 @@ function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
1517 unitOfWork.memoizedProps = unitOfWork.pendingProps;
1518 if (next === null) {
1519 // If this doesn't spawn new work, complete the current work.
1520 - next = completeUnitOfWork(unitOfWork);
1520 + completeUnitOfWork(unitOfWork);
1521 + } else {
1522 + workInProgress = next;
1523 }
1524
1525 ReactCurrentOwner.current = null;
1524 - return next;
1526 }
1527
1527 -function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1528 +function completeUnitOfWork(unitOfWork: Fiber): void {
1529 // Attempt to complete the current unit of work, then move to the next
1530 // sibling. If there are no more siblings, return to the parent fiber.
1530 - workInProgress = unitOfWork;
1531 + let completedWork = unitOfWork;
1532 do {
1533 // The current, flushed, state of this fiber is the alternate. Ideally
1534 // nothing should rely on this, but relying on it here means that we don't
1535 // need an additional field on the work in progress.
1535 - const current = workInProgress.alternate;
1536 - const returnFiber = workInProgress.return;
1536 + const current = completedWork.alternate;
1537 + const returnFiber = completedWork.return;
1538
1539 // Check if the work completed or if something threw.
1539 - if ((workInProgress.effectTag & Incomplete) === NoEffect) {
1540 - setCurrentDebugFiberInDEV(workInProgress);
1540 + if ((completedWork.effectTag & Incomplete) === NoEffect) {
1541 + setCurrentDebugFiberInDEV(completedWork);
1542 let next;
1543 if (
1544 !enableProfilerTimer ||
1544 - (workInProgress.mode & ProfileMode) === NoMode
1545 + (completedWork.mode & ProfileMode) === NoMode
1546 ) {
1546 - next = completeWork(current, workInProgress, renderExpirationTime);
1547 + next = completeWork(current, completedWork, renderExpirationTime);
1548 } else {
1548 - startProfilerTimer(workInProgress);
1549 - next = completeWork(current, workInProgress, renderExpirationTime);
1549 + startProfilerTimer(completedWork);
1550 + next = completeWork(current, completedWork, renderExpirationTime);
1551 // Update render duration assuming we didn't error.
1551 - stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
1552 + stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
1553 }
1554 resetCurrentDebugFiberInDEV();
1554 - resetChildExpirationTime(workInProgress);
1555 + resetChildExpirationTime(completedWork);
1556
1557 if (next !== null) {
1558 // Completing this fiber spawned new work. Work on that next.
1558 - return next;
1559 + workInProgress = next;
1560 + return;
1561 }
1562
1563 if (
@@ -1567,13 +1569,13 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1569 // list of the parent. The completion order of the children affects the
1570 // side-effect order.
1571 if (returnFiber.firstEffect === null) {
1570 - returnFiber.firstEffect = workInProgress.firstEffect;
1572 + returnFiber.firstEffect = completedWork.firstEffect;
1573 }
1572 - if (workInProgress.lastEffect !== null) {
1574 + if (completedWork.lastEffect !== null) {
1575 if (returnFiber.lastEffect !== null) {
1574 - returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
1576 + returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
1577 }
1576 - returnFiber.lastEffect = workInProgress.lastEffect;
1578 + returnFiber.lastEffect = completedWork.lastEffect;
1579 }
1580
1581 // If this fiber had side-effects, we append it AFTER the children's
@@ -1582,43 +1584,43 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1584 // schedule our own side-effect on our own list because if end up
1585 // reusing children we'll schedule this effect onto itself since we're
1586 // at the end.
1585 - const effectTag = workInProgress.effectTag;
1587 + const effectTag = completedWork.effectTag;
1588
1589 // Skip both NoWork and PerformedWork tags when creating the effect
1590 // list. PerformedWork effect is read by React DevTools but shouldn't be
1591 // committed.
1592 if (effectTag > PerformedWork) {
1593 if (returnFiber.lastEffect !== null) {
1592 - returnFiber.lastEffect.nextEffect = workInProgress;
1594 + returnFiber.lastEffect.nextEffect = completedWork;
1595 } else {
1594 - returnFiber.firstEffect = workInProgress;
1596 + returnFiber.firstEffect = completedWork;
1597 }
1596 - returnFiber.lastEffect = workInProgress;
1598 + returnFiber.lastEffect = completedWork;
1599 }
1600 }
1601 } else {
1602 // This fiber did not complete because something threw. Pop values off
1603 // the stack without entering the complete phase. If this is a boundary,
1604 // capture values if possible.
1603 - const next = unwindWork(workInProgress, renderExpirationTime);
1605 + const next = unwindWork(completedWork, renderExpirationTime);
1606
1607 // Because this fiber did not complete, don't reset its expiration time.
1608
1609 if (
1610 enableProfilerTimer &&
1609 - (workInProgress.mode & ProfileMode) !== NoMode
1611 + (completedWork.mode & ProfileMode) !== NoMode
1612 ) {
1613 // Record the render duration for the fiber that errored.
1612 - stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
1614 + stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
1615
1616 // Include the time spent working on failed children before continuing.
1615 - let actualDuration = workInProgress.actualDuration;
1616 - let child = workInProgress.child;
1617 + let actualDuration = completedWork.actualDuration;
1618 + let child = completedWork.child;
1619 while (child !== null) {
1620 actualDuration += child.actualDuration;
1621 child = child.sibling;
1622 }
1621 - workInProgress.actualDuration = actualDuration;
1623 + completedWork.actualDuration = actualDuration;
1624 }
1625
1626 if (next !== null) {
@@ -1627,7 +1629,8 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1629 // Since we're restarting, remove anything that is not a host effect
1630 // from the effect tag.
1631 next.effectTag &= HostEffectMask;
1630 - return next;
1632 + workInProgress = next;
1633 + return;
1634 }
1635
1636 if (returnFiber !== null) {
@@ -1637,20 +1640,22 @@ function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
1640 }
1641 }
1642
1640 - const siblingFiber = workInProgress.sibling;
1643 + const siblingFiber = completedWork.sibling;
1644 if (siblingFiber !== null) {
1645 // If there is more work to do in this returnFiber, do that next.
1643 - return siblingFiber;
1646 + workInProgress = siblingFiber;
1647 + return;
1648 }
1649 // Otherwise, return to the parent
1646 - workInProgress = returnFiber;
1647 - } while (workInProgress !== null);
1650 + completedWork = returnFiber;
1651 + // Update the next thing we're working on in case something throws.
1652 + workInProgress = completedWork;
1653 + } while (completedWork !== null);
1654
1655 // We've reached the root.
1656 if (workInProgressRootExitStatus === RootIncomplete) {
1657 workInProgressRootExitStatus = RootCompleted;
1658 }
1653 - return null;
1659 }
1660
1661 function getRemainingExpirationTime(fiber: Fiber) {