Skip to content

Commit

Permalink
Fork ReactFiberScheduler with feature flag
Browse files Browse the repository at this point in the history
Adds a feature flag `enableNewScheduler` that toggles between two
implementations of ReactFiberScheduler. This will let us land changes in
master while preserving the ability to quickly rollback.

Ideally this will be a short-lived fork. Once we've tested the new
scheduler for a week or so without issues, we will get rid of it. Until
then, we'll need to maintain two parallel implementations and run tests
against both of them. We rarely land changes to ReactFiberScheduler, so
I don't expect this will be a huge burden.

This commit does not implement anything new. The flag is still off and
tests run against the existing implementation.

Use `yarn test-new-scheduler` to run tests against the new one.
  • Loading branch information
acdlite committed Mar 20, 2019
1 parent 45f5717 commit b1a56ab
Show file tree
Hide file tree
Showing 19 changed files with 202 additions and 9 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +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-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
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ import {
createWorkInProgress,
isSimpleFunctionComponent,
} from './ReactFiber';
import {requestCurrentTime, retryTimedOutBoundary} from './ReactFiberScheduler.old';
import {requestCurrentTime, retryTimedOutBoundary} from './ReactFiberScheduler';

const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;

Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
computeExpirationForFiber,
scheduleWork,
flushPassiveEffects,
} from './ReactFiberScheduler.old';
} from './ReactFiberScheduler';

const fakeInternalInstance = {};
const isArray = Array.isArray;
Expand Down
4 changes: 2 additions & 2 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {CapturedValue, CapturedError} from './ReactCapturedValue';
import type {SuspenseState} from './ReactFiberSuspenseComponent';
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';
import type {Thenable} from './ReactFiberScheduler.old';
import type {Thenable} from './ReactFiberScheduler';

import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
import {
Expand Down Expand Up @@ -95,7 +95,7 @@ import {
captureCommitPhaseError,
requestCurrentTime,
resolveRetryThenable,
} from './ReactFiberScheduler.old';
} from './ReactFiberScheduler';
import {
NoEffect as NoHookEffect,
UnmountSnapshot,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
computeExpirationForFiber,
flushPassiveEffects,
requestCurrentTime,
} from './ReactFiberScheduler.old';
} from './ReactFiberScheduler';

import invariant from 'shared/invariant';
import warning from 'shared/warning';
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
interactiveUpdates,
flushInteractiveUpdates,
flushPassiveEffects,
} from './ReactFiberScheduler.old';
} from './ReactFiberScheduler';
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue';
import ReactFiberInstrumentation from './ReactFiberInstrumentation';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig';
import type {Thenable} from './ReactFiberScheduler.old';
import type {Thenable} from './ReactFiberScheduler';
import type {Interaction} from 'scheduler/src/Tracing';

import {noTimeout} from './ReactFiberHostConfig';
Expand Down
124 changes: 124 additions & 0 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {enableNewScheduler} from 'shared/ReactFeatureFlags';

import {
requestCurrentTime as requestCurrentTime_old,
computeExpirationForFiber as computeExpirationForFiber_old,
captureCommitPhaseError as captureCommitPhaseError_old,
onUncaughtError as onUncaughtError_old,
renderDidSuspend as renderDidSuspend_old,
renderDidError as renderDidError_old,
pingSuspendedRoot as pingSuspendedRoot_old,
retryTimedOutBoundary as retryTimedOutBoundary_old,
resolveRetryThenable as resolveRetryThenable_old,
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,
flushSync as flushSync_old,
flushControlled as flushControlled_old,
deferredUpdates as deferredUpdates_old,
syncUpdates as syncUpdates_old,
interactiveUpdates as interactiveUpdates_old,
flushInteractiveUpdates as flushInteractiveUpdates_old,
computeUniqueAsyncExpiration as computeUniqueAsyncExpiration_old,
flushPassiveEffects as flushPassiveEffects_old,
warnIfNotCurrentlyBatchingInDev as warnIfNotCurrentlyBatchingInDev_old,
} from './ReactFiberScheduler.old';

import {
requestCurrentTime as requestCurrentTime_new,
computeExpirationForFiber as computeExpirationForFiber_new,
captureCommitPhaseError as captureCommitPhaseError_new,
onUncaughtError as onUncaughtError_new,
renderDidSuspend as renderDidSuspend_new,
renderDidError as renderDidError_new,
pingSuspendedRoot as pingSuspendedRoot_new,
retryTimedOutBoundary as retryTimedOutBoundary_new,
resolveRetryThenable as resolveRetryThenable_new,
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,
flushSync as flushSync_new,
flushControlled as flushControlled_new,
deferredUpdates as deferredUpdates_new,
syncUpdates as syncUpdates_new,
interactiveUpdates as interactiveUpdates_new,
flushInteractiveUpdates as flushInteractiveUpdates_new,
computeUniqueAsyncExpiration as computeUniqueAsyncExpiration_new,
flushPassiveEffects as flushPassiveEffects_new,
warnIfNotCurrentlyBatchingInDev as warnIfNotCurrentlyBatchingInDev_new,
} from './ReactFiberScheduler.new';

export let requestCurrentTime = requestCurrentTime_old;
export let computeExpirationForFiber = computeExpirationForFiber_old;
export let captureCommitPhaseError = captureCommitPhaseError_old;
export let onUncaughtError = onUncaughtError_old;
export let renderDidSuspend = renderDidSuspend_old;
export let renderDidError = renderDidError_old;
export let pingSuspendedRoot = pingSuspendedRoot_old;
export let retryTimedOutBoundary = retryTimedOutBoundary_old;
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;
export let flushSync = flushSync_old;
export let flushControlled = flushControlled_old;
export let deferredUpdates = deferredUpdates_old;
export let syncUpdates = syncUpdates_old;
export let interactiveUpdates = interactiveUpdates_old;
export let flushInteractiveUpdates = flushInteractiveUpdates_old;
export let computeUniqueAsyncExpiration = computeUniqueAsyncExpiration_old;
export let flushPassiveEffects = flushPassiveEffects_old;
export let warnIfNotCurrentlyBatchingInDev = warnIfNotCurrentlyBatchingInDev_old;

if (enableNewScheduler) {
requestCurrentTime = requestCurrentTime_new;
computeExpirationForFiber = computeExpirationForFiber_new;
captureCommitPhaseError = captureCommitPhaseError_new;
onUncaughtError = onUncaughtError_new;
renderDidSuspend = renderDidSuspend_new;
renderDidError = renderDidError_new;
pingSuspendedRoot = pingSuspendedRoot_new;
retryTimedOutBoundary = retryTimedOutBoundary_new;
resolveRetryThenable = resolveRetryThenable_new;
markLegacyErrorBoundaryAsFailed = markLegacyErrorBoundaryAsFailed_new;
isAlreadyFailedLegacyErrorBoundary = isAlreadyFailedLegacyErrorBoundary_new;
scheduleWork = scheduleWork_new;
requestWork = requestWork_new;
flushRoot = flushRoot_new;
batchedUpdates = batchedUpdates_new;
unbatchedUpdates = unbatchedUpdates_new;
flushSync = flushSync_new;
flushControlled = flushControlled_new;
deferredUpdates = deferredUpdates_new;
syncUpdates = syncUpdates_new;
interactiveUpdates = interactiveUpdates_new;
flushInteractiveUpdates = flushInteractiveUpdates_new;
computeUniqueAsyncExpiration = computeUniqueAsyncExpiration_new;
flushPassiveEffects = flushPassiveEffects_new;
warnIfNotCurrentlyBatchingInDev = warnIfNotCurrentlyBatchingInDev_new;
}

export type Thenable = {
then(resolve: () => mixed, reject?: () => mixed): mixed,
};
36 changes: 36 additions & 0 deletions packages/react-reconciler/src/ReactFiberScheduler.new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

function notYetImplemented() {
throw new Error('Not yet implemented.');
}

export const requestCurrentTime = notYetImplemented;
export const computeExpirationForFiber = notYetImplemented;
export const captureCommitPhaseError = notYetImplemented;
export const onUncaughtError = notYetImplemented;
export const renderDidSuspend = notYetImplemented;
export const renderDidError = notYetImplemented;
export const pingSuspendedRoot = notYetImplemented;
export const retryTimedOutBoundary = notYetImplemented;
export const resolveRetryThenable = notYetImplemented;
export const markLegacyErrorBoundaryAsFailed = notYetImplemented;
export const isAlreadyFailedLegacyErrorBoundary = notYetImplemented;
export const scheduleWork = notYetImplemented;
export const requestWork = notYetImplemented;
export const flushRoot = notYetImplemented;
export const batchedUpdates = notYetImplemented;
export const unbatchedUpdates = notYetImplemented;
export const flushSync = notYetImplemented;
export const flushControlled = notYetImplemented;
export const deferredUpdates = notYetImplemented;
export const syncUpdates = notYetImplemented;
export const interactiveUpdates = notYetImplemented;
export const flushInteractiveUpdates = notYetImplemented;
export const computeUniqueAsyncExpiration = notYetImplemented;
export const flushPassiveEffects = notYetImplemented;
export const warnIfNotCurrentlyBatchingInDev = notYetImplemented;
4 changes: 2 additions & 2 deletions packages/react-reconciler/src/ReactFiberUnwindWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {FiberRoot} from './ReactFiberRoot';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {CapturedValue} from './ReactCapturedValue';
import type {Update} from './ReactUpdateQueue';
import type {Thenable} from './ReactFiberScheduler.old';
import type {Thenable} from './ReactFiberScheduler';
import type {SuspenseState} from './ReactFiberSuspenseComponent';

import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
Expand Down Expand Up @@ -67,7 +67,7 @@ import {
isAlreadyFailedLegacyErrorBoundary,
pingSuspendedRoot,
resolveRetryThenable,
} from './ReactFiberScheduler.old';
} from './ReactFiberScheduler';

import invariant from 'shared/invariant';
import maxSigned31BitInt from './maxSigned31BitInt';
Expand Down
4 changes: 4 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ export const warnAboutDeprecatedSetNativeProps = false;

// Experimental React Events support. Only used in www builds for now.
export const enableEventAPI = false;

// Enables rewritten version of ReactFiberScheduler. Added in case we need to
// quickly revert it.
export const enableNewScheduler = false;
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
export const warnAboutDeprecatedLifecycles = true;
export const warnAboutDeprecatedSetNativeProps = true;
export const enableEventAPI = false;
export const enableNewScheduler = false;

// Only used in www builds.
export function addUserTimingListener() {
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
export const enableNewScheduler = false;

// Only used in www builds.
export function addUserTimingListener() {
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.persistent.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
export const enableNewScheduler = false;

// Only used in www builds.
export function addUserTimingListener() {
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const warnAboutShorthandPropertyCollision = false;
export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableEventAPI = false;
export const enableNewScheduler = false;

// Only used in www builds.
export function addUserTimingListener() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
export const disableJavaScriptURLs = false;
export const disableYielding = false;
export const enableEventAPI = true;
export const enableNewScheduler = false;

// Only used in www builds.
export function addUserTimingListener() {
Expand Down
5 changes: 5 additions & 0 deletions packages/shared/forks/ReactFeatureFlags.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export const enableStableConcurrentModeAPIs = false;

export const enableSuspenseServerRenderer = true;

// I've chosen to make this a static flag instead of a dynamic flag controlled
// by a GK so that it doesn't increase bundle size. It should still be easy
// to rollback by reverting the commit that turns this on.
export const enableNewScheduler = false;

let refCount = 0;
export function addUserTimingListener() {
if (__DEV__) {
Expand Down
11 changes: 11 additions & 0 deletions scripts/jest/config.source-new-scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

const baseConfig = require('./config.base');

module.exports = Object.assign({}, baseConfig, {
setupFiles: [
...baseConfig.setupFiles,
require.resolve('./setupNewScheduler.js'),
require.resolve('./setupHostConfigs.js'),
],
});
7 changes: 7 additions & 0 deletions scripts/jest/setupNewScheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

jest.mock('shared/ReactFeatureFlags', () => {
const ReactFeatureFlags = require.requireActual('shared/ReactFeatureFlags');
ReactFeatureFlags.enableNewScheduler = true;
return ReactFeatureFlags;
});

0 comments on commit b1a56ab

Please sign in to comment.