Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flip expiration times #13912

Merged
merged 3 commits into from
Oct 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ ReactRoot.prototype.createBatch = function(): Batch {
let insertBefore = firstBatch;
while (
insertBefore !== null &&
insertBefore._expirationTime <= expirationTime
insertBefore._expirationTime >= expirationTime
) {
insertAfter = insertBefore;
insertBefore = insertBefore._next;
Expand Down
5 changes: 3 additions & 2 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -956,9 +956,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
rootID: string = DEFAULT_ROOT_ID,
) {
const root: any = roots.get(rootID);
const expiration = NoopRenderer.computeUniqueAsyncExpiration();
const batch = {
_defer: true,
_expirationTime: 1,
_expirationTime: expiration,
_onComplete: () => {
root.firstBatch = null;
},
Expand All @@ -969,7 +970,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
expect(actual).toEqual(expectedFlush);
return (expectedCommit: Array<mixed>) => {
batch._defer = false;
NoopRenderer.flushRoot(root, 1);
NoopRenderer.flushRoot(root, expiration);
expect(yieldedValues).toEqual(expectedCommit);
};
},
Expand Down
21 changes: 5 additions & 16 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,7 @@ function updateMemoComponent(
return child;
}
let currentChild = ((current.child: any): Fiber); // This is always exactly one child
if (
updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime
) {
if (updateExpirationTime < renderExpirationTime) {
// This will be the props with resolved defaultProps,
// unlike current.memoizedProps which will be the unresolved ones.
const prevProps = currentChild.memoizedProps;
Expand Down Expand Up @@ -302,11 +299,7 @@ function updateSimpleMemoComponent(
updateExpirationTime,
renderExpirationTime: ExpirationTime,
): null | Fiber {
if (
current !== null &&
(updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime)
) {
if (current !== null && updateExpirationTime < renderExpirationTime) {
const prevProps = current.memoizedProps;
if (
shallowEqual(prevProps, nextProps) &&
Expand Down Expand Up @@ -1471,10 +1464,7 @@ function bailoutOnAlreadyFinishedWork(

// Check if the children have any pending work.
const childExpirationTime = workInProgress.childExpirationTime;
if (
childExpirationTime === NoWork ||
childExpirationTime > renderExpirationTime
) {
if (childExpirationTime < renderExpirationTime) {
// The children don't have any work either. We can skip them.
// TODO: Once we add back resuming, we should check if the children are
// a work-in-progress set. If so, we need to transfer their effects.
Expand All @@ -1500,8 +1490,7 @@ function beginWork(
if (
oldProps === newProps &&
!hasLegacyContextChanged() &&
(updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime)
updateExpirationTime < renderExpirationTime
) {
// This fiber does not have any pending work. Bailout without entering
// the begin phase. There's still some bookkeeping we that needs to be done
Expand Down Expand Up @@ -1550,7 +1539,7 @@ function beginWork(
primaryChildFragment.childExpirationTime;
if (
primaryChildExpirationTime !== NoWork &&
primaryChildExpirationTime <= renderExpirationTime
primaryChildExpirationTime >= renderExpirationTime
) {
// The primary children have pending work. Use the normal path
// to attempt to render the primary children again.
Expand Down
14 changes: 7 additions & 7 deletions packages/react-reconciler/src/ReactFiberExpirationTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';
export type ExpirationTime = number;

export const NoWork = 0;
export const Sync = 1;
export const Never = MAX_SIGNED_31_BIT_INT;
export const Never = 1;
export const Sync = MAX_SIGNED_31_BIT_INT;

const UNIT_SIZE = 10;
const MAGIC_NUMBER_OFFSET = 2;
const MAGIC_NUMBER_OFFSET = MAX_SIGNED_31_BIT_INT - 1;

// 1 unit of expiration time represents 10ms.
export function msToExpirationTime(ms: number): ExpirationTime {
// Always add an offset so that we don't clash with the magic number for NoWork.
return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET;
return MAGIC_NUMBER_OFFSET - ((ms / UNIT_SIZE) | 0);
}

export function expirationTimeToMs(expirationTime: ExpirationTime): number {
return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE;
return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}

function ceiling(num: number, precision: number): number {
Expand All @@ -38,9 +38,9 @@ function computeExpirationBucket(
bucketSizeMs,
): ExpirationTime {
return (
MAGIC_NUMBER_OFFSET +
MAGIC_NUMBER_OFFSET -
ceiling(
currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE,
MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE,
bucketSizeMs / UNIT_SIZE,
)
);
Expand Down
7 changes: 2 additions & 5 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export function useReducer<S, A>(
let didSkip = false;
do {
const updateExpirationTime = update.expirationTime;
if (updateExpirationTime > renderExpirationTime) {
if (updateExpirationTime < renderExpirationTime) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
Expand All @@ -414,10 +414,7 @@ export function useReducer<S, A>(
newBaseState = newState;
}
// Update the remaining priority in the queue.
if (
remainingExpirationTime === NoWork ||
updateExpirationTime < remainingExpirationTime
) {
if (updateExpirationTime > remainingExpirationTime) {
remainingExpirationTime = updateExpirationTime;
}
} else {
Expand Down
20 changes: 5 additions & 15 deletions packages/react-reconciler/src/ReactFiberNewContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import warningWithoutStack from 'shared/warningWithoutStack';
import {isPrimaryRenderer} from './ReactFiberHostConfig';
import {createCursor, push, pop} from './ReactFiberStack';
import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';
import {NoWork} from './ReactFiberExpirationTime';
import {ContextProvider, ClassComponent} from 'shared/ReactWorkTags';

import invariant from 'shared/invariant';
Expand Down Expand Up @@ -169,17 +168,13 @@ export function propagateContextChange(
enqueueUpdate(fiber, update);
}

if (
fiber.expirationTime === NoWork ||
fiber.expirationTime > renderExpirationTime
) {
if (fiber.expirationTime < renderExpirationTime) {
fiber.expirationTime = renderExpirationTime;
}
let alternate = fiber.alternate;
if (
alternate !== null &&
(alternate.expirationTime === NoWork ||
alternate.expirationTime > renderExpirationTime)
alternate.expirationTime < renderExpirationTime
) {
alternate.expirationTime = renderExpirationTime;
}
Expand All @@ -188,22 +183,17 @@ export function propagateContextChange(
let node = fiber.return;
while (node !== null) {
alternate = node.alternate;
if (
node.childExpirationTime === NoWork ||
node.childExpirationTime > renderExpirationTime
) {
if (node.childExpirationTime < renderExpirationTime) {
node.childExpirationTime = renderExpirationTime;
if (
alternate !== null &&
(alternate.childExpirationTime === NoWork ||
alternate.childExpirationTime > renderExpirationTime)
alternate.childExpirationTime < renderExpirationTime
) {
alternate.childExpirationTime = renderExpirationTime;
}
} else if (
alternate !== null &&
(alternate.childExpirationTime === NoWork ||
alternate.childExpirationTime > renderExpirationTime)
alternate.childExpirationTime < renderExpirationTime
) {
alternate.childExpirationTime = renderExpirationTime;
} else {
Expand Down
52 changes: 20 additions & 32 deletions packages/react-reconciler/src/ReactFiberPendingPriority.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ export function markPendingPriorityLevel(
// No other pending updates.
root.earliestPendingTime = root.latestPendingTime = expirationTime;
} else {
if (earliestPendingTime > expirationTime) {
if (earliestPendingTime < expirationTime) {
// This is the earliest pending update.
root.earliestPendingTime = expirationTime;
} else {
const latestPendingTime = root.latestPendingTime;
if (latestPendingTime < expirationTime) {
if (latestPendingTime > expirationTime) {
// This is the latest pending update
root.latestPendingTime = expirationTime;
}
Expand Down Expand Up @@ -65,12 +65,12 @@ export function markCommittedPriorityLevels(
// Let's see if the previous latest known pending level was just flushed.
const latestPendingTime = root.latestPendingTime;
if (latestPendingTime !== NoWork) {
if (latestPendingTime < earliestRemainingTime) {
if (latestPendingTime > earliestRemainingTime) {
// We've flushed all the known pending levels.
root.earliestPendingTime = root.latestPendingTime = NoWork;
} else {
const earliestPendingTime = root.earliestPendingTime;
if (earliestPendingTime < earliestRemainingTime) {
if (earliestPendingTime > earliestRemainingTime) {
// We've flushed the earliest known pending level. Set this to the
// latest pending time.
root.earliestPendingTime = root.latestPendingTime;
Expand All @@ -92,7 +92,7 @@ export function markCommittedPriorityLevels(
}

const latestSuspendedTime = root.latestSuspendedTime;
if (earliestRemainingTime > latestSuspendedTime) {
if (earliestRemainingTime < latestSuspendedTime) {
// The earliest remaining level is later than all the suspended work. That
// means we've flushed all the suspended work.
root.earliestSuspendedTime = NoWork;
Expand All @@ -106,7 +106,7 @@ export function markCommittedPriorityLevels(
return;
}

if (earliestRemainingTime < earliestSuspendedTime) {
if (earliestRemainingTime > earliestSuspendedTime) {
// The earliest remaining time is earlier than all the suspended work.
// Treat it as a pending update.
markPendingPriorityLevel(root, earliestRemainingTime);
Expand All @@ -128,10 +128,10 @@ export function hasLowerPriorityWork(
const latestPingedTime = root.latestPingedTime;
return (
(latestPendingTime !== NoWork &&
latestPendingTime > erroredExpirationTime) ||
latestPendingTime < erroredExpirationTime) ||
(latestSuspendedTime !== NoWork &&
latestSuspendedTime > erroredExpirationTime) ||
(latestPingedTime !== NoWork && latestPingedTime > erroredExpirationTime)
latestSuspendedTime < erroredExpirationTime) ||
(latestPingedTime !== NoWork && latestPingedTime < erroredExpirationTime)
);
}

Expand All @@ -143,8 +143,8 @@ export function isPriorityLevelSuspended(
const latestSuspendedTime = root.latestSuspendedTime;
return (
earliestSuspendedTime !== NoWork &&
expirationTime >= earliestSuspendedTime &&
expirationTime <= latestSuspendedTime
expirationTime <= earliestSuspendedTime &&
expirationTime >= latestSuspendedTime
);
}

Expand Down Expand Up @@ -180,10 +180,10 @@ export function markSuspendedPriorityLevel(
// No other suspended levels.
root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
} else {
if (earliestSuspendedTime > suspendedTime) {
if (earliestSuspendedTime < suspendedTime) {
// This is the earliest suspended level.
root.earliestSuspendedTime = suspendedTime;
} else if (latestSuspendedTime < suspendedTime) {
} else if (latestSuspendedTime > suspendedTime) {
// This is the latest suspended level
root.latestSuspendedTime = suspendedTime;
}
Expand All @@ -202,7 +202,7 @@ export function markPingedPriorityLevel(
// is thrown out and not reused during the restarted render. One way to
// invalidate the progressed work is to restart at expirationTime + 1.
const latestPingedTime = root.latestPingedTime;
if (latestPingedTime === NoWork || latestPingedTime < pingedTime) {
if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
root.latestPingedTime = pingedTime;
}
findNextExpirationTimeToWorkOn(pingedTime, root);
Expand All @@ -212,7 +212,7 @@ function clearPing(root, completedTime) {
// TODO: Track whether the root was pinged during the render phase. If so,
// we need to make sure we don't lose track of it.
const latestPingedTime = root.latestPingedTime;
if (latestPingedTime !== NoWork && latestPingedTime <= completedTime) {
if (latestPingedTime !== NoWork && latestPingedTime >= completedTime) {
root.latestPingedTime = NoWork;
}
}
Expand All @@ -225,18 +225,10 @@ export function findEarliestOutstandingPriorityLevel(

const earliestPendingTime = root.earliestPendingTime;
const earliestSuspendedTime = root.earliestSuspendedTime;
if (
earliestExpirationTime === NoWork ||
(earliestPendingTime !== NoWork &&
earliestPendingTime < earliestExpirationTime)
) {
if (earliestPendingTime > earliestExpirationTime) {
earliestExpirationTime = earliestPendingTime;
}
if (
earliestExpirationTime === NoWork ||
(earliestSuspendedTime !== NoWork &&
earliestSuspendedTime < earliestExpirationTime)
) {
if (earliestSuspendedTime > earliestExpirationTime) {
earliestExpirationTime = earliestSuspendedTime;
}
return earliestExpirationTime;
Expand All @@ -247,7 +239,7 @@ export function didExpireAtExpirationTime(
currentTime: ExpirationTime,
): void {
const expirationTime = root.expirationTime;
if (expirationTime !== NoWork && currentTime >= expirationTime) {
if (expirationTime !== NoWork && currentTime <= expirationTime) {
// The root has expired. Flush all work up to the current time.
root.nextExpirationTimeToWorkOn = currentTime;
}
Expand All @@ -269,7 +261,7 @@ function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
if (
nextExpirationTimeToWorkOn === NoWork &&
(completedExpirationTime === NoWork ||
latestSuspendedTime > completedExpirationTime)
latestSuspendedTime < completedExpirationTime)
) {
// The lowest priority suspended work is the work most likely to be
// committed next. Let's start rendering it again, so that if it times out,
Expand All @@ -278,11 +270,7 @@ function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
}

let expirationTime = nextExpirationTimeToWorkOn;
if (
expirationTime !== NoWork &&
earliestSuspendedTime !== NoWork &&
earliestSuspendedTime < expirationTime
) {
if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
// Expire using the earliest known expiration time.
expirationTime = earliestSuspendedTime;
}
Expand Down
Loading