Skip to content

Commit cbcc3f5

Browse files
author
Brian Vaughn
committed
Track which fibers scheduled the current render work
This change is gated behind a new profiling-mode-only feature flag, "enableUpdaterTracking".
1 parent d778518 commit cbcc3f5

22 files changed

+999
-12
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
enableStrictEffects,
3838
deletedTreeCleanUpLevel,
3939
enableSuspenseLayoutEffectSemantics,
40+
enableUpdaterTracking,
4041
} from 'shared/ReactFeatureFlags';
4142
import {
4243
FunctionComponent,
@@ -89,7 +90,7 @@ import {
8990
resetCurrentFiber as resetCurrentDebugFiberInDEV,
9091
setCurrentFiber as setCurrentDebugFiberInDEV,
9192
} from './ReactCurrentFiber';
92-
93+
import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
9394
import {onCommitUnmount} from './ReactFiberDevToolsHook.new';
9495
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
9596
import {
@@ -137,6 +138,7 @@ import {
137138
resolveRetryWakeable,
138139
markCommitTimeOfFallback,
139140
enqueuePendingPassiveProfilerEffect,
141+
restorePendingUpdaters,
140142
} from './ReactFiberWorkLoop.new';
141143
import {
142144
NoFlags as NoHookEffect,
@@ -164,6 +166,10 @@ const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
164166

165167
let nextEffect: Fiber | null = null;
166168

169+
// Used for Profiling builds to track updaters.
170+
let inProgressLanes: Lanes | null = null;
171+
let inProgressRoot: FiberRoot | null = null;
172+
167173
const callComponentWillUnmountWithTimer = function(current, instance) {
168174
instance.props = current.memoizedProps;
169175
instance.state = current.memoizedState;
@@ -2096,6 +2102,20 @@ function attachSuspenseRetryListeners(finishedWork: Fiber) {
20962102
}
20972103
}
20982104
retryCache.add(wakeable);
2105+
2106+
if (enableUpdaterTracking) {
2107+
if (isDevToolsPresent) {
2108+
if (inProgressLanes !== null && inProgressRoot !== null) {
2109+
// If we have pending work still, associate the original updaters with it.
2110+
restorePendingUpdaters(inProgressRoot, inProgressLanes);
2111+
} else {
2112+
throw Error(
2113+
'Expected finished root and lanes to be set. This is a bug in React.',
2114+
);
2115+
}
2116+
}
2117+
}
2118+
20992119
wakeable.then(retry, retry);
21002120
}
21012121
});
@@ -2126,9 +2146,19 @@ function commitResetTextContent(current: Fiber) {
21262146
resetTextContent(current.stateNode);
21272147
}
21282148

2129-
export function commitMutationEffects(root: FiberRoot, firstChild: Fiber) {
2149+
export function commitMutationEffects(
2150+
root: FiberRoot,
2151+
firstChild: Fiber,
2152+
committedLanes: Lanes,
2153+
) {
2154+
inProgressLanes = committedLanes;
2155+
inProgressRoot = root;
21302156
nextEffect = firstChild;
2157+
21312158
commitMutationEffects_begin(root);
2159+
2160+
inProgressLanes = null;
2161+
inProgressRoot = null;
21322162
}
21332163

21342164
function commitMutationEffects_begin(root: FiberRoot) {
@@ -2282,8 +2312,14 @@ export function commitLayoutEffects(
22822312
root: FiberRoot,
22832313
committedLanes: Lanes,
22842314
): void {
2315+
inProgressLanes = committedLanes;
2316+
inProgressRoot = root;
22852317
nextEffect = finishedWork;
2318+
22862319
commitLayoutEffects_begin(finishedWork, root, committedLanes);
2320+
2321+
inProgressLanes = null;
2322+
inProgressRoot = null;
22872323
}
22882324

22892325
function commitLayoutEffects_begin(

packages/react-reconciler/src/ReactFiberCommitWork.old.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
enableStrictEffects,
3838
deletedTreeCleanUpLevel,
3939
enableSuspenseLayoutEffectSemantics,
40+
enableUpdaterTracking,
4041
} from 'shared/ReactFeatureFlags';
4142
import {
4243
FunctionComponent,
@@ -89,7 +90,7 @@ import {
8990
resetCurrentFiber as resetCurrentDebugFiberInDEV,
9091
setCurrentFiber as setCurrentDebugFiberInDEV,
9192
} from './ReactCurrentFiber';
92-
93+
import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
9394
import {onCommitUnmount} from './ReactFiberDevToolsHook.old';
9495
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
9596
import {
@@ -137,6 +138,7 @@ import {
137138
resolveRetryWakeable,
138139
markCommitTimeOfFallback,
139140
enqueuePendingPassiveProfilerEffect,
141+
restorePendingUpdaters,
140142
} from './ReactFiberWorkLoop.old';
141143
import {
142144
NoFlags as NoHookEffect,
@@ -164,6 +166,10 @@ const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
164166

165167
let nextEffect: Fiber | null = null;
166168

169+
// Used for Profiling builds to track updaters.
170+
let inProgressLanes: Lanes | null = null;
171+
let inProgressRoot: FiberRoot | null = null;
172+
167173
const callComponentWillUnmountWithTimer = function(current, instance) {
168174
instance.props = current.memoizedProps;
169175
instance.state = current.memoizedState;
@@ -2096,6 +2102,20 @@ function attachSuspenseRetryListeners(finishedWork: Fiber) {
20962102
}
20972103
}
20982104
retryCache.add(wakeable);
2105+
2106+
if (enableUpdaterTracking) {
2107+
if (isDevToolsPresent) {
2108+
if (inProgressLanes !== null && inProgressRoot !== null) {
2109+
// If we have pending work still, associate the original updaters with it.
2110+
restorePendingUpdaters(inProgressRoot, inProgressLanes);
2111+
} else {
2112+
throw Error(
2113+
'Expected finished root and lanes to be set. This is a bug in React.',
2114+
);
2115+
}
2116+
}
2117+
}
2118+
20992119
wakeable.then(retry, retry);
21002120
}
21012121
});
@@ -2126,9 +2146,19 @@ function commitResetTextContent(current: Fiber) {
21262146
resetTextContent(current.stateNode);
21272147
}
21282148

2129-
export function commitMutationEffects(root: FiberRoot, firstChild: Fiber) {
2149+
export function commitMutationEffects(
2150+
root: FiberRoot,
2151+
firstChild: Fiber,
2152+
committedLanes: Lanes,
2153+
) {
2154+
inProgressLanes = committedLanes;
2155+
inProgressRoot = root;
21302156
nextEffect = firstChild;
2157+
21312158
commitMutationEffects_begin(root);
2159+
2160+
inProgressLanes = null;
2161+
inProgressRoot = null;
21322162
}
21332163

21342164
function commitMutationEffects_begin(root: FiberRoot) {
@@ -2282,8 +2312,14 @@ export function commitLayoutEffects(
22822312
root: FiberRoot,
22832313
committedLanes: Lanes,
22842314
): void {
2315+
inProgressLanes = committedLanes;
2316+
inProgressRoot = root;
22852317
nextEffect = finishedWork;
2318+
22862319
commitLayoutEffects_begin(finishedWork, root, committedLanes);
2320+
2321+
inProgressLanes = null;
2322+
inProgressRoot = null;
22872323
}
22882324

22892325
function commitLayoutEffects_begin(

packages/react-reconciler/src/ReactFiberLane.new.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ export type Lanes = number;
3535
export type Lane = number;
3636
export type LaneMap<T> = Array<T>;
3737

38-
import {enableCache, enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
38+
import {
39+
enableCache,
40+
enableSchedulingProfiler,
41+
enableUpdaterTracking,
42+
} from 'shared/ReactFeatureFlags';
43+
import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
3944

4045
// Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler.
4146
// If those values are changed that package should be rebuilt and redeployed.
@@ -742,6 +747,57 @@ export function getBumpedLaneForHydration(
742747
return lane;
743748
}
744749

750+
export function addFiberToLanesMap(
751+
root: FiberRoot,
752+
fiber: Fiber,
753+
lanes: Lanes | Lane,
754+
) {
755+
if (!enableUpdaterTracking) {
756+
return;
757+
}
758+
if (!isDevToolsPresent) {
759+
return;
760+
}
761+
const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
762+
while (lanes > 0) {
763+
const index = laneToIndex(lanes);
764+
const lane = 1 << index;
765+
766+
const updaters = pendingUpdatersLaneMap[index];
767+
updaters.add(fiber);
768+
769+
lanes &= ~lane;
770+
}
771+
}
772+
773+
export function movePendingFibersToMemoized(root: FiberRoot, lanes: Lanes) {
774+
if (!enableUpdaterTracking) {
775+
return;
776+
}
777+
if (!isDevToolsPresent) {
778+
return;
779+
}
780+
const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
781+
const memoizedUpdaters = root.memoizedUpdaters;
782+
while (lanes > 0) {
783+
const index = laneToIndex(lanes);
784+
const lane = 1 << index;
785+
786+
const updaters = pendingUpdatersLaneMap[index];
787+
if (updaters.size > 0) {
788+
updaters.forEach(fiber => {
789+
const alternate = fiber.alternate;
790+
if (alternate === null || !memoizedUpdaters.has(alternate)) {
791+
memoizedUpdaters.add(fiber);
792+
}
793+
});
794+
updaters.clear();
795+
}
796+
797+
lanes &= ~lane;
798+
}
799+
}
800+
745801
const clz32 = Math.clz32 ? Math.clz32 : clz32Fallback;
746802

747803
// Count leading zeros. Only used on lanes, so assume input is an integer.

packages/react-reconciler/src/ReactFiberLane.old.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ export type Lanes = number;
3535
export type Lane = number;
3636
export type LaneMap<T> = Array<T>;
3737

38-
import {enableCache, enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
38+
import {
39+
enableCache,
40+
enableSchedulingProfiler,
41+
enableUpdaterTracking,
42+
} from 'shared/ReactFeatureFlags';
43+
import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
3944

4045
// Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler.
4146
// If those values are changed that package should be rebuilt and redeployed.
@@ -742,6 +747,57 @@ export function getBumpedLaneForHydration(
742747
return lane;
743748
}
744749

750+
export function addFiberToLanesMap(
751+
root: FiberRoot,
752+
fiber: Fiber,
753+
lanes: Lanes | Lane,
754+
) {
755+
if (!enableUpdaterTracking) {
756+
return;
757+
}
758+
if (!isDevToolsPresent) {
759+
return;
760+
}
761+
const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
762+
while (lanes > 0) {
763+
const index = laneToIndex(lanes);
764+
const lane = 1 << index;
765+
766+
const updaters = pendingUpdatersLaneMap[index];
767+
updaters.add(fiber);
768+
769+
lanes &= ~lane;
770+
}
771+
}
772+
773+
export function movePendingFibersToMemoized(root: FiberRoot, lanes: Lanes) {
774+
if (!enableUpdaterTracking) {
775+
return;
776+
}
777+
if (!isDevToolsPresent) {
778+
return;
779+
}
780+
const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
781+
const memoizedUpdaters = root.memoizedUpdaters;
782+
while (lanes > 0) {
783+
const index = laneToIndex(lanes);
784+
const lane = 1 << index;
785+
786+
const updaters = pendingUpdatersLaneMap[index];
787+
if (updaters.size > 0) {
788+
updaters.forEach(fiber => {
789+
const alternate = fiber.alternate;
790+
if (alternate === null || !memoizedUpdaters.has(alternate)) {
791+
memoizedUpdaters.add(fiber);
792+
}
793+
});
794+
updaters.clear();
795+
}
796+
797+
lanes &= ~lane;
798+
}
799+
}
800+
745801
const clz32 = Math.clz32 ? Math.clz32 : clz32Fallback;
746802

747803
// Count leading zeros. Only used on lanes, so assume input is an integer.

packages/react-reconciler/src/ReactFiberRoot.new.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
NoLane,
1717
NoLanes,
1818
NoTimestamp,
19+
TotalLanes,
1920
createLaneMap,
2021
} from './ReactFiberLane.new';
2122
import {
@@ -24,6 +25,7 @@ import {
2425
enableCache,
2526
enableProfilerCommitHooks,
2627
enableProfilerTimer,
28+
enableUpdaterTracking,
2729
} from 'shared/ReactFeatureFlags';
2830
import {unstable_getThreadID} from 'scheduler/tracing';
2931
import {initializeUpdateQueue} from './ReactUpdateQueue.new';
@@ -77,6 +79,14 @@ function FiberRootNode(containerInfo, tag, hydrate) {
7779
this.passiveEffectDuration = 0;
7880
}
7981

82+
if (enableUpdaterTracking) {
83+
this.memoizedUpdaters = new Set();
84+
const pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
85+
for (let i = 0; i < TotalLanes; i++) {
86+
pendingUpdatersLaneMap.push(new Set());
87+
}
88+
}
89+
8090
if (__DEV__) {
8191
switch (tag) {
8292
case ConcurrentRoot:

packages/react-reconciler/src/ReactFiberRoot.old.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
NoLane,
1717
NoLanes,
1818
NoTimestamp,
19+
TotalLanes,
1920
createLaneMap,
2021
} from './ReactFiberLane.old';
2122
import {
@@ -24,6 +25,7 @@ import {
2425
enableCache,
2526
enableProfilerCommitHooks,
2627
enableProfilerTimer,
28+
enableUpdaterTracking,
2729
} from 'shared/ReactFeatureFlags';
2830
import {unstable_getThreadID} from 'scheduler/tracing';
2931
import {initializeUpdateQueue} from './ReactUpdateQueue.old';
@@ -77,6 +79,14 @@ function FiberRootNode(containerInfo, tag, hydrate) {
7779
this.passiveEffectDuration = 0;
7880
}
7981

82+
if (enableUpdaterTracking) {
83+
this.memoizedUpdaters = new Set();
84+
const pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
85+
for (let i = 0; i < TotalLanes; i++) {
86+
pendingUpdatersLaneMap.push(new Set());
87+
}
88+
}
89+
8090
if (__DEV__) {
8191
switch (tag) {
8292
case ConcurrentRoot:

0 commit comments

Comments
 (0)