-
Notifications
You must be signed in to change notification settings - Fork 47.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Scheduler] Profiling features #16145
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -138,5 +138,9 @@ | |
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED | ||
.Scheduler.unstable_UserBlockingPriority; | ||
}, | ||
get unstable_sharedProfilingBuffer() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED | ||
.Scheduler.unstable_getFirstCallbackNode; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question |
||
}, | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -138,5 +138,9 @@ | |
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED | ||
.Scheduler.unstable_UserBlockingPriority; | ||
}, | ||
get unstable_sharedProfilingBuffer() { | ||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED | ||
.Scheduler.unstable_getFirstCallbackNode; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question |
||
}, | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,15 @@ | |
|
||
/* eslint-disable no-var */ | ||
|
||
import {enableSchedulerDebugging} from './SchedulerFeatureFlags'; | ||
import { | ||
requestHostCallback, | ||
enableSchedulerDebugging, | ||
enableSharedProfilingBuffer, | ||
enableProfiling, | ||
} from './SchedulerFeatureFlags'; | ||
import { | ||
requestHostCallback as requestHostCallbackWithoutProfiling, | ||
requestHostTimeout, | ||
cancelHostCallback, | ||
cancelHostTimeout, | ||
shouldYieldToHost, | ||
getCurrentTime, | ||
|
@@ -21,11 +26,24 @@ import { | |
import {push, pop, peek} from './SchedulerMinHeap'; | ||
|
||
// TODO: Use symbols? | ||
var ImmediatePriority = 1; | ||
var UserBlockingPriority = 2; | ||
var NormalPriority = 3; | ||
var LowPriority = 4; | ||
var IdlePriority = 5; | ||
import { | ||
ImmediatePriority, | ||
UserBlockingPriority, | ||
NormalPriority, | ||
LowPriority, | ||
IdlePriority, | ||
} from './SchedulerPriorities'; | ||
import { | ||
sharedProfilingBuffer, | ||
markTaskRun, | ||
markTaskYield, | ||
markTaskCompleted, | ||
markTaskCanceled, | ||
markTaskErrored, | ||
markSchedulerSuspended, | ||
markSchedulerUnsuspended, | ||
markTaskStart, | ||
} from './SchedulerProfiling'; | ||
|
||
// Max 31 bit integer. The max integer size in V8 for 32-bit systems. | ||
// Math.pow(2, 30) - 1 | ||
|
@@ -60,13 +78,36 @@ var isPerformingWork = false; | |
var isHostCallbackScheduled = false; | ||
var isHostTimeoutScheduled = false; | ||
|
||
function requestHostCallbackWithProfiling(cb) { | ||
if (enableProfiling) { | ||
markSchedulerSuspended(); | ||
requestHostCallbackWithoutProfiling(cb); | ||
} | ||
} | ||
|
||
// Expose a shared array buffer that contains profiling information. | ||
export const unstable_sharedProfilingBuffer = | ||
enableProfiling && enableSharedProfilingBuffer ? sharedProfilingBuffer : null; | ||
|
||
const requestHostCallback = enableProfiling | ||
? requestHostCallbackWithProfiling | ||
: requestHostCallbackWithoutProfiling; | ||
|
||
function flushTask(task, callback, currentTime) { | ||
currentPriorityLevel = task.priorityLevel; | ||
var didUserCallbackTimeout = task.expirationTime <= currentTime; | ||
markTaskRun(task); | ||
var continuationCallback = callback(didUserCallbackTimeout); | ||
return typeof continuationCallback === 'function' | ||
? continuationCallback | ||
: null; | ||
if (typeof continuationCallback === 'function') { | ||
markTaskYield(task); | ||
return continuationCallback; | ||
} else { | ||
if (enableProfiling) { | ||
markTaskCompleted(task); | ||
task.isQueued = false; | ||
} | ||
return null; | ||
} | ||
} | ||
|
||
function advanceTimers(currentTime) { | ||
|
@@ -81,6 +122,10 @@ function advanceTimers(currentTime) { | |
pop(timerQueue); | ||
timer.sortIndex = timer.expirationTime; | ||
push(taskQueue, timer); | ||
if (enableProfiling) { | ||
markTaskStart(timer); | ||
timer.isQueued = true; | ||
} | ||
} else { | ||
// Remaining timers are pending. | ||
return; | ||
|
@@ -107,6 +152,10 @@ function handleTimeout(currentTime) { | |
} | ||
|
||
function flushWork(hasTimeRemaining, initialTime) { | ||
if (isHostCallbackScheduled) { | ||
markSchedulerUnsuspended(); | ||
} | ||
|
||
// We'll need a host callback the next time work is scheduled. | ||
isHostCallbackScheduled = false; | ||
if (isHostTimeoutScheduled) { | ||
|
@@ -152,6 +201,8 @@ function flushWork(hasTimeRemaining, initialTime) { | |
} | ||
// Return whether there's additional work | ||
if (currentTask !== null) { | ||
markSchedulerSuspended(); | ||
isHostCallbackScheduled = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've found this factoring very counterintuitive. It's like There's similar implicitness in the error code path. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error code path seems inconsistent with what this code seems to imply. The host config does schedule a callback in the error case (if this throws). markSchedulerSuspended(currentTime);
isHostCallbackScheduled = true; Should it? |
||
return true; | ||
} else { | ||
let firstTimer = peek(timerQueue); | ||
|
@@ -160,6 +211,17 @@ function flushWork(hasTimeRemaining, initialTime) { | |
} | ||
return false; | ||
} | ||
} catch (error) { | ||
if (currentTask !== null) { | ||
if (enableProfiling) { | ||
markTaskErrored(currentTask); | ||
currentTask.isQueued = false; | ||
} | ||
if (currentTask === peek(taskQueue)) { | ||
pop(taskQueue); | ||
} | ||
} | ||
throw error; | ||
} finally { | ||
currentTask = null; | ||
currentPriorityLevel = previousPriorityLevel; | ||
|
@@ -250,6 +312,7 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { | |
|
||
var startTime; | ||
var timeout; | ||
var label; | ||
if (typeof options === 'object' && options !== null) { | ||
var delay = options.delay; | ||
if (typeof delay === 'number' && delay > 0) { | ||
|
@@ -261,6 +324,12 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { | |
typeof options.timeout === 'number' | ||
? options.timeout | ||
: timeoutForPriorityLevel(priorityLevel); | ||
if (enableProfiling) { | ||
var _label = options.label; | ||
if (typeof _label === 'string') { | ||
label = _label; | ||
} | ||
} | ||
} else { | ||
timeout = timeoutForPriorityLevel(priorityLevel); | ||
startTime = currentTime; | ||
|
@@ -269,14 +338,21 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { | |
var expirationTime = startTime + timeout; | ||
|
||
var newTask = { | ||
id: taskIdCounter++, | ||
id: ++taskIdCounter, | ||
callback, | ||
priorityLevel, | ||
startTime, | ||
expirationTime, | ||
sortIndex: -1, | ||
}; | ||
|
||
if (enableProfiling) { | ||
newTask.isQueued = false; | ||
if (typeof options === 'object' && options !== null) { | ||
newTask.label = label; | ||
} | ||
} | ||
|
||
if (startTime > currentTime) { | ||
// This is a delayed task. | ||
newTask.sortIndex = startTime; | ||
|
@@ -295,6 +371,10 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { | |
} else { | ||
newTask.sortIndex = expirationTime; | ||
push(taskQueue, newTask); | ||
if (enableProfiling) { | ||
markTaskStart(newTask); | ||
newTask.isQueued = true; | ||
} | ||
// Schedule a host callback, if needed. If we're already performing work, | ||
// wait until the next time we yield. | ||
if (!isHostCallbackScheduled && !isPerformingWork) { | ||
|
@@ -323,10 +403,24 @@ function unstable_getFirstCallbackNode() { | |
} | ||
|
||
function unstable_cancelCallback(task) { | ||
// Null out the callback to indicate the task has been canceled. (Can't remove | ||
// from the queue because you can't remove arbitrary nodes from an array based | ||
// heap, only the first one.) | ||
task.callback = null; | ||
if (enableProfiling && task.isQueued) { | ||
markTaskCanceled(task); | ||
task.isQueued = false; | ||
} | ||
if (task !== null && task === peek(taskQueue)) { | ||
pop(taskQueue); | ||
if (enableProfiling && !isPerformingWork && taskQueue.length === 0) { | ||
// The queue is now empty. | ||
markSchedulerUnsuspended(); | ||
isHostCallbackScheduled = false; | ||
cancelHostCallback(); | ||
} | ||
} else { | ||
// Null out the callback to indicate the task has been canceled. (Can't | ||
// remove from the queue because you can't remove arbitrary nodes from an | ||
// array based heap, only the first one.) | ||
task.callback = null; | ||
} | ||
} | ||
|
||
function unstable_getCurrentPriorityLevel() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow | ||
*/ | ||
|
||
export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5; | ||
|
||
// TODO: Use symbols? | ||
export const NoPriority = 0; | ||
export const ImmediatePriority = 1; | ||
export const UserBlockingPriority = 2; | ||
export const NormalPriority = 3; | ||
export const LowPriority = 4; | ||
export const IdlePriority = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a copy paste mistake?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes :)