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

Switch to new ReactFiberScheduler implementation #15196

Closed
wants to merge 2 commits into from
Closed
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js",
"test-persistent": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-persistent.js",
"test-fire": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-fire.js",
"test-new-scheduler": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-new-scheduler.js",
"test-old-scheduler": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-old-scheduler.js",
"test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js",
"test-fire-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source-fire.js",
"test-prod-build": "yarn test-build-prod",
Expand Down
7 changes: 5 additions & 2 deletions packages/react-cache/src/LRU.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import * as Scheduler from 'scheduler';

// Intentionally not named imports because Rollup would
// use dynamic dispatch for CommonJS interop named imports.
const {unstable_scheduleCallback: scheduleCallback} = Scheduler;
const {
unstable_scheduleCallback: scheduleCallback,
unstable_IdlePriority: IdlePriority,
} = Scheduler;

type Entry<T> = {|
value: T,
Expand All @@ -34,7 +37,7 @@ export function createLRU<T>(limit: number) {
// The cache size exceeds the limit. Schedule a callback to delete the
// least recently used entries.
cleanUpIsScheduled = true;
scheduleCallback(cleanUp);
scheduleCallback(IdlePriority, cleanUp);
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/react-dom/src/__tests__/ReactDOMHooks-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ describe('ReactDOMHooks', () => {
expect(container3.textContent).toBe('6');
});

it('can batch synchronous work inside effects with other work', () => {
// TODO: This behavior is wrong. Fix this in the old implementation.
it.skip('can batch synchronous work inside effects with other work', () => {
let otherContainer = document.createElement('div');

let calledA = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ let ReactDOM;
let Suspense;
let ReactCache;
let ReactTestUtils;
let Scheduler;
let TextResource;
let act;

Expand All @@ -26,6 +27,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
ReactDOM = require('react-dom');
ReactCache = require('react-cache');
ReactTestUtils = require('react-dom/test-utils');
Scheduler = require('scheduler');
act = ReactTestUtils.act;
Suspense = React.Suspense;
container = document.createElement('div');
Expand Down Expand Up @@ -94,6 +96,8 @@ describe('ReactDOMSuspensePlaceholder', () => {

await advanceTimers(1000);

Scheduler.flushAll();

expect(divs[0].current.style.display).toEqual('');
expect(divs[1].current.style.display).toEqual('');
// This div's display was set with a prop.
Expand All @@ -115,6 +119,8 @@ describe('ReactDOMSuspensePlaceholder', () => {

await advanceTimers(1000);

Scheduler.flushAll();

expect(container.textContent).toEqual('ABC');
});

Expand Down Expand Up @@ -160,6 +166,8 @@ describe('ReactDOMSuspensePlaceholder', () => {

await advanceTimers(1000);

Scheduler.flushAll();

expect(container.innerHTML).toEqual(
'<span style="display: inline;">Sibling</span><span style="">Async</span>',
);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactDebugFiberPerf.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export function stopRequestCallbackTimer(
expirationTime: number,
): void {
if (enableUserTimingAPI) {
if (supportsUserTiming) {
if (supportsUserTiming && isWaitingForCallback) {
isWaitingForCallback = false;
const warning = didExpire ? 'React was blocked by main thread' : null;
endMark(
Expand Down
39 changes: 39 additions & 0 deletions packages/react-reconciler/src/ReactFiberExpirationTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@
* @flow
*/

import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';

import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';

import {
ImmediatePriority,
UserBlockingPriority,
NormalPriority,
IdlePriority,
} from './SchedulerWithReactIntegration';

export type ExpirationTime = number;

export const NoWork = 0;
Expand Down Expand Up @@ -46,6 +55,8 @@ function computeExpirationBucket(
);
}

// TODO: This corresponds to Scheduler's NormalPriority, not LowPriority. Update
// the names to reflect.
export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;

Expand Down Expand Up @@ -80,3 +91,31 @@ export function computeInteractiveExpiration(currentTime: ExpirationTime) {
HIGH_PRIORITY_BATCH_SIZE,
);
}

export function inferPriorityFromExpirationTime(
currentTime: ExpirationTime,
expirationTime: ExpirationTime,
): ReactPriorityLevel {
if (expirationTime === Sync) {
return ImmediatePriority;
}
if (expirationTime === Never) {
return IdlePriority;
}
const msUntil =
msToExpirationTime(expirationTime) - msToExpirationTime(currentTime);
if (msUntil <= 0) {
return ImmediatePriority;
}
if (msUntil <= HIGH_PRIORITY_EXPIRATION) {
return UserBlockingPriority;
}
if (msUntil <= LOW_PRIORITY_EXPIRATION) {
return NormalPriority;
}

// TODO: Handle LowPriority

// Assume anything lower has idle priority
return IdlePriority;
}
2 changes: 0 additions & 2 deletions packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import {
requestCurrentTime,
computeExpirationForFiber,
scheduleWork,
requestWork,
flushRoot,
batchedUpdates,
unbatchedUpdates,
Expand Down Expand Up @@ -300,7 +299,6 @@ export function updateContainer(

export {
flushRoot,
requestWork,
computeUniqueAsyncExpiration,
batchedUpdates,
unbatchedUpdates,
Expand Down
19 changes: 19 additions & 0 deletions packages/react-reconciler/src/ReactFiberRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ type BaseFiberRootProperties = {|
firstBatch: Batch | null,
// Linked-list of roots
nextScheduledRoot: FiberRoot | null,

// New Scheduler fields
callbackNode: *,
callbackExpirationTime: ExpirationTime,
firstPendingTime: ExpirationTime,
lastPendingTime: ExpirationTime,
pingTime: ExpirationTime,
|};

// The following attributes are only used by interaction tracing builds.
Expand Down Expand Up @@ -145,6 +152,12 @@ export function createFiberRoot(
interactionThreadID: unstable_getThreadID(),
memoizedInteractions: new Set(),
pendingInteractionMap: new Map(),

callbackNode: null,
callbackExpirationTime: NoWork,
firstPendingTime: NoWork,
lastPendingTime: NoWork,
pingTime: NoWork,
}: FiberRoot);
} else {
root = ({
Expand Down Expand Up @@ -172,6 +185,12 @@ export function createFiberRoot(
expirationTime: NoWork,
firstBatch: null,
nextScheduledRoot: null,

callbackNode: null,
callbackExpirationTime: NoWork,
firstPendingTime: NoWork,
lastPendingTime: NoWork,
pingTime: NoWork,
}: BaseFiberRootProperties);
}

Expand Down
8 changes: 4 additions & 4 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
markLegacyErrorBoundaryAsFailed as markLegacyErrorBoundaryAsFailed_old,
isAlreadyFailedLegacyErrorBoundary as isAlreadyFailedLegacyErrorBoundary_old,
scheduleWork as scheduleWork_old,
requestWork as requestWork_old,
flushRoot as flushRoot_old,
batchedUpdates as batchedUpdates_old,
unbatchedUpdates as unbatchedUpdates_old,
Expand All @@ -35,6 +34,7 @@ import {
computeUniqueAsyncExpiration as computeUniqueAsyncExpiration_old,
flushPassiveEffects as flushPassiveEffects_old,
warnIfNotCurrentlyBatchingInDev as warnIfNotCurrentlyBatchingInDev_old,
inferStartTimeFromExpirationTime as inferStartTimeFromExpirationTime_old,
} from './ReactFiberScheduler.old';

import {
Expand All @@ -50,7 +50,6 @@ import {
markLegacyErrorBoundaryAsFailed as markLegacyErrorBoundaryAsFailed_new,
isAlreadyFailedLegacyErrorBoundary as isAlreadyFailedLegacyErrorBoundary_new,
scheduleWork as scheduleWork_new,
requestWork as requestWork_new,
flushRoot as flushRoot_new,
batchedUpdates as batchedUpdates_new,
unbatchedUpdates as unbatchedUpdates_new,
Expand All @@ -63,6 +62,7 @@ import {
computeUniqueAsyncExpiration as computeUniqueAsyncExpiration_new,
flushPassiveEffects as flushPassiveEffects_new,
warnIfNotCurrentlyBatchingInDev as warnIfNotCurrentlyBatchingInDev_new,
inferStartTimeFromExpirationTime as inferStartTimeFromExpirationTime_new,
} from './ReactFiberScheduler.new';

export let requestCurrentTime = requestCurrentTime_old;
Expand All @@ -77,7 +77,6 @@ export let resolveRetryThenable = resolveRetryThenable_old;
export let markLegacyErrorBoundaryAsFailed = markLegacyErrorBoundaryAsFailed_old;
export let isAlreadyFailedLegacyErrorBoundary = isAlreadyFailedLegacyErrorBoundary_old;
export let scheduleWork = scheduleWork_old;
export let requestWork = requestWork_old;
export let flushRoot = flushRoot_old;
export let batchedUpdates = batchedUpdates_old;
export let unbatchedUpdates = unbatchedUpdates_old;
Expand All @@ -90,6 +89,7 @@ export let flushInteractiveUpdates = flushInteractiveUpdates_old;
export let computeUniqueAsyncExpiration = computeUniqueAsyncExpiration_old;
export let flushPassiveEffects = flushPassiveEffects_old;
export let warnIfNotCurrentlyBatchingInDev = warnIfNotCurrentlyBatchingInDev_old;
export let inferStartTimeFromExpirationTime = inferStartTimeFromExpirationTime_old;

if (enableNewScheduler) {
requestCurrentTime = requestCurrentTime_new;
Expand All @@ -104,7 +104,6 @@ if (enableNewScheduler) {
markLegacyErrorBoundaryAsFailed = markLegacyErrorBoundaryAsFailed_new;
isAlreadyFailedLegacyErrorBoundary = isAlreadyFailedLegacyErrorBoundary_new;
scheduleWork = scheduleWork_new;
requestWork = requestWork_new;
flushRoot = flushRoot_new;
batchedUpdates = batchedUpdates_new;
unbatchedUpdates = unbatchedUpdates_new;
Expand All @@ -117,6 +116,7 @@ if (enableNewScheduler) {
computeUniqueAsyncExpiration = computeUniqueAsyncExpiration_new;
flushPassiveEffects = flushPassiveEffects_new;
warnIfNotCurrentlyBatchingInDev = warnIfNotCurrentlyBatchingInDev_new;
inferStartTimeFromExpirationTime = inferStartTimeFromExpirationTime_new;
}

export type Thenable = {
Expand Down
Loading