@samitouri / QOS-React / commits / bb844a03b2

Use lastProgressedUpdate pointer instead of firstPendingUpdate

We need to be able to access both, and since the list uses forward pointers, it makes more sense to point to the one that comes first. Otherwise to get the last progressed update you have to start at the beginning of the list.

Andrew Clark committed Dec 10, 2016 at 02:24 UTC bb844a03b2c1c1910baefeb2e24bc29d6095afd5
3 files changed +53 -48
src/renderers/noop/ReactNoop.js
+7 -1
@@ -286,10 +286,16 @@ var ReactNoop = {
286 log(
287 ' '.repeat(depth + 1) + 'QUEUED UPDATES'
288 );
289 - const firstPendingUpdate = updateQueue.firstPendingUpdate;
289 + let firstPendingUpdate;
290 + if (updateQueue.lastProgressedUpdate) {
291 + firstPendingUpdate = updateQueue.lastProgressedUpdate.next;
292 + } else {
293 + firstPendingUpdate = updateQueue.first;
294 + }
295 if (!firstPendingUpdate) {
296 return;
297 }
298 +
299 log(
300 ' '.repeat(depth + 1) + '~',
301 firstPendingUpdate && firstPendingUpdate.partialState,
src/renderers/shared/fiber/ReactFiberBeginWork.js
+1 -1
@@ -502,7 +502,7 @@ module.exports = function<T, P, I, TI, C, CX>(
502 ) // )
503 );
504 if (!hasNewProps) {
505 - const hasUpdate = updateQueue && hasPendingUpdate(updateQueue);
505 + const hasUpdate = updateQueue && hasPendingUpdate(updateQueue, priorityLevel);
506 if (!hasUpdate && !hasContextChanged()) {
507 return bailoutOnAlreadyFinishedWork(current, workInProgress);
508 }
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+45 -46
@@ -38,17 +38,34 @@ type Update = {
38 };
39
40 export type UpdateQueue = {
41 - // Points to the first (oldest) update.
41 + // Points to the first update.
42 first: Update | null,
43 - // Points to the first pending update. A pending update is one that is not
44 - // part of the progressed work. This could be null even in a non-empty queue,
45 - // when none of the updates are empty.
46 - firstPendingUpdate: Update | null,
47 - // Points to the last (newest) update.
43 + lastProgressedUpdate: Update | null,
44 + // Points to the last update.
45 last: Update | null,
46 };
47
51 -// Ensures that a fiber and its alternate have an update queue, creating a newText
48 +function getFirstPendingUpdate(queue : UpdateQueue) {
49 + if (queue.lastProgressedUpdate) {
50 + return queue.lastProgressedUpdate.next;
51 + }
52 + return queue.first;
53 +}
54 +
55 +function getFirstProgressedUpdate(queue : UpdateQueue) {
56 + if (queue.lastProgressedUpdate) {
57 + return queue.first;
58 + }
59 + return null;
60 +}
61 +
62 +function hasPendingUpdate(queue : UpdateQueue, priorityLevel : PriorityLevel) : boolean {
63 + // TODO: Check priority level
64 + return Boolean(getFirstPendingUpdate(queue));
65 +}
66 +exports.hasPendingUpdate = hasPendingUpdate;
67 +
68 +// Ensures that a fiber and its alternate have an update queue, creating a new
69 // one if needed. Returns the new or existing queue.
70 function ensureUpdateQueue(fiber : Fiber) : UpdateQueue {
71 if (fiber.updateQueue) {
@@ -57,7 +74,7 @@ function ensureUpdateQueue(fiber : Fiber) : UpdateQueue {
74 }
75 const queue = {
76 first: null,
60 - firstPendingUpdate: null,
77 + lastProgressedUpdate: null,
78 last: null,
79 };
80 fiber.updateQueue = queue;
@@ -75,15 +92,13 @@ function insertUpdateIntoQueue(queue : UpdateQueue, update : Update, priorityLev
92 // TODO: Insert updates in order of priority.
93 if (!queue.last) {
94 // The queue is empty.
78 - queue.first = queue.last = queue.firstPendingUpdate = update;
95 + queue.first = queue.last = update;
96 } else {
97 // The queue is not empty. Append the update to the end.
98 queue.last.next = update;
99 queue.last = update;
83 -
84 - if (!queue.firstPendingUpdate) {
85 - // This is the first pending update. Update the pointer.
86 - queue.firstPendingUpdate = update;
100 + if (queue.lastProgressedUpdate && !queue.lastProgressedUpdate.next) {
101 + queue.lastProgressedUpdate.next = update;
102 }
103 }
104 }
@@ -121,32 +136,18 @@ function addReplaceUpdate(
136
137 if (!queue.last) {
138 // The queue is empty.
124 - queue.first = queue.last = queue.firstPendingUpdate = replaceUpdate;
139 + queue.first = queue.last = replaceUpdate;
140 } else {
141 // The queue is not empty.
142
143 // Drop all existing pending updates.
144 // TODO: Only drop updates with matching priority.
130 - let lastMergedUpdate = null;
131 - if (queue.firstPendingUpdate) {
132 - let node = queue.first;
133 - while (node && node.next !== queue.firstPendingUpdate) {
134 - node = node.next;
135 - }
136 - lastMergedUpdate = node;
137 - } else {
138 - lastMergedUpdate = queue.last;
139 - }
140 -
141 - if (lastMergedUpdate) {
142 - // Append the new update to the end of the list.
143 - // $FlowFixMe: Union bug (I think? Getting "object literal - This type incompatible with null")
144 - lastMergedUpdate.next = replaceUpdate;
145 - queue.firstPendingUpdate = replaceUpdate;
145 + if (queue.lastProgressedUpdate) {
146 + queue.lastProgressedUpdate.next = replaceUpdate;
147 queue.last = replaceUpdate;
148 } else {
149 // Drop everything
149 - queue.first = queue.firstPendingUpdate = queue.last = replaceUpdate;
150 + queue.first = queue.last = replaceUpdate;
151 }
152 }
153
@@ -168,7 +169,7 @@ exports.addForceUpdate = addForceUpdate;
169
170
171 function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : PriorityLevel) : void {
171 - if (queue.firstPendingUpdate && queue.last && !queue.last.callback) {
172 + if (getFirstPendingUpdate(queue) && queue.last && !queue.last.callback) {
173 // If pending updates already exist, and the last pending update does not
174 // have a callback, we can add the new callback to that update.
175 // TODO: Add an additional check to ensure the priority matches.
@@ -188,17 +189,12 @@ function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : Pr
189 }
190 exports.addCallback = addCallback;
191
191 -function hasPendingUpdate(queue : UpdateQueue) : boolean {
192 - // TODO: Check priority level
193 - return queue.firstPendingUpdate !== null;
194 -}
195 -exports.hasPendingUpdate = hasPendingUpdate;
196 -
192 function getPendingPriority(queue : UpdateQueue) : PriorityLevel {
193 // Loop through the pending updates to recompute the pending priority.
194 // TODO: Once updates are sorted, just read from the first pending update.
195 let priorityLevel = NoWork;
201 - let update = queue.firstPendingUpdate;
196 + // Start with first pending update
197 + let update = getFirstPendingUpdate(queue);
198 while (update) {
199 if (priorityLevel === NoWork ||
200 priorityLevel >= update.priorityLevel) {
@@ -239,11 +235,12 @@ function beginUpdateQueue(
235
236 let state = prevState;
237 let dontMutatePrevState = true;
242 - let update : Update | null = queue.first;
238 let isEmpty = true;
239
240 // TODO: Stop merging once we reach an update whose priority doesn't match.
241 // Should this also apply to updates that were previous merged but bailed out?
242 + let update : Update | null = queue.first;
243 + let lastProgressedUpdate = null;
244 while (update) {
245 let partialState;
246 if (update.isReplace) {
@@ -270,12 +267,11 @@ function beginUpdateQueue(
267 if (update.callback) {
268 workInProgress.effectTag |= CallbackEffect;
269 }
270 + lastProgressedUpdate = update;
271 update = update.next;
272 }
273
276 - // The next pending update is the one that we exited on in the loop above.
277 - // Until priorities are implemented, this is always null.
278 - queue.firstPendingUpdate = update;
274 + queue.lastProgressedUpdate = lastProgressedUpdate;
275
276 if (isEmpty) {
277 // None of the updates contained state. Return the original state object.
@@ -287,10 +283,11 @@ function beginUpdateQueue(
283 exports.beginUpdateQueue = beginUpdateQueue;
284
285 function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context : mixed) {
286 +
287 if (finishedWork.effectTag & CallbackEffect) {
288 // Call the callbacks on all the non-pending updates.
292 - let update = queue.first;
293 - while (update && update !== queue.firstPendingUpdate) {
289 + let update = getFirstProgressedUpdate(queue);
290 + while (update && update !== getFirstPendingUpdate(queue)) {
291 const callback = update.callback;
292 if (typeof callback === 'function') {
293 callback.call(context);
@@ -300,8 +297,10 @@ function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context :
297 }
298
299 // Drop all completed updates, leaving only the pending updates.
303 - queue.first = queue.firstPendingUpdate;
300 + queue.first = getFirstPendingUpdate(queue);
301 if (!queue.first) {
302 + queue.last = queue.lastProgressedUpdate = null;
303 +
304 // If the list is now empty, we can remove it from the finished work
305 finishedWork.updateQueue = null;
306 if (finishedWork.alternate) {