From 5ca65e143ae670132cc49de47e6c9cb909154d5b Mon Sep 17 00:00:00 2001 From: Noah Lemen Date: Mon, 26 Feb 2024 11:27:46 -0500 Subject: [PATCH 1/3] clean up isInputPending --- .../scheduler/src/SchedulerFeatureFlags.js | 4 - .../scheduler/src/__tests__/Scheduler-test.js | 40 +--------- packages/scheduler/src/forks/Scheduler.js | 73 +------------------ .../SchedulerFeatureFlags.www-dynamic.js | 4 - .../src/forks/SchedulerFeatureFlags.www.js | 4 - 5 files changed, 6 insertions(+), 119 deletions(-) diff --git a/packages/scheduler/src/SchedulerFeatureFlags.js b/packages/scheduler/src/SchedulerFeatureFlags.js index 5d207922ca968..c86de5e35dc2a 100644 --- a/packages/scheduler/src/SchedulerFeatureFlags.js +++ b/packages/scheduler/src/SchedulerFeatureFlags.js @@ -8,12 +8,8 @@ */ export const enableSchedulerDebugging = false; -export const enableIsInputPending = false; export const enableProfiling = false; -export const enableIsInputPendingContinuous = false; export const frameYieldMs = 5; -export const continuousYieldMs = 50; -export const maxYieldMs = 300; export const userBlockingPriorityTimeout = 250; export const normalPriorityTimeout = 5000; diff --git a/packages/scheduler/src/__tests__/Scheduler-test.js b/packages/scheduler/src/__tests__/Scheduler-test.js index 14ef25f3ad4f5..10077866ff788 100644 --- a/packages/scheduler/src/__tests__/Scheduler-test.js +++ b/packages/scheduler/src/__tests__/Scheduler-test.js @@ -339,15 +339,7 @@ describe('SchedulerBrowser', () => { runtime.assertLog([ 'Message Event', 'Task with no pending input', - // Even though there's no input, eventually Scheduler will yield - // regardless in case there's a pending main thread task we don't know - // about, like a network event. - gate(flags => - flags.enableIsInputPending - ? 'Yield at 10ms' - : // When isInputPending is disabled, we always yield quickly - 'Yield at 5ms', - ), + 'Yield at 5ms', ]); runtime.resetTime(); @@ -393,15 +385,7 @@ describe('SchedulerBrowser', () => { runtime.assertLog([ 'Message Event', 'Task with no pending input', - // Even though there's no input, eventually Scheduler will yield - // regardless in case there's a pending main thread task we don't know - // about, like a network event. - gate(flags => - flags.enableIsInputPending - ? 'Yield at 10ms' - : // When isInputPending is disabled, we always yield quickly - 'Yield at 5ms', - ), + 'Yield at 5ms', ]); runtime.resetTime(); @@ -419,14 +403,7 @@ describe('SchedulerBrowser', () => { runtime.assertLog([ 'Message Event', 'Task with continuous input', - // This time we yielded quickly to unblock the continuous event. But not - // as quickly as for a discrete event. - gate(flags => - flags.enableIsInputPending - ? 'Yield at 10ms' - : // When isInputPending is disabled, we always yield quickly - 'Yield at 5ms', - ), + 'Yield at 5ms', 'Continuous Event', ]); }, @@ -448,16 +425,7 @@ describe('SchedulerBrowser', () => { runtime.assertLog(['Post Message']); runtime.fireMessageEvent(); - runtime.assertLog([ - 'Message Event', - 'Task with no paint', - gate(flags => - flags.enableIsInputPending - ? 'Yield at 10ms' - : // When isInputPending is disabled, we always yield quickly - 'Yield at 5ms', - ), - ]); + runtime.assertLog(['Message Event', 'Task with no paint', 'Yield at 5ms']); runtime.resetTime(); diff --git a/packages/scheduler/src/forks/Scheduler.js b/packages/scheduler/src/forks/Scheduler.js index e4eb17b37a2c9..a785bec95320c 100644 --- a/packages/scheduler/src/forks/Scheduler.js +++ b/packages/scheduler/src/forks/Scheduler.js @@ -14,11 +14,7 @@ import type {PriorityLevel} from '../SchedulerPriorities'; import { enableSchedulerDebugging, enableProfiling, - enableIsInputPending, - enableIsInputPendingContinuous, frameYieldMs, - continuousYieldMs, - maxYieldMs, userBlockingPriorityTimeout, lowPriorityTimeout, normalPriorityTimeout, @@ -104,17 +100,6 @@ const localClearTimeout = const localSetImmediate = typeof setImmediate !== 'undefined' ? setImmediate : null; // IE and Node.js + jsdom -const isInputPending = - typeof navigator !== 'undefined' && - // $FlowFixMe[prop-missing] - navigator.scheduling !== undefined && - // $FlowFixMe[incompatible-type] - navigator.scheduling.isInputPending !== undefined - ? navigator.scheduling.isInputPending.bind(navigator.scheduling) - : null; - -const continuousOptions = {includeContinuous: enableIsInputPendingContinuous}; - function advanceTimers(currentTime: number) { // Check for tasks that are no longer delayed and add them to the queue. let timer = peek(timerQueue); @@ -468,12 +453,8 @@ let taskTimeoutID: TimeoutID = (-1: any); // It does not attempt to align with frame boundaries, since most tasks don't // need to be frame aligned; for those that do, use requestAnimationFrame. let frameInterval = frameYieldMs; -const continuousInputInterval = continuousYieldMs; -const maxInterval = maxYieldMs; let startTime = -1; -let needsPaint = false; - function shouldYieldToHost(): boolean { const timeElapsed = getCurrentTime() - startTime; if (timeElapsed < frameInterval) { @@ -481,58 +462,11 @@ function shouldYieldToHost(): boolean { // smaller than a single frame. Don't yield yet. return false; } - - // The main thread has been blocked for a non-negligible amount of time. We - // may want to yield control of the main thread, so the browser can perform - // high priority tasks. The main ones are painting and user input. If there's - // a pending paint or a pending input, then we should yield. But if there's - // neither, then we can yield less often while remaining responsive. We'll - // eventually yield regardless, since there could be a pending paint that - // wasn't accompanied by a call to `requestPaint`, or other main thread tasks - // like network events. - if (enableIsInputPending) { - if (needsPaint) { - // There's a pending paint (signaled by `requestPaint`). Yield now. - return true; - } - if (timeElapsed < continuousInputInterval) { - // We haven't blocked the thread for that long. Only yield if there's a - // pending discrete input (e.g. click). It's OK if there's pending - // continuous input (e.g. mouseover). - if (isInputPending !== null) { - return isInputPending(); - } - } else if (timeElapsed < maxInterval) { - // Yield if there's either a pending discrete or continuous input. - if (isInputPending !== null) { - return isInputPending(continuousOptions); - } - } else { - // We've blocked the thread for a long time. Even if there's no pending - // input, there may be some other scheduled work that we don't know about, - // like a network event. Yield now. - return true; - } - } - - // `isInputPending` isn't available. Yield now. + // Yield now. return true; } -function requestPaint() { - if ( - enableIsInputPending && - navigator !== undefined && - // $FlowFixMe[prop-missing] - navigator.scheduling !== undefined && - // $FlowFixMe[incompatible-type] - navigator.scheduling.isInputPending !== undefined - ) { - needsPaint = true; - } - - // Since we yield every frame regardless, `requestPaint` has no effect. -} +function requestPaint() {} function forceFrameRate(fps: number) { if (fps < 0 || fps > 125) { @@ -577,9 +511,6 @@ const performWorkUntilDeadline = () => { } } } - // Yielding to the browser will give it a chance to paint, so we can - // reset this. - needsPaint = false; }; let schedulePerformWorkUntilDeadline; diff --git a/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js b/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js index a96accb914207..a4d3ac0e390ac 100644 --- a/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js +++ b/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js @@ -13,11 +13,7 @@ export const enableProfiling = __VARIANT__; -export const enableIsInputPending = __VARIANT__; -export const enableIsInputPendingContinuous = __VARIANT__; export const frameYieldMs = 5; -export const continuousYieldMs = 10; -export const maxYieldMs = 10; export const userBlockingPriorityTimeout = 250; export const normalPriorityTimeout = 5000; diff --git a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js index 2641ac8b57c99..b2e25cd31fce9 100644 --- a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js +++ b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js @@ -16,11 +16,7 @@ export const { userBlockingPriorityTimeout, normalPriorityTimeout, lowPriorityTimeout, - enableIsInputPending, - enableIsInputPendingContinuous, frameYieldMs, - continuousYieldMs, - maxYieldMs, } = dynamicFeatureFlags; export const enableSchedulerDebugging = true; export const enableProfiling: boolean = From 721b335c0f6f2cb17dedd12ac935226311f55db6 Mon Sep 17 00:00:00 2001 From: Noah Lemen Date: Mon, 26 Feb 2024 14:28:23 -0500 Subject: [PATCH 2/3] make frameYieldMs flag static, set to 10ms in www --- .../scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js | 2 -- packages/scheduler/src/forks/SchedulerFeatureFlags.www.js | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js b/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js index a4d3ac0e390ac..25d88aa4b903d 100644 --- a/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js +++ b/packages/scheduler/src/forks/SchedulerFeatureFlags.www-dynamic.js @@ -13,8 +13,6 @@ export const enableProfiling = __VARIANT__; -export const frameYieldMs = 5; - export const userBlockingPriorityTimeout = 250; export const normalPriorityTimeout = 5000; export const lowPriorityTimeout = 10000; diff --git a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js index b2e25cd31fce9..93d1b25b99c52 100644 --- a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js +++ b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js @@ -16,8 +16,9 @@ export const { userBlockingPriorityTimeout, normalPriorityTimeout, lowPriorityTimeout, - frameYieldMs, } = dynamicFeatureFlags; + +export const frameYieldMs = 10; export const enableSchedulerDebugging = true; export const enableProfiling: boolean = __PROFILE__ && enableProfilingFeatureFlag; From 18e220b618060bd05bf7d1079dbe6021d737749d Mon Sep 17 00:00:00 2001 From: Noah Lemen Date: Mon, 26 Feb 2024 15:13:30 -0500 Subject: [PATCH 3/3] cleanup/fix tests --- .../scheduler/src/__tests__/Scheduler-test.js | 145 +----------------- .../__tests__/SchedulerSetImmediate-test.js | 2 +- .../scheduler/src/forks/SchedulerPostTask.js | 3 +- 3 files changed, 3 insertions(+), 147 deletions(-) diff --git a/packages/scheduler/src/__tests__/Scheduler-test.js b/packages/scheduler/src/__tests__/Scheduler-test.js index 10077866ff788..0dc9763ac6b0b 100644 --- a/packages/scheduler/src/__tests__/Scheduler-test.js +++ b/packages/scheduler/src/__tests__/Scheduler-test.js @@ -101,23 +101,6 @@ describe('SchedulerBrowser', () => { this.port2 = port2; }; - const scheduling = { - isInputPending(options) { - if (this !== scheduling) { - throw new Error( - 'isInputPending called with incorrect `this` context', - ); - } - - return ( - hasPendingDiscreteEvent || - (options && options.includeContinuous && hasPendingContinuousEvent) - ); - }, - }; - - global.navigator = {scheduling}; - function ensureLogIsEmpty() { if (eventLog.length !== 0) { throw Error('Log is not empty. Call assertLog before continuing.'); @@ -218,7 +201,7 @@ describe('SchedulerBrowser', () => { runtime.assertLog([ 'Message Event', 'Task', - 'Yield at 5ms', + gate(flags => (flags.www ? 'Yield at 10ms' : 'Yield at 5ms')), 'Post Message', ]); @@ -320,132 +303,6 @@ describe('SchedulerBrowser', () => { runtime.assertLog(['Message Event', 'B']); }); - it('when isInputPending is available, we can wait longer before yielding', () => { - function blockUntilSchedulerAsksToYield() { - while (!Scheduler.unstable_shouldYield()) { - runtime.advanceTime(1); - } - runtime.log(`Yield at ${performance.now()}ms`); - } - - // First show what happens when we don't request a paint - scheduleCallback(NormalPriority, () => { - runtime.log('Task with no pending input'); - blockUntilSchedulerAsksToYield(); - }); - runtime.assertLog(['Post Message']); - - runtime.fireMessageEvent(); - runtime.assertLog([ - 'Message Event', - 'Task with no pending input', - 'Yield at 5ms', - ]); - - runtime.resetTime(); - - // Now do the same thing, but while the task is running, simulate an - // input event. - scheduleCallback(NormalPriority, () => { - runtime.log('Task with pending input'); - runtime.scheduleDiscreteEvent(); - blockUntilSchedulerAsksToYield(); - }); - runtime.assertLog(['Post Message']); - - runtime.fireMessageEvent(); - runtime.assertLog([ - 'Message Event', - 'Task with pending input', - // This time we yielded quickly to unblock the discrete event. - 'Yield at 5ms', - 'Discrete Event', - ]); - }); - - it( - 'isInputPending will also check for continuous inputs, but after a ' + - 'slightly larger threshold', - () => { - function blockUntilSchedulerAsksToYield() { - while (!Scheduler.unstable_shouldYield()) { - runtime.advanceTime(1); - } - runtime.log(`Yield at ${performance.now()}ms`); - } - - // First show what happens when we don't request a paint - scheduleCallback(NormalPriority, () => { - runtime.log('Task with no pending input'); - blockUntilSchedulerAsksToYield(); - }); - runtime.assertLog(['Post Message']); - - runtime.fireMessageEvent(); - runtime.assertLog([ - 'Message Event', - 'Task with no pending input', - 'Yield at 5ms', - ]); - - runtime.resetTime(); - - // Now do the same thing, but while the task is running, simulate a - // continuous input event. - scheduleCallback(NormalPriority, () => { - runtime.log('Task with continuous input'); - runtime.scheduleContinuousEvent(); - blockUntilSchedulerAsksToYield(); - }); - runtime.assertLog(['Post Message']); - - runtime.fireMessageEvent(); - runtime.assertLog([ - 'Message Event', - 'Task with continuous input', - 'Yield at 5ms', - 'Continuous Event', - ]); - }, - ); - - it('requestPaint forces a yield at the end of the next frame interval', () => { - function blockUntilSchedulerAsksToYield() { - while (!Scheduler.unstable_shouldYield()) { - runtime.advanceTime(1); - } - runtime.log(`Yield at ${performance.now()}ms`); - } - - // First show what happens when we don't request a paint - scheduleCallback(NormalPriority, () => { - runtime.log('Task with no paint'); - blockUntilSchedulerAsksToYield(); - }); - runtime.assertLog(['Post Message']); - - runtime.fireMessageEvent(); - runtime.assertLog(['Message Event', 'Task with no paint', 'Yield at 5ms']); - - runtime.resetTime(); - - // Now do the same thing, but call requestPaint inside the task - scheduleCallback(NormalPriority, () => { - runtime.log('Task with paint'); - requestPaint(); - blockUntilSchedulerAsksToYield(); - }); - runtime.assertLog(['Post Message']); - - runtime.fireMessageEvent(); - runtime.assertLog([ - 'Message Event', - 'Task with paint', - // This time we yielded quickly (5ms) because we requested a paint. - 'Yield at 5ms', - ]); - }); - it('yielding continues in a new task regardless of how much time is remaining', () => { scheduleCallback(NormalPriority, () => { runtime.log('Original Task'); diff --git a/packages/scheduler/src/__tests__/SchedulerSetImmediate-test.js b/packages/scheduler/src/__tests__/SchedulerSetImmediate-test.js index 52b71b569f415..bf521b15d3664 100644 --- a/packages/scheduler/src/__tests__/SchedulerSetImmediate-test.js +++ b/packages/scheduler/src/__tests__/SchedulerSetImmediate-test.js @@ -173,7 +173,7 @@ describe('SchedulerDOMSetImmediate', () => { runtime.assertLog([ 'setImmediate Callback', 'Task', - 'Yield at 5ms', + gate(flags => (flags.www ? 'Yield at 10ms' : 'Yield at 5ms')), 'Set Immediate', ]); diff --git a/packages/scheduler/src/forks/SchedulerPostTask.js b/packages/scheduler/src/forks/SchedulerPostTask.js index 547f165ff9632..a029fce0cbfcc 100644 --- a/packages/scheduler/src/forks/SchedulerPostTask.js +++ b/packages/scheduler/src/forks/SchedulerPostTask.js @@ -57,8 +57,7 @@ let deadline = 0; let currentPriorityLevel_DEPRECATED = NormalPriority; -// `isInputPending` is not available. Since we have no way of knowing if -// there's pending input, always yield at the end of the frame. +// Always yield at the end of the frame. export function unstable_shouldYield(): boolean { return getCurrentTime() >= deadline; }