Skip to content

Commit ae56603

Browse files
Bartlomiej Bloniarzmeta-codesync[bot]
authored andcommitted
Wire the Android pull model in C++ behind the feature flag (#57579)
Summary: Pull Request resolved: #57579 Makes `enableMountingCoordinatorPullModelAndroid` functional. With the flag on, the commit thread only signals transaction availability and the UI thread pulls and applies at mount time (matching iOS/macOS); with the flag off (default), behavior is byte-for-byte identical. - `schedulerShouldRenderTransactions`: notifies via JNI (`FabricMountingManager::onTransactionAvailable`) instead of pulling and building the batch. - `schedulerDidFinishTransaction`: no-op under the pull model. - `FabricUIManagerBinding::pullAndExecuteTransaction` (new JNI method): pulls the surface's transaction on the UI thread and runs `executeMount`. - `FabricMountingManager::executeMount`: gains a `synchronous` mode that executes the batch directly on the UI thread. - The accumulation sites remain gated on `enableAccumulatedUpdatesInRawPropsAndroid`; the pull model requires that flag to be co-enabled, since a pull may collapse several commits into one diff and therefore needs complete accumulated rawProps. ## Changelog: [Android] [Added] - Wire the pull-model mounting path in C++ behind `enableMountingCoordinatorPullModelAndroid` Reviewed By: christophpurrer Differential Revision: D112309053
1 parent a919d7d commit ae56603

8 files changed

Lines changed: 122 additions & 22 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,8 @@ private void scheduleMountItem(
936936
long layoutEndTime,
937937
long finishTransactionStartTime,
938938
long finishTransactionEndTime,
939-
int affectedLayoutNodesCount) {
939+
int affectedLayoutNodesCount,
940+
boolean synchronous) {
940941
// When Binding.cpp calls scheduleMountItems during a commit phase, it always calls with
941942
// a BatchMountItem. No other sites call into this with a BatchMountItem, and Binding.cpp only
942943
// calls scheduleMountItems with a BatchMountItem.
@@ -950,8 +951,9 @@ private void scheduleMountItem(
950951
} else {
951952
shouldSchedule = mountItem != null;
952953
}
953-
// In case of sync rendering, this could be called on the UI thread. Otherwise,
954-
// it should ~always be called on the JS thread.
954+
// In the push model, this is ~always called on the JS thread, or on the UI
955+
// thread in case of sync rendering.
956+
// In the pull model, this is always called on the UI thread, at pull time.
955957
for (UIManagerListener listener : mListeners) {
956958
listener.didScheduleMountItems(this);
957959
}
@@ -966,16 +968,22 @@ private void scheduleMountItem(
966968

967969
if (shouldSchedule) {
968970
Assertions.assertNotNull(mountItem, "MountItem is null");
969-
mMountItemDispatcher.addMountItem(mountItem);
970-
if (UiThreadUtil.isOnUiThread()) {
971-
Runnable runnable =
972-
new GuardedRunnable(mReactApplicationContext) {
973-
@Override
974-
public void runGuarded() {
975-
mMountItemDispatcher.tryDispatchMountItems();
976-
}
977-
};
978-
runnable.run();
971+
if (synchronous) {
972+
// Pull model: we are already on the UI thread, inside the dispatcher's loop executing
973+
// a PullTransactionMountItem. We don't schedule the item, we execute it directly.
974+
mountItem.execute(mMountingManager);
975+
} else {
976+
mMountItemDispatcher.addMountItem(mountItem);
977+
if (UiThreadUtil.isOnUiThread()) {
978+
Runnable runnable =
979+
new GuardedRunnable(mReactApplicationContext) {
980+
@Override
981+
public void runGuarded() {
982+
mMountItemDispatcher.tryDispatchMountItems();
983+
}
984+
};
985+
runnable.run();
986+
}
979987
}
980988
}
981989

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ void FabricMountingManager::onSurfaceStop(SurfaceId surfaceId) {
5252
allocatedViewRegistry_.erase(surfaceId);
5353
}
5454

55+
void FabricMountingManager::onTransactionAvailable(SurfaceId surfaceId) {
56+
static auto onTransactionAvailable =
57+
JFabricUIManager::javaClassStatic()->getMethod<void(jint)>(
58+
"onTransactionAvailable");
59+
onTransactionAvailable(javaUIManager_, surfaceId);
60+
}
61+
5562
bool FabricMountingManager::isViewAllocated(SurfaceId surfaceId, Tag tag) {
5663
std::lock_guard lock(allocatedViewsMutex_);
5764
auto it = allocatedViewRegistry_.find(surfaceId);
@@ -568,7 +575,8 @@ inline void writeUpdateOverflowInsetMountItem(
568575
} // namespace
569576

570577
void FabricMountingManager::executeMount(
571-
const MountingTransaction& transaction) {
578+
const MountingTransaction& transaction,
579+
bool synchronous) {
572580
TraceSection section("FabricMountingManager::executeMount");
573581

574582
std::scoped_lock lock(commitMutex_);
@@ -830,7 +838,8 @@ void FabricMountingManager::executeMount(
830838
jlong,
831839
jlong,
832840
jlong,
833-
jint)>("scheduleMountItem");
841+
jint,
842+
jboolean)>("scheduleMountItem");
834843

835844
if (batchMountItemIntsSize == 0) {
836845
auto finishTransactionEndTime = telemetryTimePointNow();
@@ -845,7 +854,8 @@ void FabricMountingManager::executeMount(
845854
telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
846855
telemetryTimePointToMilliseconds(finishTransactionStartTime),
847856
telemetryTimePointToMilliseconds(finishTransactionEndTime),
848-
telemetry.getAffectedLayoutNodesCount());
857+
telemetry.getAffectedLayoutNodesCount(),
858+
static_cast<jboolean>(synchronous));
849859
return;
850860
}
851861

@@ -1012,7 +1022,8 @@ void FabricMountingManager::executeMount(
10121022
telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
10131023
telemetryTimePointToMilliseconds(finishTransactionStartTime),
10141024
telemetryTimePointToMilliseconds(finishTransactionEndTime),
1015-
telemetry.getAffectedLayoutNodesCount());
1025+
telemetry.getAffectedLayoutNodesCount(),
1026+
static_cast<jboolean>(synchronous));
10161027

10171028
env->DeleteLocalRef(buffer.ints);
10181029
}

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,23 @@ class FabricMountingManager final {
5454
*/
5555
bool isViewAllocated(SurfaceId surfaceId, Tag tag);
5656

57-
void executeMount(const MountingTransaction &transaction);
57+
/*
58+
* Converts the transaction's mutations into an IntBufferBatchMountItem and
59+
* hands it to Java.
60+
*
61+
* In the push model (`synchronous` = false), the batch is
62+
* scheduled onto the UI thread asynchronously.
63+
*
64+
* In the pull model (`synchronous` = true) the batch is
65+
* applied immediately on the calling (UI) thread.
66+
*/
67+
void executeMount(const MountingTransaction &transaction, bool synchronous = false);
68+
69+
/*
70+
* Pull model: notify Java that a transaction is available for `surfaceId` so
71+
* the UI thread can pull it via a PullTransactionMountItem.
72+
*/
73+
void onTransactionAvailable(SurfaceId surfaceId);
5874

5975
void dispatchCommand(const ShadowView &shadowView, const std::string &commandName, const folly::dynamic &args);
6076

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,55 @@ void FabricUIManagerBinding::reportMount(SurfaceId surfaceId) {
120120
scheduler->reportMount(surfaceId);
121121
}
122122

123+
void FabricUIManagerBinding::pullAndExecuteTransaction(SurfaceId surfaceId) {
124+
TraceSection section("FabricUIManagerBinding::pullAndExecuteTransaction");
125+
126+
std::shared_ptr<const MountingCoordinator> mountingCoordinator;
127+
{
128+
std::shared_lock lock(surfaceHandlerRegistryMutex_);
129+
auto iterator = surfaceHandlerRegistry_.find(surfaceId);
130+
if (iterator == surfaceHandlerRegistry_.end()) {
131+
return;
132+
}
133+
const auto* surfaceHandler = std::get_if<SurfaceHandler>(&iterator->second);
134+
jni::local_ref<SurfaceHandlerBinding::jhybridobject> javaSurfaceHandler;
135+
if (surfaceHandler == nullptr) {
136+
javaSurfaceHandler =
137+
std::get<jni::weak_ref<SurfaceHandlerBinding::jhybridobject>>(
138+
iterator->second)
139+
.lockLocal();
140+
if (javaSurfaceHandler) {
141+
surfaceHandler = &javaSurfaceHandler->cthis()->getSurfaceHandler();
142+
}
143+
}
144+
if (surfaceHandler != nullptr) {
145+
mountingCoordinator = surfaceHandler->getMountingCoordinator();
146+
}
147+
}
148+
149+
if (mountingCoordinator == nullptr) {
150+
return;
151+
}
152+
153+
auto mountingManager = getMountingManager("pullAndExecuteTransaction");
154+
if (!mountingManager) {
155+
return;
156+
}
157+
158+
// The UI thread pulls the transaction itself (it may accumulate several
159+
// revisions committed since the notification was enqueued, and may be empty
160+
// if a previous pull already consumed them). willPerformAsynchronously =
161+
// false: the transaction is applied synchronously right here, so no
162+
// `didPerformAsyncTransactions` bookkeeping is needed.
163+
auto mountingTransaction =
164+
mountingCoordinator->pullTransaction(/* willPerformAsynchronously = */
165+
false);
166+
if (mountingTransaction.has_value()) {
167+
mountingManager->executeMount(
168+
*mountingTransaction, /* synchronous = */ true);
169+
}
170+
}
171+
123172
#pragma mark - Surface management
124173

125174
// Used by bridgeless
@@ -678,7 +727,15 @@ void FabricUIManagerBinding::schedulerShouldRenderTransactions(
678727
}
679728
}
680729

681-
if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) {
730+
if (ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid()) {
731+
// Pull model: do NOT pull the transaction or build the batch here (on the
732+
// commit thread). Just notify Java that a transaction is available; the UI
733+
// thread will pull it via a PullTransactionMountItem and call back into
734+
// `pullAndExecuteTransaction`.
735+
mountingManager->onTransactionAvailable(
736+
mountingCoordinator->getSurfaceId());
737+
} else if (ReactNativeFeatureFlags::
738+
enableAccumulatedUpdatesInRawPropsAndroid()) {
682739
auto mountingTransaction = mountingCoordinator->pullTransaction(
683740
/* willPerformAsynchronously = */ true);
684741
if (mountingTransaction.has_value()) {
@@ -867,6 +924,9 @@ void FabricUIManagerBinding::registerNatives() {
867924
"drainPreallocateViewsQueue",
868925
FabricUIManagerBinding::drainPreallocateViewsQueue),
869926
makeNativeMethod("reportMount", FabricUIManagerBinding::reportMount),
927+
makeNativeMethod(
928+
"pullAndExecuteTransaction",
929+
FabricUIManagerBinding::pullAndExecuteTransaction),
870930
makeNativeMethod(
871931
"uninstallFabricUIManager",
872932
FabricUIManagerBinding::uninstallFabricUIManager),

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class FabricUIManagerBinding : public jni::HybridClass<FabricUIManagerBinding>,
133133

134134
void reportMount(SurfaceId surfaceId);
135135

136+
void pullAndExecuteTransaction(SurfaceId surfaceId);
137+
136138
jint findNextFocusableElement(jint parentTag, jint focusedTag, jint direction);
137139

138140
jintArray getRelativeAncestorList(jint rootTag, jint childTag);

scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2370,12 +2370,13 @@ class facebook::react::FabricMountingManager {
23702370
public void destroyUnmountedShadowNode(const facebook::react::ShadowNodeFamily& family);
23712371
public void dispatchCommand(const facebook::react::ShadowView& shadowView, const std::string& commandName, const folly::dynamic& args);
23722372
public void drainPreallocateViewsQueue();
2373-
public void executeMount(const facebook::react::MountingTransaction& transaction);
2373+
public void executeMount(const facebook::react::MountingTransaction& transaction, bool synchronous = false);
23742374
public void maybePreallocateShadowNode(const facebook::react::ShadowNode& shadowNode);
23752375
public void onAllAnimationsComplete();
23762376
public void onAnimationStarted();
23772377
public void onSurfaceStart(facebook::react::SurfaceId surfaceId);
23782378
public void onSurfaceStop(facebook::react::SurfaceId surfaceId);
2379+
public void onTransactionAvailable(facebook::react::SurfaceId surfaceId);
23792380
public void preallocateShadowView(const facebook::react::ShadowView& shadowView);
23802381
public void scheduleReactRevisionMerge(facebook::react::SurfaceId surfaceId);
23812382
public void sendAccessibilityEvent(const facebook::react::ShadowView& shadowView, const std::string& eventType);

scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2353,12 +2353,13 @@ class facebook::react::FabricMountingManager {
23532353
public void destroyUnmountedShadowNode(const facebook::react::ShadowNodeFamily& family);
23542354
public void dispatchCommand(const facebook::react::ShadowView& shadowView, const std::string& commandName, const folly::dynamic& args);
23552355
public void drainPreallocateViewsQueue();
2356-
public void executeMount(const facebook::react::MountingTransaction& transaction);
2356+
public void executeMount(const facebook::react::MountingTransaction& transaction, bool synchronous = false);
23572357
public void maybePreallocateShadowNode(const facebook::react::ShadowNode& shadowNode);
23582358
public void onAllAnimationsComplete();
23592359
public void onAnimationStarted();
23602360
public void onSurfaceStart(facebook::react::SurfaceId surfaceId);
23612361
public void onSurfaceStop(facebook::react::SurfaceId surfaceId);
2362+
public void onTransactionAvailable(facebook::react::SurfaceId surfaceId);
23622363
public void preallocateShadowView(const facebook::react::ShadowView& shadowView);
23632364
public void scheduleReactRevisionMerge(facebook::react::SurfaceId surfaceId);
23642365
public void sendAccessibilityEvent(const facebook::react::ShadowView& shadowView, const std::string& eventType);

scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2368,12 +2368,13 @@ class facebook::react::FabricMountingManager {
23682368
public void destroyUnmountedShadowNode(const facebook::react::ShadowNodeFamily& family);
23692369
public void dispatchCommand(const facebook::react::ShadowView& shadowView, const std::string& commandName, const folly::dynamic& args);
23702370
public void drainPreallocateViewsQueue();
2371-
public void executeMount(const facebook::react::MountingTransaction& transaction);
2371+
public void executeMount(const facebook::react::MountingTransaction& transaction, bool synchronous = false);
23722372
public void maybePreallocateShadowNode(const facebook::react::ShadowNode& shadowNode);
23732373
public void onAllAnimationsComplete();
23742374
public void onAnimationStarted();
23752375
public void onSurfaceStart(facebook::react::SurfaceId surfaceId);
23762376
public void onSurfaceStop(facebook::react::SurfaceId surfaceId);
2377+
public void onTransactionAvailable(facebook::react::SurfaceId surfaceId);
23772378
public void preallocateShadowView(const facebook::react::ShadowView& shadowView);
23782379
public void scheduleReactRevisionMerge(facebook::react::SurfaceId surfaceId);
23792380
public void sendAccessibilityEvent(const facebook::react::ShadowView& shadowView, const std::string& eventType);

0 commit comments

Comments
 (0)