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

Move sync task queue to its own module #21109

Merged
merged 1 commit into from
Mar 28, 2021
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
16 changes: 4 additions & 12 deletions packages/react-dom/src/events/ReactDOMEventListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,21 @@ import {
import getEventTarget from './getEventTarget';
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';

import {
enableLegacyFBSupport,
enableNewReconciler,
} from 'shared/ReactFeatureFlags';
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
import {
flushDiscreteUpdatesIfNeeded,
discreteUpdates,
} from './ReactDOMUpdateBatching';

import {getCurrentPriorityLevel as getCurrentPriorityLevel_old} from 'react-reconciler/src/SchedulerWithReactIntegration.old';
import {
getCurrentPriorityLevel as getCurrentPriorityLevel_new,
getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
IdlePriority as IdleSchedulerPriority,
ImmediatePriority as ImmediateSchedulerPriority,
LowPriority as LowSchedulerPriority,
NormalPriority as NormalSchedulerPriority,
UserBlockingPriority as UserBlockingSchedulerPriority,
} from 'react-reconciler/src/SchedulerWithReactIntegration.new';
} from 'react-reconciler/src/Scheduler';
import {
DiscreteEventPriority,
ContinuousEventPriority,
Expand All @@ -62,10 +58,6 @@ import {
setCurrentUpdatePriority,
} from 'react-reconciler/src/ReactEventPriorities';

const getCurrentPriorityLevel = enableNewReconciler
? getCurrentPriorityLevel_new
: getCurrentPriorityLevel_old;

// TODO: can we stop exporting these?
export let _enabled = true;

Expand Down Expand Up @@ -392,7 +384,7 @@ export function getEventPriority(domEventName: DOMEventName): * {
// We might be in the Scheduler callback.
// Eventually this mechanism will be replaced by a check
// of the current priority on the native scheduler.
const schedulerPriority = getCurrentPriorityLevel();
const schedulerPriority = getCurrentSchedulerPriorityLevel();
switch (schedulerPriority) {
case ImmediateSchedulerPriority:
return DiscreteEventPriority;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new';

import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new';

import {now} from './SchedulerWithReactIntegration.new';
import {now} from './Scheduler';

import {
IndeterminateComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old';

import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old';

import {now} from './SchedulerWithReactIntegration.old';
import {now} from './Scheduler';

import {
IndeterminateComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
UserBlockingPriority as UserBlockingSchedulerPriority,
NormalPriority as NormalSchedulerPriority,
IdlePriority as IdleSchedulerPriority,
} from './SchedulerWithReactIntegration.new';
} from './Scheduler';

declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
UserBlockingPriority as UserBlockingSchedulerPriority,
NormalPriority as NormalSchedulerPriority,
IdlePriority as IdleSchedulerPriority,
} from './SchedulerWithReactIntegration.old';
} from './Scheduler';

declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;

Expand Down
67 changes: 67 additions & 0 deletions packages/react-reconciler/src/ReactFiberSyncTaskQueue.new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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 type {SchedulerCallback} from './Scheduler';

import {
DiscreteEventPriority,
getCurrentUpdatePriority,
setCurrentUpdatePriority,
} from './ReactEventPriorities.new';
import {ImmediatePriority, scheduleCallback} from './Scheduler';

let syncQueue: Array<SchedulerCallback> | null = null;
let isFlushingSyncQueue: boolean = false;

export function scheduleSyncCallback(callback: SchedulerCallback) {
// Push this callback into an internal queue. We'll flush these either in
// the next tick, or earlier if something calls `flushSyncCallbackQueue`.
if (syncQueue === null) {
syncQueue = [callback];
} else {
// Push onto existing queue. Don't need to schedule a callback because
// we already scheduled one when we created the queue.
syncQueue.push(callback);
}
}

export function flushSyncCallbackQueue() {
if (!isFlushingSyncQueue && syncQueue !== null) {
// Prevent re-entrancy.
isFlushingSyncQueue = true;
let i = 0;
const previousUpdatePriority = getCurrentUpdatePriority();
try {
const isSync = true;
const queue = syncQueue;
// TODO: Is this necessary anymore? The only user code that runs in this
// queue is in the render or commit phases.
setCurrentUpdatePriority(DiscreteEventPriority);
for (; i < queue.length; i++) {
let callback = queue[i];
do {
callback = callback(isSync);
} while (callback !== null);
}
syncQueue = null;
} catch (error) {
// If something throws, leave the remaining callbacks on the queue.
if (syncQueue !== null) {
syncQueue = syncQueue.slice(i + 1);
}
// Resume flushing in the next tick
scheduleCallback(ImmediatePriority, flushSyncCallbackQueue);
throw error;
} finally {
setCurrentUpdatePriority(previousUpdatePriority);
isFlushingSyncQueue = false;
}
}
return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,15 @@
* @flow
*/

// This module only exists as an ESM wrapper around the external CommonJS
// Scheduler dependency. Notice that we're intentionally not using named imports
// because Rollup would use dynamic dispatch for CommonJS interop named imports.
// When we switch to ESM, we can delete this module.
import * as Scheduler from 'scheduler';
import {__interactionsRef} from 'scheduler/tracing';
import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
import type {SchedulerCallback} from './Scheduler';

import {
DiscreteEventPriority,
getCurrentUpdatePriority,
setCurrentUpdatePriority,
} from './ReactEventPriorities.old';
import {ImmediatePriority, scheduleCallback} from './Scheduler';

export const scheduleCallback = Scheduler.unstable_scheduleCallback;
export const cancelCallback = Scheduler.unstable_cancelCallback;
export const shouldYield = Scheduler.unstable_shouldYield;
export const requestPaint = Scheduler.unstable_requestPaint;
export const now = Scheduler.unstable_now;
export const getCurrentPriorityLevel =
Scheduler.unstable_getCurrentPriorityLevel;
export const ImmediatePriority = Scheduler.unstable_ImmediatePriority;
export const UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
export const NormalPriority = Scheduler.unstable_NormalPriority;
export const LowPriority = Scheduler.unstable_LowPriority;
export const IdlePriority = Scheduler.unstable_IdlePriority;

if (enableSchedulerTracing) {
// Provide explicit error message when production+profiling bundle of e.g.
// react-dom is used with production (non-profiling) bundle of
// scheduler/tracing
invariant(
__interactionsRef != null && __interactionsRef.current != null,
'It is not supported to run the profiling version of a renderer (for ' +
'example, `react-dom/profiling`) without also replacing the ' +
'`scheduler/tracing` module with `scheduler/tracing-profiling`. Your ' +
'bundler might have a setting for aliasing both modules. Learn more at ' +
'https://reactjs.org/link/profiling',
);
}

export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;

// TODO: Move sync task queue to its own module.
let syncQueue: Array<SchedulerCallback> | null = null;
let isFlushingSyncQueue: boolean = false;

Expand Down
4 changes: 3 additions & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ import {
UserBlockingPriority as UserBlockingSchedulerPriority,
NormalPriority as NormalSchedulerPriority,
IdlePriority as IdleSchedulerPriority,
} from './Scheduler';
import {
flushSyncCallbackQueue,
scheduleSyncCallback,
} from './SchedulerWithReactIntegration.new';
} from './ReactFiberSyncTaskQueue.new';
import {
NoFlags as NoHookEffect,
Passive as HookPassive,
Expand Down
4 changes: 3 additions & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ import {
UserBlockingPriority as UserBlockingSchedulerPriority,
NormalPriority as NormalSchedulerPriority,
IdlePriority as IdleSchedulerPriority,
} from './Scheduler';
import {
flushSyncCallbackQueue,
scheduleSyncCallback,
} from './SchedulerWithReactIntegration.old';
} from './ReactFiberSyncTaskQueue.old';
import {
NoFlags as NoHookEffect,
Passive as HookPassive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ import * as Scheduler from 'scheduler';
import {__interactionsRef} from 'scheduler/tracing';
import {enableSchedulerTracing} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
import {
DiscreteEventPriority,
getCurrentUpdatePriority,
setCurrentUpdatePriority,
} from './ReactEventPriorities.new';

export const scheduleCallback = Scheduler.unstable_scheduleCallback;
export const cancelCallback = Scheduler.unstable_cancelCallback;
Expand Down Expand Up @@ -49,54 +44,3 @@ if (enableSchedulerTracing) {
}

export type SchedulerCallback = (isSync: boolean) => SchedulerCallback | null;

// TODO: Move sync task queue to its own module.
let syncQueue: Array<SchedulerCallback> | null = null;
let isFlushingSyncQueue: boolean = false;

export function scheduleSyncCallback(callback: SchedulerCallback) {
// Push this callback into an internal queue. We'll flush these either in
// the next tick, or earlier if something calls `flushSyncCallbackQueue`.
if (syncQueue === null) {
syncQueue = [callback];
} else {
// Push onto existing queue. Don't need to schedule a callback because
// we already scheduled one when we created the queue.
syncQueue.push(callback);
}
}

export function flushSyncCallbackQueue() {
if (!isFlushingSyncQueue && syncQueue !== null) {
// Prevent re-entrancy.
isFlushingSyncQueue = true;
let i = 0;
const previousUpdatePriority = getCurrentUpdatePriority();
try {
const isSync = true;
const queue = syncQueue;
// TODO: Is this necessary anymore? The only user code that runs in this
// queue is in the render or commit phases.
setCurrentUpdatePriority(DiscreteEventPriority);
for (; i < queue.length; i++) {
let callback = queue[i];
do {
callback = callback(isSync);
} while (callback !== null);
}
syncQueue = null;
} catch (error) {
// If something throws, leave the remaining callbacks on the queue.
if (syncQueue !== null) {
syncQueue = syncQueue.slice(i + 1);
}
// Resume flushing in the next tick
scheduleCallback(ImmediatePriority, flushSyncCallbackQueue);
throw error;
} finally {
setCurrentUpdatePriority(previousUpdatePriority);
isFlushingSyncQueue = false;
}
}
return null;
}