Repository navigation
perf: Batch transition commands into one main-looper message #10213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
MatiPl01
merged 15 commits into
main
from
@matipl01/android-css-batch-transition-commands
Aug 14, 2026
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7422677
Batch transition commands into one main-looper message
MatiPl01 7f83011
Do not run queued commands after invalidation
MatiPl01 10efa18
Recycle the command buffer even when a command throws
MatiPl01 6f5d046
Describe what the command buffer recycle actually prevents
MatiPl01 b2279ba
Pass the start command instead of splatting its fields
MatiPl01 8909c46
Stop a queued mount retry from starting after invalidation
MatiPl01 32864ea
Lend the spare command buffer out instead of sharing it
MatiPl01 6a67155
Drain queued transition commands in the pre-draw pass
MatiPl01 10a2d43
Name the pre-draw step for what it does
MatiPl01 e491dcb
Give the remove command its own executor
MatiPl01 7efd746
Match the reconciler callback name to what it runs
MatiPl01 351cb3b
Hold the command queue in its own class
MatiPl01 6f234ae
Drop the message import the queue took along
MatiPl01 126e096
Post the drain message before another drain can clear the flag
MatiPl01 1852b3d
Name the reconciler callback so it cannot shadow the listener
MatiPl01 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
...e-reanimated/android/src/main/java/com/swmansion/reanimated/css/MainThreadCommandQueue.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package com.swmansion.reanimated.css | ||
|
|
||
| import android.os.Handler | ||
| import android.os.Looper | ||
| import android.os.Message | ||
|
|
||
| /** | ||
| * Coalesces commands from any thread into one main-looper message per burst: a commit can | ||
| * produce a command per view on screen, and a message each floods the looper. The message is | ||
| * asynchronous so a traversal's sync barrier cannot defer it past the frame whose committed | ||
| * values the commands are meant to replace. [execute] runs on the main thread, from the posted | ||
| * message or from an explicit [drain]. | ||
| */ | ||
| internal class MainThreadCommandQueue<T : Any>( | ||
| private val execute: (T) -> Unit, | ||
| ) { | ||
| private val lock = Any() | ||
| private var pending = ArrayList<T>() | ||
|
|
||
| /** Null while a drain holds it, so the two buffers can never be the same list. */ | ||
| private var spare: ArrayList<T>? = ArrayList() | ||
| private var scheduled = false | ||
| private val mainHandler = Handler(Looper.getMainLooper()) | ||
| private val drainRunnable = Runnable { drain() } | ||
|
|
||
| fun enqueue(command: T) { | ||
| synchronized(lock) { | ||
| pending.add(command) | ||
| if (scheduled) return | ||
| scheduled = true | ||
| // Posted under the lock: a drain between deciding and posting would reset | ||
| // `scheduled`, and the next enqueue would post a second message for this burst. | ||
| val message = Message.obtain(mainHandler, drainRunnable) | ||
| message.isAsynchronous = true | ||
| mainHandler.sendMessage(message) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
| } | ||
|
|
||
| /** Runs every queued command now; a later message for the same burst finds nothing left. */ | ||
| fun drain() { | ||
| val batch: ArrayList<T> | ||
| synchronized(lock) { | ||
| batch = pending | ||
| pending = spare ?: ArrayList() | ||
| spare = null | ||
| scheduled = false | ||
| } | ||
| try { | ||
| for (command in batch) execute(command) | ||
| } finally { | ||
| batch.clear() | ||
| synchronized(lock) { spare = batch } | ||
| } | ||
| } | ||
|
|
||
| fun hasPending(): Boolean = synchronized(lock) { pending.isNotEmpty() } | ||
| } | ||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.