Skip to content

Commit f5077bc

Browse files
authored
[Scheduler] Always yield to native macro tasks when a virtual task completes (facebook#31787)
As an alternative to facebook#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.
1 parent 34ee391 commit f5077bc

File tree

5 files changed

+53
-11
lines changed

5 files changed

+53
-11
lines changed

packages/scheduler/src/SchedulerFeatureFlags.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ export const userBlockingPriorityTimeout = 250;
1515
export const normalPriorityTimeout = 5000;
1616
export const lowPriorityTimeout = 10000;
1717
export const enableRequestPaint = true;
18+
19+
export const enableAlwaysYieldScheduler = __EXPERIMENTAL__;

packages/scheduler/src/__tests__/Scheduler-test.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,10 @@ describe('SchedulerBrowser', () => {
201201
runtime.assertLog([
202202
'Message Event',
203203
'Task',
204-
SchedulerFeatureFlags.enableRequestPaint
205-
? 'Yield at 0ms'
206-
: `Yield at ${SchedulerFeatureFlags.frameYieldMs}ms`,
204+
gate(flags => flags.enableAlwaysYieldScheduler) ||
205+
!SchedulerFeatureFlags.enableRequestPaint
206+
? gate(flags => (flags.www ? 'Yield at 10ms' : 'Yield at 5ms'))
207+
: 'Yield at 0ms',
207208
'Post Message',
208209
]);
209210

@@ -220,7 +221,13 @@ describe('SchedulerBrowser', () => {
220221
});
221222
runtime.assertLog(['Post Message']);
222223
runtime.fireMessageEvent();
223-
runtime.assertLog(['Message Event', 'A', 'B']);
224+
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
225+
runtime.assertLog(['Message Event', 'A', 'Post Message']);
226+
runtime.fireMessageEvent();
227+
runtime.assertLog(['Message Event', 'B']);
228+
} else {
229+
runtime.assertLog(['Message Event', 'A', 'B']);
230+
}
224231
});
225232

226233
it('multiple tasks with a yield in between', () => {
@@ -267,6 +274,10 @@ describe('SchedulerBrowser', () => {
267274
runtime.assertLog(['Message Event', 'Oops!', 'Post Message']);
268275

269276
runtime.fireMessageEvent();
277+
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
278+
runtime.assertLog(['Message Event', 'Post Message']);
279+
runtime.fireMessageEvent();
280+
}
270281
runtime.assertLog(['Message Event', 'Yay']);
271282
});
272283

packages/scheduler/src/__tests__/SchedulerSetImmediate-test.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,13 @@ describe('SchedulerDOMSetImmediate', () => {
188188
});
189189
runtime.assertLog(['Set Immediate']);
190190
runtime.fireSetImmediate();
191-
runtime.assertLog(['setImmediate Callback', 'A', 'B']);
191+
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
192+
runtime.assertLog(['setImmediate Callback', 'A', 'Set Immediate']);
193+
runtime.fireSetImmediate();
194+
runtime.assertLog(['setImmediate Callback', 'B']);
195+
} else {
196+
runtime.assertLog(['setImmediate Callback', 'A', 'B']);
197+
}
192198
});
193199

194200
it('multiple tasks at different priority', () => {
@@ -200,7 +206,13 @@ describe('SchedulerDOMSetImmediate', () => {
200206
});
201207
runtime.assertLog(['Set Immediate']);
202208
runtime.fireSetImmediate();
203-
runtime.assertLog(['setImmediate Callback', 'B', 'A']);
209+
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
210+
runtime.assertLog(['setImmediate Callback', 'B', 'Set Immediate']);
211+
runtime.fireSetImmediate();
212+
runtime.assertLog(['setImmediate Callback', 'A']);
213+
} else {
214+
runtime.assertLog(['setImmediate Callback', 'B', 'A']);
215+
}
204216
});
205217

206218
it('multiple tasks with a yield in between', () => {
@@ -246,7 +258,13 @@ describe('SchedulerDOMSetImmediate', () => {
246258
runtime.assertLog(['setImmediate Callback', 'Oops!', 'Set Immediate']);
247259

248260
runtime.fireSetImmediate();
249-
runtime.assertLog(['setImmediate Callback', 'Yay']);
261+
if (gate(flags => flags.enableAlwaysYieldScheduler)) {
262+
runtime.assertLog(['setImmediate Callback', 'Set Immediate']);
263+
runtime.fireSetImmediate();
264+
runtime.assertLog(['setImmediate Callback', 'Yay']);
265+
} else {
266+
runtime.assertLog(['setImmediate Callback', 'Yay']);
267+
}
250268
});
251269

252270
it('schedule new task after queue has emptied', () => {

packages/scheduler/src/forks/Scheduler.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
lowPriorityTimeout,
2020
normalPriorityTimeout,
2121
enableRequestPaint,
22+
enableAlwaysYieldScheduler,
2223
} from '../SchedulerFeatureFlags';
2324

2425
import {push, pop, peek} from '../SchedulerMinHeap';
@@ -196,9 +197,11 @@ function workLoop(initialTime: number) {
196197
currentTask !== null &&
197198
!(enableSchedulerDebugging && isSchedulerPaused)
198199
) {
199-
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
200-
// This currentTask hasn't expired, and we've reached the deadline.
201-
break;
200+
if (!enableAlwaysYieldScheduler) {
201+
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
202+
// This currentTask hasn't expired, and we've reached the deadline.
203+
break;
204+
}
202205
}
203206
// $FlowFixMe[incompatible-use] found when upgrading Flow
204207
const callback = currentTask.callback;
@@ -242,6 +245,12 @@ function workLoop(initialTime: number) {
242245
pop(taskQueue);
243246
}
244247
currentTask = peek(taskQueue);
248+
if (enableAlwaysYieldScheduler) {
249+
if (currentTask === null || currentTask.expirationTime > currentTime) {
250+
// This currentTask hasn't expired we yield to the browser task.
251+
break;
252+
}
253+
}
245254
}
246255
// Return whether there's additional work
247256
if (currentTask !== null) {
@@ -459,7 +468,7 @@ let frameInterval = frameYieldMs;
459468
let startTime = -1;
460469

461470
function shouldYieldToHost(): boolean {
462-
if (enableRequestPaint && needsPaint) {
471+
if (!enableAlwaysYieldScheduler && enableRequestPaint && needsPaint) {
463472
// Yield now.
464473
return true;
465474
}

packages/scheduler/src/forks/SchedulerFeatureFlags.www.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ export const frameYieldMs = 10;
1919
export const userBlockingPriorityTimeout = 250;
2020
export const normalPriorityTimeout = 5000;
2121
export const lowPriorityTimeout = 10000;
22+
23+
export const enableAlwaysYieldScheduler = false;

0 commit comments

Comments
 (0)