Skip to content
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

Add "nested-update" phase to Profiler API #20163

Merged
merged 1 commit into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 22 additions & 11 deletions packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
enableSchedulerTracing,
enableProfilerTimer,
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableSuspenseServerRenderer,
enableFundamentalAPI,
enableSuspenseCallback,
Expand Down Expand Up @@ -94,6 +95,7 @@ import {
import {onCommitUnmount} from './ReactFiberDevToolsHook.new';
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
import {
isCurrentUpdateNested,
getCommitTime,
recordLayoutEffectDuration,
startLayoutEffectTimer,
Expand Down Expand Up @@ -369,22 +371,24 @@ function commitProfilerPassiveEffect(
// It does not get reset until the start of the next commit phase.
const commitTime = getCommitTime();

let phase = finishedWork.alternate === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
if (isCurrentUpdateNested()) {
phase = 'nested-update';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on update-nested instead? That seems to read more like "Update (nested)" to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I think it reads a bit more awkward. When we talk about these things, we call them "nested updates".

}
}

if (typeof onPostCommit === 'function') {
if (enableSchedulerTracing) {
onPostCommit(
id,
finishedWork.alternate === null ? 'mount' : 'update',
phase,
passiveEffectDuration,
commitTime,
finishedRoot.memoizedInteractions,
);
} else {
onPostCommit(
id,
finishedWork.alternate === null ? 'mount' : 'update',
passiveEffectDuration,
commitTime,
);
onPostCommit(id, phase, passiveEffectDuration, commitTime);
}
}
break;
Expand Down Expand Up @@ -1362,11 +1366,18 @@ function commitLayoutEffectsForProfiler(
const OnRenderFlag = Update;
const OnCommitFlag = Callback;

let phase = current === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
if (isCurrentUpdateNested()) {
phase = 'nested-update';
}
}

if ((flags & OnRenderFlag) !== NoFlags && typeof onRender === 'function') {
if (enableSchedulerTracing) {
onRender(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
Expand All @@ -1376,7 +1387,7 @@ function commitLayoutEffectsForProfiler(
} else {
onRender(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
Expand All @@ -1393,15 +1404,15 @@ function commitLayoutEffectsForProfiler(
if (enableSchedulerTracing) {
onCommit(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
effectDuration,
commitTime,
finishedRoot.memoizedInteractions,
);
} else {
onCommit(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
effectDuration,
commitTime,
);
Expand Down
33 changes: 22 additions & 11 deletions packages/react-reconciler/src/ReactFiberCommitWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
enableSchedulerTracing,
enableProfilerTimer,
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableSuspenseServerRenderer,
enableFundamentalAPI,
enableSuspenseCallback,
Expand Down Expand Up @@ -73,6 +74,7 @@ import invariant from 'shared/invariant';
import {onCommitUnmount} from './ReactFiberDevToolsHook.old';
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
import {
isCurrentUpdateNested,
getCommitTime,
recordLayoutEffectDuration,
startLayoutEffectTimer,
Expand Down Expand Up @@ -434,22 +436,24 @@ export function commitPassiveEffectDurations(
// It does not get reset until the start of the next commit phase.
const commitTime = getCommitTime();

let phase = finishedWork.alternate === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
if (isCurrentUpdateNested()) {
phase = 'nested-update';
}
}

if (typeof onPostCommit === 'function') {
if (enableSchedulerTracing) {
onPostCommit(
id,
finishedWork.alternate === null ? 'mount' : 'update',
phase,
passiveEffectDuration,
commitTime,
finishedRoot.memoizedInteractions,
);
} else {
onPostCommit(
id,
finishedWork.alternate === null ? 'mount' : 'update',
passiveEffectDuration,
commitTime,
);
onPostCommit(id, phase, passiveEffectDuration, commitTime);
}
}

Expand Down Expand Up @@ -706,11 +710,18 @@ function commitLifeCycles(

const commitTime = getCommitTime();

let phase = current === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
if (isCurrentUpdateNested()) {
phase = 'nested-update';
}
}

if (typeof onRender === 'function') {
if (enableSchedulerTracing) {
onRender(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
Expand All @@ -720,7 +731,7 @@ function commitLifeCycles(
} else {
onRender(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
Expand All @@ -734,15 +745,15 @@ function commitLifeCycles(
if (enableSchedulerTracing) {
onCommit(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
effectDuration,
commitTime,
finishedRoot.memoizedInteractions,
);
} else {
onCommit(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
phase,
effectDuration,
commitTime,
);
Expand Down
11 changes: 11 additions & 0 deletions packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
enableSuspenseServerRenderer,
replayFailedUnitOfWorkWithInvokeGuardedCallback,
enableProfilerTimer,
enableProfilerNestedUpdatePhase,
enableSchedulerTracing,
warnAboutUnmockedScheduler,
deferRenderPhaseUpdateToNextBatch,
Expand Down Expand Up @@ -195,9 +196,11 @@ import {
} from './ReactFiberStack.new';

import {
markNestedUpdateScheduled,
recordCommitTime,
startProfilerTimer,
stopProfilerTimerIfRunningAndRecordDelta,
syncNestedUpdateFlag,
} from './ReactProfilerTimer.new';

// DEV stuff
Expand Down Expand Up @@ -939,6 +942,10 @@ function markRootSuspended(root, suspendedLanes) {
// This is the entry point for synchronous tasks that don't go
// through Scheduler
function performSyncWorkOnRoot(root) {
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
syncNestedUpdateFlag();
}

invariant(
(executionContext & (RenderContext | CommitContext)) === NoContext,
'Should not already be working.',
Expand Down Expand Up @@ -1996,6 +2003,10 @@ function commitRootImpl(root, renderPriorityLevel) {
}

if (remainingLanes === SyncLane) {
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
markNestedUpdateScheduled();
}

// Count the number of times the root synchronously re-renders without
// finishing. If there are too many, it indicates an infinite update loop.
if (root === rootWithNestedUpdates) {
Expand Down
11 changes: 11 additions & 0 deletions packages/react-reconciler/src/ReactFiberWorkLoop.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
replayFailedUnitOfWorkWithInvokeGuardedCallback,
enableProfilerTimer,
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableSchedulerTracing,
warnAboutUnmockedScheduler,
deferRenderPhaseUpdateToNextBatch,
Expand Down Expand Up @@ -207,11 +208,13 @@ import {
} from './ReactFiberStack.old';

import {
markNestedUpdateScheduled,
recordCommitTime,
recordPassiveEffectDuration,
startPassiveEffectTimer,
startProfilerTimer,
stopProfilerTimerIfRunningAndRecordDelta,
syncNestedUpdateFlag,
} from './ReactProfilerTimer.old';

// DEV stuff
Expand Down Expand Up @@ -962,6 +965,10 @@ function markRootSuspended(root, suspendedLanes) {
// This is the entry point for synchronous tasks that don't go
// through Scheduler
function performSyncWorkOnRoot(root) {
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
syncNestedUpdateFlag();
}

invariant(
(executionContext & (RenderContext | CommitContext)) === NoContext,
'Should not already be working.',
Expand Down Expand Up @@ -2189,6 +2196,10 @@ function commitRootImpl(root, renderPriorityLevel) {
}

if (remainingLanes === SyncLane) {
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
markNestedUpdateScheduled();
}

// Count the number of times the root synchronously re-renders without
// finishing. If there are too many, it indicates an infinite update loop.
if (root === rootWithNestedUpdates) {
Expand Down
45 changes: 44 additions & 1 deletion packages/react-reconciler/src/ReactProfilerTimer.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
import type {Fiber} from './ReactInternalTypes';

import {
enableProfilerTimer,
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableProfilerTimer,
} from 'shared/ReactFeatureFlags';
import {Profiler} from './ReactWorkTags';

Expand All @@ -23,10 +24,13 @@ const {unstable_now: now} = Scheduler;

export type ProfilerTimer = {
getCommitTime(): number,
isCurrentUpdateNested(): boolean,
markNestedUpdateScheduled(): void,
recordCommitTime(): void,
startProfilerTimer(fiber: Fiber): void,
stopProfilerTimerIfRunning(fiber: Fiber): void,
stopProfilerTimerIfRunningAndRecordDelta(fiber: Fiber): void,
syncNestedUpdateFlag(): void,
...
};

Expand All @@ -35,6 +39,42 @@ let layoutEffectStartTime: number = -1;
let profilerStartTime: number = -1;
let passiveEffectStartTime: number = -1;

/**
* Tracks whether the current update was a nested/cascading update (scheduled from a layout effect).
*
* The overall sequence is:
* 1. render
* 2. commit (and call `onRender`, `onCommit`)
* 3. check for nested updates
* 4. flush passive effects (and call `onPostCommit`)
*
* Nested updates are identified in step 3 above,
* but step 4 still applies to the work that was just committed.
* We use two flags to track nested updates then:
* one tracks whether the upcoming update is a nested update,
* and the other tracks whether the current update was a nested update.
* The first value gets synced to the second at the start of the render phase.
*/
let currentUpdateIsNested: boolean = false;
let nestedUpdateScheduled: boolean = false;

function isCurrentUpdateNested(): boolean {
return currentUpdateIsNested;
}

function markNestedUpdateScheduled(): void {
if (enableProfilerNestedUpdatePhase) {
nestedUpdateScheduled = true;
}
}

function syncNestedUpdateFlag(): void {
if (enableProfilerNestedUpdatePhase) {
currentUpdateIsNested = nestedUpdateScheduled;
nestedUpdateScheduled = false;
}
}

function getCommitTime(): number {
return commitTime;
}
Expand Down Expand Up @@ -161,6 +201,8 @@ function transferActualDuration(fiber: Fiber): void {

export {
getCommitTime,
isCurrentUpdateNested,
markNestedUpdateScheduled,
recordCommitTime,
recordLayoutEffectDuration,
recordPassiveEffectDuration,
Expand All @@ -169,5 +211,6 @@ export {
startProfilerTimer,
stopProfilerTimerIfRunning,
stopProfilerTimerIfRunningAndRecordDelta,
syncNestedUpdateFlag,
transferActualDuration,
};