Skip to content

Commit 9da1a01

Browse files
coadometa-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 fbshipit-source-id: 6e3ca5b8fbc8db647356ac555890c2e465890100
1 parent 67a813a commit 9da1a01

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
@@ -938,7 +938,8 @@ private void scheduleMountItem(
938938
long layoutEndTime,
939939
long finishTransactionStartTime,
940940
long finishTransactionEndTime,
941-
int affectedLayoutNodesCount) {
941+
int affectedLayoutNodesCount,
942+
boolean synchronous) {
942943
// When Binding.cpp calls scheduleMountItems during a commit phase, it always calls with
943944
// a BatchMountItem. No other sites call into this with a BatchMountItem, and Binding.cpp only
944945
// calls scheduleMountItems with a BatchMountItem.
@@ -952,8 +953,9 @@ private void scheduleMountItem(
952953
} else {
953954
shouldSchedule = mountItem != null;
954955
}
955-
// In case of sync rendering, this could be called on the UI thread. Otherwise,
956-
// it should ~always be called on the JS thread.
956+
// In the push model, this is ~always called on the JS thread, or on the UI
957+
// thread in case of sync rendering.
958+
// In the pull model, this is always called on the UI thread, at pull time.
957959
for (UIManagerListener listener : mListeners) {
958960
listener.didScheduleMountItems(this);
959961
}
@@ -968,16 +970,22 @@ private void scheduleMountItem(
968970

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

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)