add transition tracing transitions stack (#24321)
Added a transitions stack for to keep track of which transitions are still happening for the current boundary. * On the root, we will get all transitions that have been initiated for the corresponding lanes. * Whenever we encounter a suspended boundary, we will add all transitions on the stack onto the boundary * Whenever we encounter a boundary that just unsuspended, we will add all transitions on the boundary onto the stack A transition will be considered complete when there are no boundaries that have the associated transition
Luna Ruan committed
Apr 8, 2022 at 19:48 UTC
e0160d50c5a492a925db6ab3f8478e118336c722
8 files changed
+162
-88
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+10
-7
@@ -661,7 +661,7 @@ function updateOffscreenComponent(
661
// push the cache pool even though we're going to bail out
662
// because otherwise there'd be a context mismatch
663
if (current !== null) {
664
- pushTransition(workInProgress, null);
664
+ pushTransition(workInProgress, null, null);
665
}
666
}
667
pushRenderLanes(workInProgress, renderLanes);
@@ -695,7 +695,7 @@ function updateOffscreenComponent(
695
// push the cache pool even though we're going to bail out
696
// because otherwise there'd be a context mismatch
697
if (current !== null) {
698
- pushTransition(workInProgress, null);
698
+ pushTransition(workInProgress, null, null);
699
}
700
}
701
@@ -733,7 +733,9 @@ function updateOffscreenComponent(
733
// using the same cache. Unless the parent changed, since that means
734
// there was a refresh.
735
const prevCachePool = prevState !== null ? prevState.cachePool : null;
736
- pushTransition(workInProgress, prevCachePool);
736
+ // TODO: Consider if and how Offscreen pre-rendering should
737
+ // be attributed to the transition that spawned it
738
+ pushTransition(workInProgress, prevCachePool, null);
739
}
740
741
pushRenderLanes(workInProgress, subtreeRenderLanes);
@@ -751,7 +753,7 @@ function updateOffscreenComponent(
753
// using the same cache. Unless the parent changed, since that means
754
// there was a refresh.
755
const prevCachePool = prevState.cachePool;
754
- pushTransition(workInProgress, prevCachePool);
756
+ pushTransition(workInProgress, prevCachePool, null);
757
}
758
759
// Since we're not hidden anymore, reset the state
@@ -767,7 +769,7 @@ function updateOffscreenComponent(
769
// using the same cache. Unless the parent changed, since that means
770
// there was a refresh.
771
if (current !== null) {
770
- pushTransition(workInProgress, null);
772
+ pushTransition(workInProgress, null, null);
773
}
774
}
775
}
@@ -1330,10 +1332,10 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1332
1333
const nextState: RootState = workInProgress.memoizedState;
1334
const root: FiberRoot = workInProgress.stateNode;
1335
+ pushRootTransition(workInProgress, root, renderLanes);
1336
1337
if (enableCache) {
1338
const nextCache: Cache = nextState.cache;
1336
- pushRootTransition(root);
1339
pushCacheProvider(workInProgress, nextCache);
1340
if (nextCache !== prevState.cache) {
1341
// The root cache refreshed.
@@ -3572,10 +3574,11 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
3574
case HostRoot:
3575
pushHostRootContext(workInProgress);
3576
const root: FiberRoot = workInProgress.stateNode;
3577
+ pushRootTransition(workInProgress, root, renderLanes);
3578
+
3579
if (enableCache) {
3580
const cache: Cache = current.memoizedState.cache;
3581
pushCacheProvider(workInProgress, cache);
3578
- pushRootTransition(root);
3582
}
3583
if (enableTransitionTracing) {
3584
workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+10
-7
@@ -661,7 +661,7 @@ function updateOffscreenComponent(
661
// push the cache pool even though we're going to bail out
662
// because otherwise there'd be a context mismatch
663
if (current !== null) {
664
- pushTransition(workInProgress, null);
664
+ pushTransition(workInProgress, null, null);
665
}
666
}
667
pushRenderLanes(workInProgress, renderLanes);
@@ -695,7 +695,7 @@ function updateOffscreenComponent(
695
// push the cache pool even though we're going to bail out
696
// because otherwise there'd be a context mismatch
697
if (current !== null) {
698
- pushTransition(workInProgress, null);
698
+ pushTransition(workInProgress, null, null);
699
}
700
}
701
@@ -733,7 +733,9 @@ function updateOffscreenComponent(
733
// using the same cache. Unless the parent changed, since that means
734
// there was a refresh.
735
const prevCachePool = prevState !== null ? prevState.cachePool : null;
736
- pushTransition(workInProgress, prevCachePool);
736
+ // TODO: Consider if and how Offscreen pre-rendering should
737
+ // be attributed to the transition that spawned it
738
+ pushTransition(workInProgress, prevCachePool, null);
739
}
740
741
pushRenderLanes(workInProgress, subtreeRenderLanes);
@@ -751,7 +753,7 @@ function updateOffscreenComponent(
753
// using the same cache. Unless the parent changed, since that means
754
// there was a refresh.
755
const prevCachePool = prevState.cachePool;
754
- pushTransition(workInProgress, prevCachePool);
756
+ pushTransition(workInProgress, prevCachePool, null);
757
}
758
759
// Since we're not hidden anymore, reset the state
@@ -767,7 +769,7 @@ function updateOffscreenComponent(
769
// using the same cache. Unless the parent changed, since that means
770
// there was a refresh.
771
if (current !== null) {
770
- pushTransition(workInProgress, null);
772
+ pushTransition(workInProgress, null, null);
773
}
774
}
775
}
@@ -1330,10 +1332,10 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1332
1333
const nextState: RootState = workInProgress.memoizedState;
1334
const root: FiberRoot = workInProgress.stateNode;
1335
+ pushRootTransition(workInProgress, root, renderLanes);
1336
1337
if (enableCache) {
1338
const nextCache: Cache = nextState.cache;
1336
- pushRootTransition(root);
1339
pushCacheProvider(workInProgress, nextCache);
1340
if (nextCache !== prevState.cache) {
1341
// The root cache refreshed.
@@ -3572,10 +3574,11 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
3574
case HostRoot:
3575
pushHostRootContext(workInProgress);
3576
const root: FiberRoot = workInProgress.stateNode;
3577
+ pushRootTransition(workInProgress, root, renderLanes);
3578
+
3579
if (enableCache) {
3580
const cache: Cache = current.memoizedState.cache;
3581
pushCacheProvider(workInProgress, cache);
3578
- pushRootTransition(root);
3582
}
3583
if (enableTransitionTracing) {
3584
workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+3
-5
@@ -875,8 +875,6 @@ function completeWork(
875
}
876
877
if (enableCache) {
878
- popRootTransition(fiberRoot, renderLanes);
879
-
878
let previousCache: Cache | null = null;
879
if (current !== null) {
880
previousCache = current.memoizedState.cache;
@@ -888,6 +886,7 @@ function completeWork(
886
}
887
popCacheProvider(workInProgress, cache);
888
}
889
+ popRootTransition(workInProgress, fiberRoot, renderLanes);
890
popHostContainer(workInProgress);
891
popTopLevelLegacyContextObject(workInProgress);
892
resetMutableSourceWorkInProgressVersions();
@@ -1593,11 +1592,10 @@ function completeWork(
1592
// Run passive effects to retain/release the cache.
1593
workInProgress.flags |= Passive;
1594
}
1596
- if (current !== null) {
1597
- popTransition(workInProgress);
1598
- }
1595
}
1596
1597
+ popTransition(workInProgress, current);
1598
+
1599
return null;
1600
}
1601
case CacheComponent: {
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+3
-5
@@ -875,8 +875,6 @@ function completeWork(
875
}
876
877
if (enableCache) {
878
- popRootTransition(fiberRoot, renderLanes);
879
-
878
let previousCache: Cache | null = null;
879
if (current !== null) {
880
previousCache = current.memoizedState.cache;
@@ -888,6 +886,7 @@ function completeWork(
886
}
887
popCacheProvider(workInProgress, cache);
888
}
889
+ popRootTransition(workInProgress, fiberRoot, renderLanes);
890
popHostContainer(workInProgress);
891
popTopLevelLegacyContextObject(workInProgress);
892
resetMutableSourceWorkInProgressVersions();
@@ -1593,11 +1592,10 @@ function completeWork(
1592
// Run passive effects to retain/release the cache.
1593
workInProgress.flags |= Passive;
1594
}
1596
- if (current !== null) {
1597
- popTransition(workInProgress);
1598
- }
1595
}
1596
1597
+ popTransition(workInProgress, current);
1598
+
1599
return null;
1600
}
1601
case CacheComponent: {
packages/react-reconciler/src/ReactFiberTransition.new.js
+62
-15
@@ -10,11 +10,15 @@ import type {FiberRoot} from './ReactInternalTypes';
10
import type {Lanes} from './ReactFiberLane.new';
11
import type {StackCursor} from './ReactFiberStack.new';
12
import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new';
13
+import type {Transition} from './ReactFiberTracingMarkerComponent.new';
14
14
-import {enableCache} from 'shared/ReactFeatureFlags';
15
+import {enableCache, enableTransitionTracing} from 'shared/ReactFeatureFlags';
16
import {isPrimaryRenderer} from './ReactFiberHostConfig';
17
import {createCursor, push, pop} from './ReactFiberStack.new';
17
-import {getWorkInProgressRoot} from './ReactFiberWorkLoop.new';
18
+import {
19
+ getWorkInProgressRoot,
20
+ getWorkInProgressTransitions,
21
+} from './ReactFiberWorkLoop.new';
22
import {
23
createCache,
24
retainCache,
@@ -25,6 +29,15 @@ import {
29
// used during the previous render by placing it here, on the stack.
30
const resumedCache: StackCursor<Cache | null> = createCursor(null);
31
32
+// During the render/synchronous commit phase, we don't actually process the
33
+// transitions. Therefore, we want to lazily combine transitions. Instead of
34
+// comparing the arrays of transitions when we combine them and storing them
35
+// and filtering out the duplicates, we will instead store the unprocessed transitions
36
+// in an array and actually filter them in the passive phase.
37
+const transitionStack: StackCursor<Array<Transition> | null> = createCursor(
38
+ null,
39
+);
40
+
41
function peekCacheFromPool(): Cache | null {
42
if (!enableCache) {
43
return (null: any);
@@ -75,25 +88,31 @@ export function requestCacheFromPool(renderLanes: Lanes): Cache {
88
return freshCache;
89
}
90
78
-export function pushRootTransition(root: FiberRoot) {
79
- if (enableCache) {
80
- return;
91
+export function pushRootTransition(
92
+ workInProgress: Fiber,
93
+ root: FiberRoot,
94
+ renderLanes: Lanes,
95
+) {
96
+ if (enableTransitionTracing) {
97
+ const rootTransitions = getWorkInProgressTransitions();
98
+ push(transitionStack, rootTransitions, workInProgress);
99
}
82
- // Note: This function currently does nothing but I'll leave it here for
83
- // code organization purposes in case that changes.
100
}
101
86
-export function popRootTransition(root: FiberRoot, renderLanes: Lanes) {
87
- if (enableCache) {
88
- return;
102
+export function popRootTransition(
103
+ workInProgress: Fiber,
104
+ root: FiberRoot,
105
+ renderLanes: Lanes,
106
+) {
107
+ if (enableTransitionTracing) {
108
+ pop(transitionStack, workInProgress);
109
}
90
- // Note: This function currently does nothing but I'll leave it here for
91
- // code organization purposes in case that changes.
110
}
111
112
export function pushTransition(
113
offscreenWorkInProgress: Fiber,
114
prevCachePool: SpawnedCachePool | null,
115
+ newTransitions: Array<Transition> | null,
116
): void {
117
if (enableCache) {
118
if (prevCachePool === null) {
@@ -102,12 +121,40 @@ export function pushTransition(
121
push(resumedCache, prevCachePool.pool, offscreenWorkInProgress);
122
}
123
}
124
+
125
+ if (enableTransitionTracing) {
126
+ if (transitionStack.current === null) {
127
+ push(transitionStack, newTransitions, offscreenWorkInProgress);
128
+ } else if (newTransitions === null) {
129
+ push(transitionStack, transitionStack.current, offscreenWorkInProgress);
130
+ } else {
131
+ push(
132
+ transitionStack,
133
+ transitionStack.current.concat(newTransitions),
134
+ offscreenWorkInProgress,
135
+ );
136
+ }
137
+ }
138
}
139
107
-export function popTransition(workInProgress: Fiber) {
108
- if (enableCache) {
109
- pop(resumedCache, workInProgress);
140
+export function popTransition(workInProgress: Fiber, current: Fiber | null) {
141
+ if (current !== null) {
142
+ if (enableCache) {
143
+ pop(resumedCache, workInProgress);
144
+ }
145
+
146
+ if (enableTransitionTracing) {
147
+ pop(transitionStack, workInProgress);
148
+ }
149
+ }
150
+}
151
+
152
+export function getSuspendedTransitions(): Array<Transition> | null {
153
+ if (!enableTransitionTracing) {
154
+ return null;
155
}
156
+
157
+ return transitionStack.current;
158
}
159
160
export function getSuspendedCache(): SpawnedCachePool | null {
packages/react-reconciler/src/ReactFiberTransition.old.js
+62
-15
@@ -10,11 +10,15 @@ import type {FiberRoot} from './ReactInternalTypes';
10
import type {Lanes} from './ReactFiberLane.old';
11
import type {StackCursor} from './ReactFiberStack.old';
12
import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old';
13
+import type {Transition} from './ReactFiberTracingMarkerComponent.old';
14
14
-import {enableCache} from 'shared/ReactFeatureFlags';
15
+import {enableCache, enableTransitionTracing} from 'shared/ReactFeatureFlags';
16
import {isPrimaryRenderer} from './ReactFiberHostConfig';
17
import {createCursor, push, pop} from './ReactFiberStack.old';
17
-import {getWorkInProgressRoot} from './ReactFiberWorkLoop.old';
18
+import {
19
+ getWorkInProgressRoot,
20
+ getWorkInProgressTransitions,
21
+} from './ReactFiberWorkLoop.old';
22
import {
23
createCache,
24
retainCache,
@@ -25,6 +29,15 @@ import {
29
// used during the previous render by placing it here, on the stack.
30
const resumedCache: StackCursor<Cache | null> = createCursor(null);
31
32
+// During the render/synchronous commit phase, we don't actually process the
33
+// transitions. Therefore, we want to lazily combine transitions. Instead of
34
+// comparing the arrays of transitions when we combine them and storing them
35
+// and filtering out the duplicates, we will instead store the unprocessed transitions
36
+// in an array and actually filter them in the passive phase.
37
+const transitionStack: StackCursor<Array<Transition> | null> = createCursor(
38
+ null,
39
+);
40
+
41
function peekCacheFromPool(): Cache | null {
42
if (!enableCache) {
43
return (null: any);
@@ -75,25 +88,31 @@ export function requestCacheFromPool(renderLanes: Lanes): Cache {
88
return freshCache;
89
}
90
78
-export function pushRootTransition(root: FiberRoot) {
79
- if (enableCache) {
80
- return;
91
+export function pushRootTransition(
92
+ workInProgress: Fiber,
93
+ root: FiberRoot,
94
+ renderLanes: Lanes,
95
+) {
96
+ if (enableTransitionTracing) {
97
+ const rootTransitions = getWorkInProgressTransitions();
98
+ push(transitionStack, rootTransitions, workInProgress);
99
}
82
- // Note: This function currently does nothing but I'll leave it here for
83
- // code organization purposes in case that changes.
100
}
101
86
-export function popRootTransition(root: FiberRoot, renderLanes: Lanes) {
87
- if (enableCache) {
88
- return;
102
+export function popRootTransition(
103
+ workInProgress: Fiber,
104
+ root: FiberRoot,
105
+ renderLanes: Lanes,
106
+) {
107
+ if (enableTransitionTracing) {
108
+ pop(transitionStack, workInProgress);
109
}
90
- // Note: This function currently does nothing but I'll leave it here for
91
- // code organization purposes in case that changes.
110
}
111
112
export function pushTransition(
113
offscreenWorkInProgress: Fiber,
114
prevCachePool: SpawnedCachePool | null,
115
+ newTransitions: Array<Transition> | null,
116
): void {
117
if (enableCache) {
118
if (prevCachePool === null) {
@@ -102,12 +121,40 @@ export function pushTransition(
121
push(resumedCache, prevCachePool.pool, offscreenWorkInProgress);
122
}
123
}
124
+
125
+ if (enableTransitionTracing) {
126
+ if (transitionStack.current === null) {
127
+ push(transitionStack, newTransitions, offscreenWorkInProgress);
128
+ } else if (newTransitions === null) {
129
+ push(transitionStack, transitionStack.current, offscreenWorkInProgress);
130
+ } else {
131
+ push(
132
+ transitionStack,
133
+ transitionStack.current.concat(newTransitions),
134
+ offscreenWorkInProgress,
135
+ );
136
+ }
137
+ }
138
}
139
107
-export function popTransition(workInProgress: Fiber) {
108
- if (enableCache) {
109
- pop(resumedCache, workInProgress);
140
+export function popTransition(workInProgress: Fiber, current: Fiber | null) {
141
+ if (current !== null) {
142
+ if (enableCache) {
143
+ pop(resumedCache, workInProgress);
144
+ }
145
+
146
+ if (enableTransitionTracing) {
147
+ pop(transitionStack, workInProgress);
148
+ }
149
+ }
150
+}
151
+
152
+export function getSuspendedTransitions(): Array<Transition> | null {
153
+ if (!enableTransitionTracing) {
154
+ return null;
155
}
156
+
157
+ return transitionStack.current;
158
}
159
160
export function getSuspendedCache(): SpawnedCachePool | null {
packages/react-reconciler/src/ReactFiberUnwindWork.new.js
+6
-17
@@ -79,13 +79,12 @@ function unwindWork(
79
return null;
80
}
81
case HostRoot: {
82
+ const root: FiberRoot = workInProgress.stateNode;
83
if (enableCache) {
83
- const root: FiberRoot = workInProgress.stateNode;
84
- popRootTransition(root, renderLanes);
85
-
84
const cache: Cache = workInProgress.memoizedState.cache;
85
popCacheProvider(workInProgress, cache);
86
}
87
+ popRootTransition(workInProgress, root, renderLanes);
88
popHostContainer(workInProgress);
89
popTopLevelLegacyContextObject(workInProgress);
90
resetMutableSourceWorkInProgressVersions();
@@ -153,11 +152,7 @@ function unwindWork(
152
case OffscreenComponent:
153
case LegacyHiddenComponent:
154
popRenderLanes(workInProgress);
156
- if (enableCache) {
157
- if (current !== null) {
158
- popTransition(workInProgress);
159
- }
160
- }
155
+ popTransition(workInProgress, current);
156
return null;
157
case CacheComponent:
158
if (enableCache) {
@@ -189,13 +184,12 @@ function unwindInterruptedWork(
184
break;
185
}
186
case HostRoot: {
187
+ const root: FiberRoot = interruptedWork.stateNode;
188
if (enableCache) {
193
- const root: FiberRoot = interruptedWork.stateNode;
194
- popRootTransition(root, renderLanes);
195
-
189
const cache: Cache = interruptedWork.memoizedState.cache;
190
popCacheProvider(interruptedWork, cache);
191
}
192
+ popRootTransition(interruptedWork, root, renderLanes);
193
popHostContainer(interruptedWork);
194
popTopLevelLegacyContextObject(interruptedWork);
195
resetMutableSourceWorkInProgressVersions();
@@ -221,12 +215,7 @@ function unwindInterruptedWork(
215
case OffscreenComponent:
216
case LegacyHiddenComponent:
217
popRenderLanes(interruptedWork);
224
- if (enableCache) {
225
- if (current !== null) {
226
- popTransition(interruptedWork);
227
- }
228
- }
229
-
218
+ popTransition(interruptedWork, current);
219
break;
220
case CacheComponent:
221
if (enableCache) {
packages/react-reconciler/src/ReactFiberUnwindWork.old.js
+6
-17
@@ -79,13 +79,12 @@ function unwindWork(
79
return null;
80
}
81
case HostRoot: {
82
+ const root: FiberRoot = workInProgress.stateNode;
83
if (enableCache) {
83
- const root: FiberRoot = workInProgress.stateNode;
84
- popRootTransition(root, renderLanes);
85
-
84
const cache: Cache = workInProgress.memoizedState.cache;
85
popCacheProvider(workInProgress, cache);
86
}
87
+ popRootTransition(workInProgress, root, renderLanes);
88
popHostContainer(workInProgress);
89
popTopLevelLegacyContextObject(workInProgress);
90
resetMutableSourceWorkInProgressVersions();
@@ -153,11 +152,7 @@ function unwindWork(
152
case OffscreenComponent:
153
case LegacyHiddenComponent:
154
popRenderLanes(workInProgress);
156
- if (enableCache) {
157
- if (current !== null) {
158
- popTransition(workInProgress);
159
- }
160
- }
155
+ popTransition(workInProgress, current);
156
return null;
157
case CacheComponent:
158
if (enableCache) {
@@ -189,13 +184,12 @@ function unwindInterruptedWork(
184
break;
185
}
186
case HostRoot: {
187
+ const root: FiberRoot = interruptedWork.stateNode;
188
if (enableCache) {
193
- const root: FiberRoot = interruptedWork.stateNode;
194
- popRootTransition(root, renderLanes);
195
-
189
const cache: Cache = interruptedWork.memoizedState.cache;
190
popCacheProvider(interruptedWork, cache);
191
}
192
+ popRootTransition(interruptedWork, root, renderLanes);
193
popHostContainer(interruptedWork);
194
popTopLevelLegacyContextObject(interruptedWork);
195
resetMutableSourceWorkInProgressVersions();
@@ -221,12 +215,7 @@ function unwindInterruptedWork(
215
case OffscreenComponent:
216
case LegacyHiddenComponent:
217
popRenderLanes(interruptedWork);
224
- if (enableCache) {
225
- if (current !== null) {
226
- popTransition(interruptedWork);
227
- }
228
- }
229
-
218
+ popTransition(interruptedWork, current);
219
break;
220
case CacheComponent:
221
if (enableCache) {