From bb844a03b2c1c1910baefeb2e24bc29d6095afd5 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Sat, 10 Dec 2016 02:24:49 -0800 Subject: [PATCH] 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. --- src/renderers/noop/ReactNoop.js | 8 +- .../shared/fiber/ReactFiberBeginWork.js | 2 +- .../shared/fiber/ReactFiberUpdateQueue.js | 91 +++++++++---------- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/renderers/noop/ReactNoop.js b/src/renderers/noop/ReactNoop.js index c3ca7bc633..8804b14f3e 100644 --- a/src/renderers/noop/ReactNoop.js +++ b/src/renderers/noop/ReactNoop.js @@ -286,10 +286,16 @@ var ReactNoop = { log( ' '.repeat(depth + 1) + 'QUEUED UPDATES' ); - const firstPendingUpdate = updateQueue.firstPendingUpdate; + let firstPendingUpdate; + if (updateQueue.lastProgressedUpdate) { + firstPendingUpdate = updateQueue.lastProgressedUpdate.next; + } else { + firstPendingUpdate = updateQueue.first; + } if (!firstPendingUpdate) { return; } + log( ' '.repeat(depth + 1) + '~', firstPendingUpdate && firstPendingUpdate.partialState, diff --git a/src/renderers/shared/fiber/ReactFiberBeginWork.js b/src/renderers/shared/fiber/ReactFiberBeginWork.js index 03b36473fd..f00bcca831 100644 --- a/src/renderers/shared/fiber/ReactFiberBeginWork.js +++ b/src/renderers/shared/fiber/ReactFiberBeginWork.js @@ -502,7 +502,7 @@ module.exports = function( ) // ) ); if (!hasNewProps) { - const hasUpdate = updateQueue && hasPendingUpdate(updateQueue); + const hasUpdate = updateQueue && hasPendingUpdate(updateQueue, priorityLevel); if (!hasUpdate && !hasContextChanged()) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } diff --git a/src/renderers/shared/fiber/ReactFiberUpdateQueue.js b/src/renderers/shared/fiber/ReactFiberUpdateQueue.js index 7083979136..e823abe8ca 100644 --- a/src/renderers/shared/fiber/ReactFiberUpdateQueue.js +++ b/src/renderers/shared/fiber/ReactFiberUpdateQueue.js @@ -38,17 +38,34 @@ type Update = { }; export type UpdateQueue = { - // Points to the first (oldest) update. + // Points to the first update. first: Update | null, - // Points to the first pending update. A pending update is one that is not - // part of the progressed work. This could be null even in a non-empty queue, - // when none of the updates are empty. - firstPendingUpdate: Update | null, - // Points to the last (newest) update. + lastProgressedUpdate: Update | null, + // Points to the last update. last: Update | null, }; -// Ensures that a fiber and its alternate have an update queue, creating a newText +function getFirstPendingUpdate(queue : UpdateQueue) { + if (queue.lastProgressedUpdate) { + return queue.lastProgressedUpdate.next; + } + return queue.first; +} + +function getFirstProgressedUpdate(queue : UpdateQueue) { + if (queue.lastProgressedUpdate) { + return queue.first; + } + return null; +} + +function hasPendingUpdate(queue : UpdateQueue, priorityLevel : PriorityLevel) : boolean { + // TODO: Check priority level + return Boolean(getFirstPendingUpdate(queue)); +} +exports.hasPendingUpdate = hasPendingUpdate; + +// Ensures that a fiber and its alternate have an update queue, creating a new // one if needed. Returns the new or existing queue. function ensureUpdateQueue(fiber : Fiber) : UpdateQueue { if (fiber.updateQueue) { @@ -57,7 +74,7 @@ function ensureUpdateQueue(fiber : Fiber) : UpdateQueue { } const queue = { first: null, - firstPendingUpdate: null, + lastProgressedUpdate: null, last: null, }; fiber.updateQueue = queue; @@ -75,15 +92,13 @@ function insertUpdateIntoQueue(queue : UpdateQueue, update : Update, priorityLev // TODO: Insert updates in order of priority. if (!queue.last) { // The queue is empty. - queue.first = queue.last = queue.firstPendingUpdate = update; + queue.first = queue.last = update; } else { // The queue is not empty. Append the update to the end. queue.last.next = update; queue.last = update; - - if (!queue.firstPendingUpdate) { - // This is the first pending update. Update the pointer. - queue.firstPendingUpdate = update; + if (queue.lastProgressedUpdate && !queue.lastProgressedUpdate.next) { + queue.lastProgressedUpdate.next = update; } } } @@ -121,32 +136,18 @@ function addReplaceUpdate( if (!queue.last) { // The queue is empty. - queue.first = queue.last = queue.firstPendingUpdate = replaceUpdate; + queue.first = queue.last = replaceUpdate; } else { // The queue is not empty. // Drop all existing pending updates. // TODO: Only drop updates with matching priority. - let lastMergedUpdate = null; - if (queue.firstPendingUpdate) { - let node = queue.first; - while (node && node.next !== queue.firstPendingUpdate) { - node = node.next; - } - lastMergedUpdate = node; - } else { - lastMergedUpdate = queue.last; - } - - if (lastMergedUpdate) { - // Append the new update to the end of the list. - // $FlowFixMe: Union bug (I think? Getting "object literal - This type incompatible with null") - lastMergedUpdate.next = replaceUpdate; - queue.firstPendingUpdate = replaceUpdate; + if (queue.lastProgressedUpdate) { + queue.lastProgressedUpdate.next = replaceUpdate; queue.last = replaceUpdate; } else { // Drop everything - queue.first = queue.firstPendingUpdate = queue.last = replaceUpdate; + queue.first = queue.last = replaceUpdate; } } @@ -168,7 +169,7 @@ exports.addForceUpdate = addForceUpdate; function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : PriorityLevel) : void { - if (queue.firstPendingUpdate && queue.last && !queue.last.callback) { + if (getFirstPendingUpdate(queue) && queue.last && !queue.last.callback) { // If pending updates already exist, and the last pending update does not // have a callback, we can add the new callback to that update. // TODO: Add an additional check to ensure the priority matches. @@ -188,17 +189,12 @@ function addCallback(queue : UpdateQueue, callback: Callback, priorityLevel : Pr } exports.addCallback = addCallback; -function hasPendingUpdate(queue : UpdateQueue) : boolean { - // TODO: Check priority level - return queue.firstPendingUpdate !== null; -} -exports.hasPendingUpdate = hasPendingUpdate; - function getPendingPriority(queue : UpdateQueue) : PriorityLevel { // Loop through the pending updates to recompute the pending priority. // TODO: Once updates are sorted, just read from the first pending update. let priorityLevel = NoWork; - let update = queue.firstPendingUpdate; + // Start with first pending update + let update = getFirstPendingUpdate(queue); while (update) { if (priorityLevel === NoWork || priorityLevel >= update.priorityLevel) { @@ -239,11 +235,12 @@ function beginUpdateQueue( let state = prevState; let dontMutatePrevState = true; - let update : Update | null = queue.first; let isEmpty = true; // TODO: Stop merging once we reach an update whose priority doesn't match. // Should this also apply to updates that were previous merged but bailed out? + let update : Update | null = queue.first; + let lastProgressedUpdate = null; while (update) { let partialState; if (update.isReplace) { @@ -270,12 +267,11 @@ function beginUpdateQueue( if (update.callback) { workInProgress.effectTag |= CallbackEffect; } + lastProgressedUpdate = update; update = update.next; } - // The next pending update is the one that we exited on in the loop above. - // Until priorities are implemented, this is always null. - queue.firstPendingUpdate = update; + queue.lastProgressedUpdate = lastProgressedUpdate; if (isEmpty) { // None of the updates contained state. Return the original state object. @@ -287,10 +283,11 @@ function beginUpdateQueue( exports.beginUpdateQueue = beginUpdateQueue; function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context : mixed) { + if (finishedWork.effectTag & CallbackEffect) { // Call the callbacks on all the non-pending updates. - let update = queue.first; - while (update && update !== queue.firstPendingUpdate) { + let update = getFirstProgressedUpdate(queue); + while (update && update !== getFirstPendingUpdate(queue)) { const callback = update.callback; if (typeof callback === 'function') { callback.call(context); @@ -300,8 +297,10 @@ function commitUpdateQueue(finishedWork : Fiber, queue : UpdateQueue, context : } // Drop all completed updates, leaving only the pending updates. - queue.first = queue.firstPendingUpdate; + queue.first = getFirstPendingUpdate(queue); if (!queue.first) { + queue.last = queue.lastProgressedUpdate = null; + // If the list is now empty, we can remove it from the finished work finishedWork.updateQueue = null; if (finishedWork.alternate) {