88
99/* eslint-disable no-var */
1010
11- import { enableSchedulerDebugging } from './SchedulerFeatureFlags' ;
11+ import {
12+ enableSchedulerDebugging ,
13+ enableProfiling ,
14+ } from './SchedulerFeatureFlags' ;
1215import {
1316 requestHostCallback ,
1417 requestHostTimeout ,
@@ -21,11 +24,26 @@ import {
2124import { push , pop , peek } from './SchedulerMinHeap' ;
2225
2326// TODO: Use symbols?
24- var ImmediatePriority = 1 ;
25- var UserBlockingPriority = 2 ;
26- var NormalPriority = 3 ;
27- var LowPriority = 4 ;
28- var IdlePriority = 5 ;
27+ import {
28+ ImmediatePriority ,
29+ UserBlockingPriority ,
30+ NormalPriority ,
31+ LowPriority ,
32+ IdlePriority ,
33+ } from './SchedulerPriorities' ;
34+ import {
35+ sharedProfilingBuffer ,
36+ markTaskRun ,
37+ markTaskYield ,
38+ markTaskCompleted ,
39+ markTaskCanceled ,
40+ markTaskErrored ,
41+ markSchedulerSuspended ,
42+ markSchedulerUnsuspended ,
43+ markTaskStart ,
44+ stopLoggingProfilingEvents ,
45+ startLoggingProfilingEvents ,
46+ } from './SchedulerProfiling' ;
2947
3048// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
3149// Math.pow(2, 30) - 1
@@ -46,7 +64,7 @@ var taskQueue = [];
4664var timerQueue = [ ] ;
4765
4866// Incrementing id counter. Used to maintain insertion order.
49- var taskIdCounter = 0 ;
67+ var taskIdCounter = 1 ;
5068
5169// Pausing the scheduler is useful for debugging.
5270var isSchedulerPaused = false ;
@@ -60,15 +78,6 @@ var isPerformingWork = false;
6078var isHostCallbackScheduled = false ;
6179var isHostTimeoutScheduled = false ;
6280
63- function flushTask ( task , callback , currentTime ) {
64- currentPriorityLevel = task . priorityLevel ;
65- var didUserCallbackTimeout = task . expirationTime <= currentTime ;
66- var continuationCallback = callback ( didUserCallbackTimeout ) ;
67- return typeof continuationCallback === 'function'
68- ? continuationCallback
69- : null ;
70- }
71-
7281function advanceTimers ( currentTime ) {
7382 // Check for tasks that are no longer delayed and add them to the queue.
7483 let timer = peek ( timerQueue ) ;
@@ -81,6 +90,10 @@ function advanceTimers(currentTime) {
8190 pop ( timerQueue ) ;
8291 timer . sortIndex = timer . expirationTime ;
8392 push ( taskQueue , timer ) ;
93+ if ( enableProfiling ) {
94+ markTaskStart ( timer ) ;
95+ timer . isQueued = true ;
96+ }
8497 } else {
8598 // Remaining timers are pending.
8699 return ;
@@ -107,6 +120,10 @@ function handleTimeout(currentTime) {
107120}
108121
109122function flushWork ( hasTimeRemaining , initialTime ) {
123+ if ( enableProfiling ) {
124+ markSchedulerUnsuspended ( initialTime ) ;
125+ }
126+
110127 // We'll need a host callback the next time work is scheduled.
111128 isHostCallbackScheduled = false ;
112129 if ( isHostTimeoutScheduled ) {
@@ -118,52 +135,82 @@ function flushWork(hasTimeRemaining, initialTime) {
118135 isPerformingWork = true ;
119136 const previousPriorityLevel = currentPriorityLevel ;
120137 try {
121- let currentTime = initialTime ;
122- advanceTimers ( currentTime ) ;
123- currentTask = peek ( taskQueue ) ;
124- while (
125- currentTask !== null &&
126- ! ( enableSchedulerDebugging && isSchedulerPaused )
127- ) {
128- if (
129- currentTask . expirationTime > currentTime &&
130- ( ! hasTimeRemaining || shouldYieldToHost ( ) )
131- ) {
132- // This currentTask hasn't expired, and we've reached the deadline.
133- break ;
134- }
135- const callback = currentTask . callback ;
136- if ( callback !== null ) {
137- currentTask . callback = null ;
138- const continuation = flushTask ( currentTask , callback , currentTime ) ;
139- if ( continuation !== null ) {
140- currentTask . callback = continuation ;
141- } else {
142- if ( currentTask === peek ( taskQueue ) ) {
143- pop ( taskQueue ) ;
144- }
138+ if ( enableProfiling ) {
139+ try {
140+ return workLoop ( hasTimeRemaining , initialTime ) ;
141+ } catch ( error ) {
142+ if ( currentTask !== null ) {
143+ const currentTime = getCurrentTime ( ) ;
144+ markTaskErrored ( currentTask , currentTime ) ;
145+ currentTask . isQueued = false ;
145146 }
146- currentTime = getCurrentTime ( ) ;
147- advanceTimers ( currentTime ) ;
148- } else {
149- pop ( taskQueue ) ;
147+ throw error ;
150148 }
151- currentTask = peek ( taskQueue ) ;
152- }
153- // Return whether there's additional work
154- if ( currentTask !== null ) {
155- return true ;
156149 } else {
157- let firstTimer = peek ( timerQueue ) ;
158- if ( firstTimer !== null ) {
159- requestHostTimeout ( handleTimeout , firstTimer . startTime - currentTime ) ;
160- }
161- return false ;
150+ // No catch in prod codepath.
151+ return workLoop ( hasTimeRemaining , initialTime ) ;
162152 }
163153 } finally {
164154 currentTask = null ;
165155 currentPriorityLevel = previousPriorityLevel ;
166156 isPerformingWork = false ;
157+ if ( enableProfiling ) {
158+ const currentTime = getCurrentTime ( ) ;
159+ markSchedulerSuspended ( currentTime ) ;
160+ }
161+ }
162+ }
163+
164+ function workLoop ( hasTimeRemaining , initialTime ) {
165+ let currentTime = initialTime ;
166+ advanceTimers ( currentTime ) ;
167+ currentTask = peek ( taskQueue ) ;
168+ while (
169+ currentTask !== null &&
170+ ! ( enableSchedulerDebugging && isSchedulerPaused )
171+ ) {
172+ if (
173+ currentTask . expirationTime > currentTime &&
174+ ( ! hasTimeRemaining || shouldYieldToHost ( ) )
175+ ) {
176+ // This currentTask hasn't expired, and we've reached the deadline.
177+ break ;
178+ }
179+ const callback = currentTask . callback ;
180+ if ( callback !== null ) {
181+ currentTask . callback = null ;
182+ currentPriorityLevel = currentTask . priorityLevel ;
183+ const didUserCallbackTimeout = currentTask . expirationTime <= currentTime ;
184+ markTaskRun ( currentTask , currentTime ) ;
185+ const continuationCallback = callback ( didUserCallbackTimeout ) ;
186+ currentTime = getCurrentTime ( ) ;
187+ if ( typeof continuationCallback === 'function' ) {
188+ currentTask . callback = continuationCallback ;
189+ markTaskYield ( currentTask , currentTime ) ;
190+ } else {
191+ if ( enableProfiling ) {
192+ markTaskCompleted ( currentTask , currentTime ) ;
193+ currentTask . isQueued = false ;
194+ }
195+ if ( currentTask === peek ( taskQueue ) ) {
196+ pop ( taskQueue ) ;
197+ }
198+ }
199+ advanceTimers ( currentTime ) ;
200+ } else {
201+ pop ( taskQueue ) ;
202+ }
203+ currentTask = peek ( taskQueue ) ;
204+ }
205+ // Return whether there's additional work
206+ if ( currentTask !== null ) {
207+ return true ;
208+ } else {
209+ let firstTimer = peek ( timerQueue ) ;
210+ if ( firstTimer !== null ) {
211+ requestHostTimeout ( handleTimeout , firstTimer . startTime - currentTime ) ;
212+ }
213+ return false ;
167214 }
168215}
169216
@@ -276,6 +323,9 @@ function unstable_scheduleCallback(priorityLevel, callback, options) {
276323 expirationTime,
277324 sortIndex : - 1 ,
278325 } ;
326+ if ( enableProfiling ) {
327+ newTask . isQueued = false ;
328+ }
279329
280330 if ( startTime > currentTime ) {
281331 // This is a delayed task.
@@ -295,6 +345,10 @@ function unstable_scheduleCallback(priorityLevel, callback, options) {
295345 } else {
296346 newTask . sortIndex = expirationTime ;
297347 push ( taskQueue , newTask ) ;
348+ if ( enableProfiling ) {
349+ markTaskStart ( newTask , currentTime ) ;
350+ newTask . isQueued = true ;
351+ }
298352 // Schedule a host callback, if needed. If we're already performing work,
299353 // wait until the next time we yield.
300354 if ( ! isHostCallbackScheduled && ! isPerformingWork ) {
@@ -323,9 +377,17 @@ function unstable_getFirstCallbackNode() {
323377}
324378
325379function unstable_cancelCallback ( task ) {
326- // Null out the callback to indicate the task has been canceled. (Can't remove
327- // from the queue because you can't remove arbitrary nodes from an array based
328- // heap, only the first one.)
380+ if ( enableProfiling ) {
381+ if ( task . isQueued ) {
382+ const currentTime = getCurrentTime ( ) ;
383+ markTaskCanceled ( task , currentTime ) ;
384+ task . isQueued = false ;
385+ }
386+ }
387+
388+ // Null out the callback to indicate the task has been canceled. (Can't
389+ // remove from the queue because you can't remove arbitrary nodes from an
390+ // array based heap, only the first one.)
329391 task . callback = null ;
330392}
331393
@@ -370,3 +432,11 @@ export {
370432 getCurrentTime as unstable_now ,
371433 forceFrameRate as unstable_forceFrameRate ,
372434} ;
435+
436+ export const unstable_Profiling = enableProfiling
437+ ? {
438+ startLoggingProfilingEvents,
439+ stopLoggingProfilingEvents,
440+ sharedProfilingBuffer,
441+ }
442+ : null ;
0 commit comments