Store interleaved updates on separate queue until end of render#20615
Merged
acdlite merged 1 commit intofacebook:masterfrom Jan 22, 2021
Merged
Store interleaved updates on separate queue until end of render#20615acdlite merged 1 commit intofacebook:masterfrom
acdlite merged 1 commit intofacebook:masterfrom
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
An interleaved update is one that is scheduled while a render is already in progress, typically from a concurrent user input event.
We have to take care not to process these updates during the current render, because a multiple interleaved updates may have been scheduled across many components; to avoid tearing, we cannot render some of those updates without rendering all of them.
Old approach
What we currently do when we detect an interleaved update is assign a lane that is not part of the current render.
This has some unfortunate drawbacks. For example, we will eventually run out of lanes at a given priority level. When this happens, our last resort is to interrupt the current render and start over from scratch. If this happens enough, it can lead to starvation.
More concerning, there are a surprising number of places that must separately account for this case, often in subtle ways. The maintenance complexity has led to a number of tearing bugs.
New approach
I added a new field to the update queue,
interleaved. It's a linked list, just like thependingfield. When an interleaved update is scheduled, we add it to theinterleavedlist instead ofpending.Then we push the entire queue object onto a global array. When the current render exits, we iterate through the array of interleaved queues and transfer the
interleavedlist to thependinglist.So, until the current render has exited (whether due to a commit or an interruption), it's impossible to process an interleaved update, because they have not yet been enqueued.
In this new approach, we don't need to resort to clever lanes tricks to avoid inconsistencies. This should allow us to simplify a lot of the logic that's currently in ReactFiberWorkLoop and ReactFiberLane, especially
findUpdateLaneandgetNextLanes. All the logic for interleaved updates is isolated to one place.Rollout plan
This is a moderately risky change, so I'm going to land this only in the new fork.
I'll wait until after the effects refactor has landed.
In this first PR, I've implemented the new queueing behavior, but I haven't yet cleaned up any of the old logic we used to rely on to address the same problem. Cleaning that stuff up is the main motivation for this change, but it introduces additional risk. So I'll follow up with a separate PR, in case there's a bug and we need to bisect.