@samitouri / QOS-React / commits / 8df4d59be5

Implement pauseExecution, continueExecution, dumpQueue for Scheduler (#14053)

* Implement pauseExecution, continueExecution, dumpQueue * Expose firstCallbackNode. Fix tests. Revert results.json * Put scheduler pausing behind a feature flag

Kevin Chavez committed Dec 6, 2018 at 13:57 UTC 8df4d59be532e56b6513815184094f57f566df09
16 files changed +197 -4
fixtures/scheduler/index.html
+74 -1
@@ -90,6 +90,21 @@
90 </div>
91 <div> If the counter advanced while you were away from this tab, it's correct.</div>
92 </li>
93 + <li>
94 + <p>Can pause execution, dump scheduled callbacks, and continue where it left off</p>
95 + <button onClick="runTestEight()">Run Test 8</button>
96 + <div><b>Click the button above, press "continue" to finish the test after it pauses:</b></div>
97 + <button onClick="continueTestEight()">continue</button>
98 + <div><b>Expected:</b></div>
99 + <div id="test-8-expected">
100 + </div>
101 + <div> -------------------------------------------------</div>
102 + <div> If the test didn't progress until you hit "continue" and </div>
103 + <div> you see the same above and below afterwards it's correct.
104 + <div> -------------------------------------------------</div>
105 + <div><b>Actual:</b></div>
106 + <div id="test-8"></div>
107 + </li>
108 </ol>
109 <script src="../../build/node_modules/react/umd/react.development.js"></script>
110 <script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
@@ -98,7 +113,10 @@
113 const {
114 unstable_scheduleCallback: scheduleCallback,
115 unstable_cancelCallback: cancelCallback,
101 - unstable_now: now
116 + unstable_now: now,
117 + unstable_getFirstCallbackNode: getFirstCallbackNode,
118 + unstable_pauseExecution: pauseExecution,
119 + unstable_continueExecution: continueExecution,
120 } = Scheduler;
121 function displayTestResult(testNumber) {
122 const expectationNode = document.getElementById('test-' + testNumber + '-expected');
@@ -215,6 +233,16 @@ const expectedResults = [
233 [
234 // ... TODO
235 ],
236 + [],
237 + [],
238 + // Test 8
239 + [
240 + 'Queue size: 0.',
241 + 'Pausing... press continue to resume.',
242 + 'Queue size: 2.',
243 + 'Finishing...',
244 + 'Done!',
245 + ],
246 ];
247 function runTestOne() {
248 // Test 1
@@ -496,6 +524,51 @@ function runTestSeven() {
524 }
525 scheduleCallback(incrementCounterAndScheduleNextCallback);
526 }
527 +
528 +function runTestEight() {
529 + // Test 8
530 + // Pauses execution, dumps the queue, and continues execution
531 + clearTestResult(8);
532 +
533 + function countNodesInStack(firstCallbackNode) {
534 + var node = firstCallbackNode;
535 + var count = 0;
536 + if (node !== null) {
537 + do {
538 + count = count + 1;
539 + node = node.next;
540 + } while (node !== firstCallbackNode);
541 + }
542 + return count;
543 + }
544 +
545 + scheduleCallback(() => {
546 +
547 + // size should be 0
548 + updateTestResult(8, `Queue size: ${countNodesInStack(getFirstCallbackNode())}.`);
549 + updateTestResult(8, 'Pausing... press continue to resume.');
550 + pauseExecution();
551 +
552 + scheduleCallback(function () {
553 + updateTestResult(8, 'Finishing...');
554 + displayTestResult(8);
555 + })
556 + scheduleCallback(function () {
557 + updateTestResult(8, 'Done!');
558 + displayTestResult(8);
559 + checkTestResult(8);
560 + })
561 +
562 + // new size should be 2 now
563 + updateTestResult(8, `Queue size: ${countNodesInStack(getFirstCallbackNode())}.`);
564 + displayTestResult(8);
565 + });
566 +}
567 +
568 +function continueTestEight() {
569 + continueExecution();
570 +}
571 +
572 </script type="text/babel">
573 </body>
574 </html>
\ No newline at end of file
packages/react/src/ReactSharedInternals.js
+6
@@ -12,6 +12,9 @@ import {
12 unstable_now,
13 unstable_scheduleCallback,
14 unstable_runWithPriority,
15 + unstable_getFirstCallbackNode,
16 + unstable_pauseExecution,
17 + unstable_continueExecution,
18 unstable_wrapCallback,
19 unstable_getCurrentPriorityLevel,
20 } from 'scheduler';
@@ -49,6 +52,9 @@ if (__UMD__) {
52 unstable_scheduleCallback,
53 unstable_runWithPriority,
54 unstable_wrapCallback,
55 + unstable_getFirstCallbackNode,
56 + unstable_pauseExecution,
57 + unstable_continueExecution,
58 unstable_getCurrentPriorityLevel,
59 },
60 SchedulerTracing: {
packages/scheduler/npm/umd/scheduler.development.js
+24
@@ -68,6 +68,27 @@
68 );
69 }
70
71 + function unstable_getFirstCallbackNode() {
72 + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_getFirstCallbackNode.apply(
73 + this,
74 + arguments
75 + );
76 + }
77 +
78 + function unstable_pauseExecution() {
79 + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_pauseExecution.apply(
80 + this,
81 + arguments
82 + );
83 + }
84 +
85 + function unstable_continueExecution() {
86 + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_continueExecution.apply(
87 + this,
88 + arguments
89 + );
90 + }
91 +
92 return Object.freeze({
93 unstable_now: unstable_now,
94 unstable_scheduleCallback: unstable_scheduleCallback,
@@ -76,5 +97,8 @@
97 unstable_runWithPriority: unstable_runWithPriority,
98 unstable_wrapCallback: unstable_wrapCallback,
99 unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
100 + unstable_continueExecution: unstable_continueExecution,
101 + unstable_pauseExecution: unstable_pauseExecution,
102 + unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
103 });
104 });
packages/scheduler/npm/umd/scheduler.production.min.js
+18
@@ -68,6 +68,21 @@
68 );
69 }
70
71 + function unstable_getFirstCallbackNode() {
72 + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_getFirstCallbackNode.apply(
73 + this,
74 + arguments
75 + );
76 + }
77 +
78 + function unstable_pauseExecution() {
79 + return undefined;
80 + }
81 +
82 + function unstable_continueExecution() {
83 + return undefined;
84 + }
85 +
86 return Object.freeze({
87 unstable_now: unstable_now,
88 unstable_scheduleCallback: unstable_scheduleCallback,
@@ -76,5 +91,8 @@
91 unstable_runWithPriority: unstable_runWithPriority,
92 unstable_wrapCallback: unstable_wrapCallback,
93 unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
94 + unstable_continueExecution: unstable_continueExecution,
95 + unstable_pauseExecution: unstable_pauseExecution,
96 + unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
97 });
98 });
packages/scheduler/npm/umd/scheduler.profiling.min.js
+18
@@ -68,6 +68,21 @@
68 );
69 }
70
71 + function unstable_getFirstCallbackNode() {
72 + return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler.unstable_getFirstCallbackNode.apply(
73 + this,
74 + arguments
75 + );
76 + }
77 +
78 + function unstable_pauseExecution() {
79 + return undefined;
80 + }
81 +
82 + function unstable_continueExecution() {
83 + return undefined;
84 + }
85 +
86 return Object.freeze({
87 unstable_now: unstable_now,
88 unstable_scheduleCallback: unstable_scheduleCallback,
@@ -76,5 +91,8 @@
91 unstable_runWithPriority: unstable_runWithPriority,
92 unstable_wrapCallback: unstable_wrapCallback,
93 unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
94 + unstable_continueExecution: unstable_continueExecution,
95 + unstable_pauseExecution: unstable_pauseExecution,
96 + unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
97 });
98 });
packages/scheduler/src/Scheduler.js
+39 -2
@@ -8,6 +8,8 @@
8
9 /* eslint-disable no-var */
10
11 +import {enableSchedulerDebugging} from 'shared/ReactFeatureFlags';
12 +
13 // TODO: Use symbols?
14 var ImmediatePriority = 1;
15 var UserBlockingPriority = 2;
@@ -33,6 +35,9 @@ var IDLE_PRIORITY = maxSigned31BitInt;
35 var firstCallbackNode = null;
36
37 var currentDidTimeout = false;
38 +// Pausing the scheduler is useful for debugging.
39 +var isSchedulerPaused = false;
40 +
41 var currentPriorityLevel = NormalPriority;
42 var currentEventStartTime = -1;
43 var currentExpirationTime = -1;
@@ -173,13 +178,23 @@ function flushImmediateWork() {
178 }
179
180 function flushWork(didTimeout) {
181 + // Exit right away if we're currently paused
182 +
183 + if (enableSchedulerDebugging && isSchedulerPaused) {
184 + return;
185 + }
186 +
187 isExecutingCallback = true;
188 const previousDidTimeout = currentDidTimeout;
189 currentDidTimeout = didTimeout;
190 try {
191 if (didTimeout) {
192 // Flush all the expired callbacks without yielding.
182 - while (firstCallbackNode !== null) {
193 + while (
194 + firstCallbackNode !== null &&
195 + !(enableSchedulerDebugging && isSchedulerPaused)
196 + ) {
197 + // TODO Wrap i nfeature flag
198 // Read the current time. Flush all the callbacks that expire at or
199 // earlier than that time. Then read the current time again and repeat.
200 // This optimizes for as few performance.now calls as possible.
@@ -189,7 +204,8 @@ function flushWork(didTimeout) {
204 flushFirstCallback();
205 } while (
206 firstCallbackNode !== null &&
192 - firstCallbackNode.expirationTime <= currentTime
207 + firstCallbackNode.expirationTime <= currentTime &&
208 + !(enableSchedulerDebugging && isSchedulerPaused)
209 );
210 continue;
211 }
@@ -199,6 +215,9 @@ function flushWork(didTimeout) {
215 // Keep flushing callbacks until we run out of time in the frame.
216 if (firstCallbackNode !== null) {
217 do {
218 + if (enableSchedulerDebugging && isSchedulerPaused) {
219 + break;
220 + }
221 flushFirstCallback();
222 } while (firstCallbackNode !== null && !shouldYieldToHost());
223 }
@@ -342,6 +361,21 @@ function unstable_scheduleCallback(callback, deprecated_options) {
361 return newNode;
362 }
363
364 +function unstable_pauseExecution() {
365 + isSchedulerPaused = true;
366 +}
367 +
368 +function unstable_continueExecution() {
369 + isSchedulerPaused = false;
370 + if (firstCallbackNode !== null) {
371 + ensureHostCallbackIsScheduled();
372 + }
373 +}
374 +
375 +function unstable_getFirstCallbackNode() {
376 + return firstCallbackNode;
377 +}
378 +
379 function unstable_cancelCallback(callbackNode) {
380 var next = callbackNode.next;
381 if (next === null) {
@@ -659,5 +693,8 @@ export {
693 unstable_wrapCallback,
694 unstable_getCurrentPriorityLevel,
695 unstable_shouldYield,
696 + unstable_continueExecution,
697 + unstable_pauseExecution,
698 + unstable_getFirstCallbackNode,
699 getCurrentTime as unstable_now,
700 };
packages/shared/ReactFeatureFlags.js
+4 -1
@@ -33,7 +33,10 @@ export const enableProfilerTimer = __PROFILE__;
33 export const enableSchedulerTracing = __PROFILE__;
34
35 // Only used in www builds.
36 -export const enableSuspenseServerRenderer = false;
36 +export const enableSuspenseServerRenderer = false; // TODO: __DEV__? Here it might just be false.
37 +
38 +// Only used in www builds.
39 +export const enableSchedulerDebugging = __DEV__;
40
41 // Only used in www builds.
42 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js
+1
@@ -24,6 +24,7 @@ export const enableSuspenseServerRenderer = false;
24 export const disableInputAttributeSyncing = false;
25 export const enableStableConcurrentModeAPIs = false;
26 export const warnAboutShorthandPropertyCollision = false;
27 +export const enableSchedulerDebugging = false;
28
29 // Only used in www builds.
30 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js
+1
@@ -24,6 +24,7 @@ export const enableSuspenseServerRenderer = false;
24 export const disableInputAttributeSyncing = false;
25 export const enableStableConcurrentModeAPIs = false;
26 export const warnAboutShorthandPropertyCollision = false;
27 +export const enableSchedulerDebugging = false;
28
29 // Only used in www builds.
30 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -29,6 +29,7 @@ export const enableSchedulerTracing = __PROFILE__;
29 export const enableSuspenseServerRenderer = false;
30 export const enableStableConcurrentModeAPIs = false;
31 export const warnAboutShorthandPropertyCollision = false;
32 +export const enableSchedulerDebugging = false;
33
34 // Only used in www builds.
35 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -24,6 +24,7 @@ export const enableSuspenseServerRenderer = false;
24 export const disableInputAttributeSyncing = false;
25 export const enableStableConcurrentModeAPIs = false;
26 export const warnAboutShorthandPropertyCollision = false;
27 +export const enableSchedulerDebugging = false;
28
29 // Only used in www builds.
30 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -24,6 +24,7 @@ export const enableSuspenseServerRenderer = false;
24 export const disableInputAttributeSyncing = false;
25 export const enableStableConcurrentModeAPIs = false;
26 export const warnAboutShorthandPropertyCollision = false;
27 +export const enableSchedulerDebugging = false;
28
29 // Only used in www builds.
30 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -24,6 +24,7 @@ export const enableSuspenseServerRenderer = false;
24 export const disableInputAttributeSyncing = false;
25 export const enableStableConcurrentModeAPIs = false;
26 export const warnAboutShorthandPropertyCollision = false;
27 +export const enableSchedulerDebugging = false;
28
29 // Only used in www builds.
30 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -22,6 +22,7 @@ export const enableProfilerTimer = false;
22 export const enableSchedulerTracing = false;
23 export const enableSuspenseServerRenderer = false;
24 export const enableStableConcurrentModeAPIs = false;
25 +export const enableSchedulerDebugging = false;
26
27 // Only used in www builds.
28 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -34,6 +34,7 @@ export let enableUserTimingAPI = __DEV__;
34
35 export const enableProfilerTimer = __PROFILE__;
36 export const enableSchedulerTracing = __PROFILE__;
37 +export const enableSchedulerDebugging = __DEV__; // TODO or just true
38
39 export const enableStableConcurrentModeAPIs = false;
40
packages/shared/forks/Scheduler.umd.js
+6
@@ -16,6 +16,9 @@ const {
16 unstable_now,
17 unstable_scheduleCallback,
18 unstable_shouldYield,
19 + unstable_getFirstCallbackNode,
20 + unstable_continueExecution,
21 + unstable_pauseExecution,
22 } = ReactInternals.Scheduler;
23
24 export {
@@ -23,4 +26,7 @@ export {
26 unstable_now,
27 unstable_scheduleCallback,
28 unstable_shouldYield,
29 + unstable_getFirstCallbackNode,
30 + unstable_continueExecution,
31 + unstable_pauseExecution,
32 };