mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[Scheduler] Always yield to native macro tasks when a virtual task completes (#31787)
As an alternative to #31784. We should really just always yield each virtual task to a native task. So that it's 1:1 with native tasks. This affects when microtasks within each task happens. This brings us closer to native `postTask` semantics which makes it more seamless to just use that when available. This still doesn't yield when a task expires to protect against starvation.
This commit is contained in:
@@ -15,3 +15,5 @@ export const userBlockingPriorityTimeout = 250;
|
||||
export const normalPriorityTimeout = 5000;
|
||||
export const lowPriorityTimeout = 10000;
|
||||
export const enableRequestPaint = true;
|
||||
|
||||
export const enableAlwaysYieldScheduler = __EXPERIMENTAL__;
|
||||
|
||||
@@ -201,9 +201,10 @@ describe('SchedulerBrowser', () => {
|
||||
runtime.assertLog([
|
||||
'Message Event',
|
||||
'Task',
|
||||
SchedulerFeatureFlags.enableRequestPaint
|
||||
? 'Yield at 0ms'
|
||||
: `Yield at ${SchedulerFeatureFlags.frameYieldMs}ms`,
|
||||
gate(flags => flags.enableAlwaysYieldScheduler) ||
|
||||
!SchedulerFeatureFlags.enableRequestPaint
|
||||
? gate(flags => (flags.www ? 'Yield at 10ms' : 'Yield at 5ms'))
|
||||
: 'Yield at 0ms',
|
||||
'Post Message',
|
||||
]);
|
||||
|
||||
@@ -220,7 +221,13 @@ describe('SchedulerBrowser', () => {
|
||||
});
|
||||
runtime.assertLog(['Post Message']);
|
||||
runtime.fireMessageEvent();
|
||||
runtime.assertLog(['Message Event', 'A', 'B']);
|
||||
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
|
||||
runtime.assertLog(['Message Event', 'A', 'Post Message']);
|
||||
runtime.fireMessageEvent();
|
||||
runtime.assertLog(['Message Event', 'B']);
|
||||
} else {
|
||||
runtime.assertLog(['Message Event', 'A', 'B']);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiple tasks with a yield in between', () => {
|
||||
@@ -267,6 +274,10 @@ describe('SchedulerBrowser', () => {
|
||||
runtime.assertLog(['Message Event', 'Oops!', 'Post Message']);
|
||||
|
||||
runtime.fireMessageEvent();
|
||||
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
|
||||
runtime.assertLog(['Message Event', 'Post Message']);
|
||||
runtime.fireMessageEvent();
|
||||
}
|
||||
runtime.assertLog(['Message Event', 'Yay']);
|
||||
});
|
||||
|
||||
|
||||
@@ -188,7 +188,13 @@ describe('SchedulerDOMSetImmediate', () => {
|
||||
});
|
||||
runtime.assertLog(['Set Immediate']);
|
||||
runtime.fireSetImmediate();
|
||||
runtime.assertLog(['setImmediate Callback', 'A', 'B']);
|
||||
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
|
||||
runtime.assertLog(['setImmediate Callback', 'A', 'Set Immediate']);
|
||||
runtime.fireSetImmediate();
|
||||
runtime.assertLog(['setImmediate Callback', 'B']);
|
||||
} else {
|
||||
runtime.assertLog(['setImmediate Callback', 'A', 'B']);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiple tasks at different priority', () => {
|
||||
@@ -200,7 +206,13 @@ describe('SchedulerDOMSetImmediate', () => {
|
||||
});
|
||||
runtime.assertLog(['Set Immediate']);
|
||||
runtime.fireSetImmediate();
|
||||
runtime.assertLog(['setImmediate Callback', 'B', 'A']);
|
||||
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
|
||||
runtime.assertLog(['setImmediate Callback', 'B', 'Set Immediate']);
|
||||
runtime.fireSetImmediate();
|
||||
runtime.assertLog(['setImmediate Callback', 'A']);
|
||||
} else {
|
||||
runtime.assertLog(['setImmediate Callback', 'B', 'A']);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiple tasks with a yield in between', () => {
|
||||
@@ -246,7 +258,13 @@ describe('SchedulerDOMSetImmediate', () => {
|
||||
runtime.assertLog(['setImmediate Callback', 'Oops!', 'Set Immediate']);
|
||||
|
||||
runtime.fireSetImmediate();
|
||||
runtime.assertLog(['setImmediate Callback', 'Yay']);
|
||||
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
|
||||
runtime.assertLog(['setImmediate Callback', 'Set Immediate']);
|
||||
runtime.fireSetImmediate();
|
||||
runtime.assertLog(['setImmediate Callback', 'Yay']);
|
||||
} else {
|
||||
runtime.assertLog(['setImmediate Callback', 'Yay']);
|
||||
}
|
||||
});
|
||||
|
||||
it('schedule new task after queue has emptied', () => {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
lowPriorityTimeout,
|
||||
normalPriorityTimeout,
|
||||
enableRequestPaint,
|
||||
enableAlwaysYieldScheduler,
|
||||
} from '../SchedulerFeatureFlags';
|
||||
|
||||
import {push, pop, peek} from '../SchedulerMinHeap';
|
||||
@@ -196,9 +197,11 @@ function workLoop(initialTime: number) {
|
||||
currentTask !== null &&
|
||||
!(enableSchedulerDebugging && isSchedulerPaused)
|
||||
) {
|
||||
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
|
||||
// This currentTask hasn't expired, and we've reached the deadline.
|
||||
break;
|
||||
if (!enableAlwaysYieldScheduler) {
|
||||
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
|
||||
// This currentTask hasn't expired, and we've reached the deadline.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
||||
const callback = currentTask.callback;
|
||||
@@ -242,6 +245,12 @@ function workLoop(initialTime: number) {
|
||||
pop(taskQueue);
|
||||
}
|
||||
currentTask = peek(taskQueue);
|
||||
if (enableAlwaysYieldScheduler) {
|
||||
if (currentTask === null || currentTask.expirationTime > currentTime) {
|
||||
// This currentTask hasn't expired we yield to the browser task.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return whether there's additional work
|
||||
if (currentTask !== null) {
|
||||
@@ -459,7 +468,7 @@ let frameInterval = frameYieldMs;
|
||||
let startTime = -1;
|
||||
|
||||
function shouldYieldToHost(): boolean {
|
||||
if (enableRequestPaint && needsPaint) {
|
||||
if (!enableAlwaysYieldScheduler && enableRequestPaint && needsPaint) {
|
||||
// Yield now.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,3 +19,5 @@ export const frameYieldMs = 10;
|
||||
export const userBlockingPriorityTimeout = 250;
|
||||
export const normalPriorityTimeout = 5000;
|
||||
export const lowPriorityTimeout = 10000;
|
||||
|
||||
export const enableAlwaysYieldScheduler = false;
|
||||
|
||||
Reference in New Issue
Block a user