Skip to content

Commit 0c6681e

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 b4f119c commit 0c6681e

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
@@ -36,6 +36,7 @@ import {
3636
enableScopeAPI,
3737
enableStrictEffects,
3838
deletedTreeCleanUpLevel,
39+
enableUpdaterTracking,
3940
} from 'shared/ReactFeatureFlags';
4041
import {
4142
FunctionComponent,
@@ -86,7 +87,7 @@ import {
8687
resetCurrentFiber as resetCurrentDebugFiberInDEV,
8788
setCurrentFiber as setCurrentDebugFiberInDEV,
8889
} from './ReactCurrentFiber';
89-
90+
import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
9091
import {onCommitUnmount} from './ReactFiberDevToolsHook.new';
9192
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
9293
import {
@@ -134,6 +135,7 @@ import {
134135
resolveRetryWakeable,
135136
markCommitTimeOfFallback,
136137
enqueuePendingPassiveProfilerEffect,
138+
restorePendingUpdaters,
137139
} from './ReactFiberWorkLoop.new';
138140
import {
139141
NoFlags as NoHookEffect,
@@ -153,6 +155,10 @@ const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
153155

154156
let nextEffect: Fiber | null = null;
155157

158+
// Used for Profiling builds to track updaters.
159+
let inProgressLanes: Lanes | null = null;
160+
let inProgressRoot: FiberRoot | null = null;
161+
156162
const callComponentWillUnmountWithTimer = function(current, instance) {
157163
instance.props = current.memoizedProps;
158164
instance.state = current.memoizedState;
@@ -1948,6 +1954,20 @@ function attachSuspenseRetryListeners(finishedWork: Fiber) {
19481954
}
19491955
}
19501956
retryCache.add(wakeable);
1957+
1958+
if (enableUpdaterTracking) {
1959+
if (isDevToolsPresent) {
1960+
if (inProgressLanes !== null && inProgressRoot !== null) {
1961+
// If we have pending work still, associate the original updaters with it.
1962+
restorePendingUpdaters(inProgressRoot, inProgressLanes);
1963+
} else {
1964+
throw Error(
1965+
'Expected finished root and lanes to be set. This is a bug in React.',
1966+
);
1967+
}
1968+
}
1969+
}
1970+
19511971
wakeable.then(retry, retry);
19521972
}
19531973
});
@@ -1978,9 +1998,19 @@ function commitResetTextContent(current: Fiber) {
19781998
resetTextContent(current.stateNode);
19791999
}
19802000

1981-
export function commitMutationEffects(root: FiberRoot, firstChild: Fiber) {
2001+
export function commitMutationEffects(
2002+
root: FiberRoot,
2003+
firstChild: Fiber,
2004+
committedLanes: Lanes,
2005+
) {
2006+
inProgressLanes = committedLanes;
2007+
inProgressRoot = root;
19822008
nextEffect = firstChild;
2009+
19832010
commitMutationEffects_begin(root);
2011+
2012+
inProgressLanes = null;
2013+
inProgressRoot = null;
19842014
}
19852015

19862016
function commitMutationEffects_begin(root: FiberRoot) {
@@ -2134,8 +2164,14 @@ export function commitLayoutEffects(
21342164
root: FiberRoot,
21352165
committedLanes: Lanes,
21362166
): void {
2167+
inProgressLanes = committedLanes;
2168+
inProgressRoot = root;
21372169
nextEffect = finishedWork;
2170+
21382171
commitLayoutEffects_begin(finishedWork, root, committedLanes);
2172+
2173+
inProgressLanes = null;
2174+
inProgressRoot = null;
21392175
}
21402176

21412177
function commitLayoutEffects_begin(

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

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
enableScopeAPI,
3737
enableStrictEffects,
3838
deletedTreeCleanUpLevel,
39+
enableUpdaterTracking,
3940
} from 'shared/ReactFeatureFlags';
4041
import {
4142
FunctionComponent,
@@ -86,7 +87,7 @@ import {
8687
resetCurrentFiber as resetCurrentDebugFiberInDEV,
8788
setCurrentFiber as setCurrentDebugFiberInDEV,
8889
} from './ReactCurrentFiber';
89-
90+
import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
9091
import {onCommitUnmount} from './ReactFiberDevToolsHook.old';
9192
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
9293
import {
@@ -134,6 +135,7 @@ import {
134135
resolveRetryWakeable,
135136
markCommitTimeOfFallback,
136137
enqueuePendingPassiveProfilerEffect,
138+
restorePendingUpdaters,
137139
} from './ReactFiberWorkLoop.old';
138140
import {
139141
NoFlags as NoHookEffect,
@@ -153,6 +155,10 @@ const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
153155

154156
let nextEffect: Fiber | null = null;
155157

158+
// Used for Profiling builds to track updaters.
159+
let inProgressLanes: Lanes | null = null;
160+
let inProgressRoot: FiberRoot | null = null;
161+
156162
const callComponentWillUnmountWithTimer = function(current, instance) {
157163
instance.props = current.memoizedProps;
158164
instance.state = current.memoizedState;
@@ -1948,6 +1954,20 @@ function attachSuspenseRetryListeners(finishedWork: Fiber) {
19481954
}
19491955
}
19501956
retryCache.add(wakeable);
1957+
1958+
if (enableUpdaterTracking) {
1959+
if (isDevToolsPresent) {
1960+
if (inProgressLanes !== null && inProgressRoot !== null) {
1961+
// If we have pending work still, associate the original updaters with it.
1962+
restorePendingUpdaters(inProgressRoot, inProgressLanes);
1963+
} else {
1964+
throw Error(
1965+
'Expected finished root and lanes to be set. This is a bug in React.',
1966+
);
1967+
}
1968+
}
1969+
}
1970+
19511971
wakeable.then(retry, retry);
19521972
}
19531973
});
@@ -1978,9 +1998,19 @@ function commitResetTextContent(current: Fiber) {
19781998
resetTextContent(current.stateNode);
19791999
}
19802000

1981-
export function commitMutationEffects(root: FiberRoot, firstChild: Fiber) {
2001+
export function commitMutationEffects(
2002+
root: FiberRoot,
2003+
firstChild: Fiber,
2004+
committedLanes: Lanes,
2005+
) {
2006+
inProgressLanes = committedLanes;
2007+
inProgressRoot = root;
19822008
nextEffect = firstChild;
2009+
19832010
commitMutationEffects_begin(root);
2011+
2012+
inProgressLanes = null;
2013+
inProgressRoot = null;
19842014
}
19852015

19862016
function commitMutationEffects_begin(root: FiberRoot) {
@@ -2134,8 +2164,14 @@ export function commitLayoutEffects(
21342164
root: FiberRoot,
21352165
committedLanes: Lanes,
21362166
): void {
2167+
inProgressLanes = committedLanes;
2168+
inProgressRoot = root;
21372169
nextEffect = finishedWork;
2170+
21382171
commitLayoutEffects_begin(finishedWork, root, committedLanes);
2172+
2173+
inProgressLanes = null;
2174+
inProgressRoot = null;
21392175
}
21402176

21412177
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)