Skip to content

Commit

Permalink
[Fiber] Move Profiler onPostCommit processing of passive effect durat…
Browse files Browse the repository at this point in the history
…ions to plain passive effect (#30966)

We used to queue a separate third passive phase to invoke onPostCommit
but this is unnecessary. We can just treat it as a plain passive effect.
This means it is interleaved with other passive effects but we only need
to know the duration of the things below us which is already done at
this point.

I also extracted the user space call to onPostCommit into
ReactCommitEffects. Same as onCommit. It's now covered by
runWithFiberInDEV and catches.
  • Loading branch information
sebmarkbage authored Sep 16, 2024
1 parent 0eab377 commit ee1a403
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 96 deletions.
8 changes: 8 additions & 0 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,10 @@ function updateProfiler(
workInProgress.flags |= Update;

if (enableProfilerCommitHooks) {
// Schedule a passive effect for this Profiler to call onPostCommit hooks.
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
// because the effect is also where times bubble to parent Profilers.
workInProgress.flags |= Passive;
// Reset effect durations for the next eventual effect phase.
// These are reset during render to allow the DevTools commit hook a chance to read them,
const stateNode = workInProgress.stateNode;
Expand Down Expand Up @@ -3700,6 +3704,10 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
}

if (enableProfilerCommitHooks) {
// Schedule a passive effect for this Profiler to call onPostCommit hooks.
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
// because the effect is also where times bubble to parent Profilers.
workInProgress.flags |= Passive;
// Reset effect durations for the next eventual effect phase.
// These are reset during render to allow the DevTools commit hook a chance to read them,
const stateNode = workInProgress.stateNode;
Expand Down
53 changes: 51 additions & 2 deletions packages/react-reconciler/src/ReactFiberCommitEffects.js
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ function commitProfiler(
commitTime: number,
effectDuration: number,
) {
const {onCommit, onRender} = finishedWork.memoizedProps;
const {id, onCommit, onRender} = finishedWork.memoizedProps;

let phase = current === null ? 'mount' : 'update';
if (enableProfilerNestedUpdatePhase) {
Expand All @@ -892,7 +892,7 @@ function commitProfiler(

if (typeof onRender === 'function') {
onRender(
finishedWork.memoizedProps.id,
id,
phase,
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
Expand Down Expand Up @@ -938,3 +938,52 @@ export function commitProfilerUpdate(
}
}
}

function commitProfilerPostCommitImpl(
finishedWork: Fiber,
current: Fiber | null,
commitTime: number,
passiveEffectDuration: number,
): void {
const {id, onPostCommit} = finishedWork.memoizedProps;

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

if (typeof onPostCommit === 'function') {
onPostCommit(id, phase, passiveEffectDuration, commitTime);
}
}

export function commitProfilerPostCommit(
finishedWork: Fiber,
current: Fiber | null,
commitTime: number,
passiveEffectDuration: number,
) {
try {
if (__DEV__) {
runWithFiberInDEV(
finishedWork,
commitProfilerPostCommitImpl,
finishedWork,
current,
commitTime,
passiveEffectDuration,
);
} else {
commitProfilerPostCommitImpl(
finishedWork,
current,
commitTime,
passiveEffectDuration,
);
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
116 changes: 47 additions & 69 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import {
enablePersistedModeClonedFlag,
enableProfilerTimer,
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableSchedulingProfiler,
enableSuspenseCallback,
enableScopeAPI,
Expand Down Expand Up @@ -100,7 +99,6 @@ import {
Cloned,
} from './ReactFiberFlags';
import {
isCurrentUpdateNested,
getCommitTime,
recordLayoutEffectDuration,
startLayoutEffectTimer,
Expand Down Expand Up @@ -137,7 +135,6 @@ import {
captureCommitPhaseError,
resolveRetryWakeable,
markCommitTimeOfFallback,
enqueuePendingPassiveProfilerEffect,
restorePendingUpdaters,
addTransitionStartCallbackToPendingTransition,
addTransitionProgressCallbackToPendingTransition,
Expand Down Expand Up @@ -193,6 +190,7 @@ import {
safelyDetachRef,
safelyCallDestroy,
commitProfilerUpdate,
commitProfilerPostCommit,
commitRootCallbacks,
} from './ReactFiberCommitEffects';
import {
Expand Down Expand Up @@ -394,62 +392,6 @@ function commitBeforeMutationEffectsDeletion(deletion: Fiber) {
}
}

export function commitPassiveEffectDurations(
finishedRoot: FiberRoot,
finishedWork: Fiber,
): void {
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
getExecutionContext() & CommitContext
) {
// Only Profilers with work in their subtree will have an Update effect scheduled.
if ((finishedWork.flags & Update) !== NoFlags) {
switch (finishedWork.tag) {
case Profiler: {
const {passiveEffectDuration} = finishedWork.stateNode;
const {id, onPostCommit} = finishedWork.memoizedProps;

// This value will still reflect the previous commit phase.
// 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') {
onPostCommit(id, phase, passiveEffectDuration, commitTime);
}

// Bubble times to the next nearest ancestor Profiler.
// After we process that Profiler, we'll bubble further up.
let parentFiber = finishedWork.return;
outer: while (parentFiber !== null) {
switch (parentFiber.tag) {
case HostRoot:
const root = parentFiber.stateNode;
root.passiveEffectDuration += passiveEffectDuration;
break outer;
case Profiler:
const parentStateNode = parentFiber.stateNode;
parentStateNode.passiveEffectDuration += passiveEffectDuration;
break outer;
}
parentFiber = parentFiber.return;
}
break;
}
default:
break;
}
}
}
}

function commitLayoutEffectOnFiber(
finishedRoot: FiberRoot,
current: Fiber | null,
Expand Down Expand Up @@ -557,11 +499,6 @@ function commitLayoutEffectOnFiber(
effectDuration,
);

// Schedule a passive effect for this Profiler to call onPostCommit hooks.
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
// because the effect is also where times bubble to parent Profilers.
enqueuePendingPassiveProfilerEffect(finishedWork);

// Propagate layout effect durations to the next nearest Profiler ancestor.
// Do not reset these values until the next render so DevTools has a chance to read them first.
let parentFiber = finishedWork.return;
Expand Down Expand Up @@ -2475,11 +2412,6 @@ export function reappearLayoutEffects(
effectDuration,
);

// Schedule a passive effect for this Profiler to call onPostCommit hooks.
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
// because the effect is also where times bubble to parent Profilers.
enqueuePendingPassiveProfilerEffect(finishedWork);

// Propagate layout effect durations to the next nearest Profiler ancestor.
// Do not reset these values until the next render so DevTools has a chance to read them first.
let parentFiber = finishedWork.return;
Expand Down Expand Up @@ -2824,6 +2756,52 @@ function commitPassiveMountOnFiber(
}
break;
}
case Profiler: {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
);

// Only Profilers with work in their subtree will have a Passive effect scheduled.
if (flags & Passive) {
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
getExecutionContext() & CommitContext
) {
const {passiveEffectDuration} = finishedWork.stateNode;

commitProfilerPostCommit(
finishedWork,
finishedWork.alternate,
// This value will still reflect the previous commit phase.
// It does not get reset until the start of the next commit phase.
getCommitTime(),
passiveEffectDuration,
);

// Bubble times to the next nearest ancestor Profiler.
// After we process that Profiler, we'll bubble further up.
let parentFiber = finishedWork.return;
outer: while (parentFiber !== null) {
switch (parentFiber.tag) {
case HostRoot:
const root = parentFiber.stateNode;
root.passiveEffectDuration += passiveEffectDuration;
break outer;
case Profiler:
const parentStateNode = parentFiber.stateNode;
parentStateNode.passiveEffectDuration += passiveEffectDuration;
break outer;
}
parentFiber = parentFiber.return;
}
}
}
break;
}
case LegacyHiddenComponent: {
if (enableLegacyHidden) {
recursivelyTraversePassiveMountEffects(
Expand Down
25 changes: 0 additions & 25 deletions packages/react-reconciler/src/ReactFiberWorkLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ import {
commitBeforeMutationEffects,
commitLayoutEffects,
commitMutationEffects,
commitPassiveEffectDurations,
commitPassiveMountEffects,
commitPassiveUnmountEffects,
disappearLayoutEffects,
Expand Down Expand Up @@ -580,7 +579,6 @@ let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
let rootDoesHavePassiveEffects: boolean = false;
let rootWithPendingPassiveEffects: FiberRoot | null = null;
let pendingPassiveEffectsLanes: Lanes = NoLanes;
let pendingPassiveProfilerEffects: Array<Fiber> = [];
let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes;
let pendingPassiveTransitions: Array<Transition> | null = null;

Expand Down Expand Up @@ -3475,19 +3473,6 @@ export function flushPassiveEffects(): boolean {
return false;
}

export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {
if (enableProfilerTimer && enableProfilerCommitHooks) {
pendingPassiveProfilerEffects.push(fiber);
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
scheduleCallback(NormalSchedulerPriority, () => {
flushPassiveEffects();
return null;
});
}
}
}

function flushPassiveEffectsImpl() {
if (rootWithPendingPassiveEffects === null) {
return false;
Expand Down Expand Up @@ -3528,16 +3513,6 @@ function flushPassiveEffectsImpl() {
commitPassiveUnmountEffects(root.current);
commitPassiveMountEffects(root, root.current, lanes, transitions);

// TODO: Move to commitPassiveMountEffects
if (enableProfilerTimer && enableProfilerCommitHooks) {
const profilerEffects = pendingPassiveProfilerEffects;
pendingPassiveProfilerEffects = [];
for (let i = 0; i < profilerEffects.length; i++) {
const fiber = ((profilerEffects[i]: any): Fiber);
commitPassiveEffectDurations(root, fiber);
}
}

if (__DEV__) {
if (enableDebugTracing) {
logPassiveEffectsStopped();
Expand Down

0 comments on commit ee1a403

Please sign in to comment.