Skip to content

Commit bcadeda

Browse files
j-piaseckimeta-codesync[bot]
authored andcommitted
Merge all intermediate revisions when props accumulation is not guaranteed (#56914)
Summary: Pull Request resolved: #56914 Changelog: [ANDROID][FIXED] Fix commit branching dropping updates when `enableAccumulatedUpdatesInRawPropsAndroid` is not enabled. Without `enableAccumulatedUpdatesInRawPropsAndroid` each shadow tree revision on Android carries only the props applied in that specific revision. To handle this properly, all intermediate revisions need to pass through the mounting layer so the platform is aware of all the props. Reviewed By: rubennorte Differential Revision: D105835162 fbshipit-source-id: c5a724df43cd0176a5bef4f723dae12d18ca6bc9
1 parent b32a6c9 commit bcadeda

2 files changed

Lines changed: 99 additions & 31 deletions

File tree

packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ std::string getShadowTreeCommitSourceName(ShadowTreeCommitSource source) {
3838
return "ReactRevisionMerge";
3939
}
4040
}
41+
42+
inline bool isPropsUpdatesAccumulationGuaranteed() {
43+
#ifdef __ANDROID__
44+
return ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid();
45+
#else
46+
return true;
47+
#endif
48+
}
4149
} // namespace
4250

4351
using CommitStatus = ShadowTree::CommitStatus;
@@ -482,31 +490,63 @@ void ShadowTree::mount(ShadowTreeRevision revision, bool mountSynchronously)
482490

483491
void ShadowTree::mergeReactRevision() const {
484492
ShadowTreeRevision promotedRevision;
493+
std::vector<ShadowTreeRevision> promotedRevisions;
494+
// If props updates accumulation is guaranteed, we can merge the promoted
495+
// react revision, otherwise we need to merge all queued revisions.
485496

486497
{
487498
UniqueLock lock = uniqueRevisionLock(false);
488499

489-
if (!reactRevisionToBePromoted_.has_value()) {
490-
return;
491-
}
500+
if (isPropsUpdatesAccumulationGuaranteed()) {
501+
if (!reactRevisionToBePromoted_.has_value()) {
502+
return;
503+
}
492504

493-
promotedRevision =
494-
std::exchange(reactRevisionToBePromoted_, std::nullopt).value();
495-
}
505+
promotedRevision =
506+
std::exchange(reactRevisionToBePromoted_, std::nullopt).value();
507+
} else {
508+
if (promotedReactRevisions_.empty()) {
509+
return;
510+
}
496511

497-
const auto mergedRevisionNumber = promotedRevision.number;
512+
promotedReactRevisions_.swap(promotedRevisions);
513+
}
514+
}
498515

499-
this->commit(
500-
[revision = std::move(promotedRevision)](
501-
const RootShadowNode& /*oldRootShadowNode*/) {
502-
return std::make_shared<RootShadowNode>(
503-
*revision.rootShadowNode, ShadowNodeFragment{});
504-
},
505-
{
506-
.enableStateReconciliation = true,
507-
.mountSynchronously = true,
508-
.source = CommitSource::ReactRevisionMerge,
509-
});
516+
ShadowTreeRevision::Number lastMergedRevisionNumber;
517+
518+
if (isPropsUpdatesAccumulationGuaranteed()) {
519+
lastMergedRevisionNumber = promotedRevision.number;
520+
this->commit(
521+
[revision = std::move(promotedRevision)](
522+
const RootShadowNode& /*oldRootShadowNode*/) {
523+
return std::make_shared<RootShadowNode>(
524+
*revision.rootShadowNode, ShadowNodeFragment{});
525+
},
526+
{
527+
.enableStateReconciliation = true,
528+
.mountSynchronously = true,
529+
.source = CommitSource::ReactRevisionMerge,
530+
});
531+
} else {
532+
for (size_t i = 0; i < promotedRevisions.size(); ++i) {
533+
auto& revision = promotedRevisions[i];
534+
bool isLast = i == promotedRevisions.size() - 1;
535+
lastMergedRevisionNumber = revision.number;
536+
537+
this->commit(
538+
[revision = std::move(revision)](
539+
const RootShadowNode& /*oldRootShadowNode*/) {
540+
return std::make_shared<RootShadowNode>(
541+
*revision.rootShadowNode, ShadowNodeFragment{});
542+
},
543+
{
544+
.enableStateReconciliation = isLast,
545+
.mountSynchronously = true,
546+
.source = CommitSource::ReactRevisionMerge,
547+
});
548+
}
549+
}
510550

511551
{
512552
UniqueLock commitLock = uniqueRevisionLock(
@@ -515,34 +555,60 @@ void ShadowTree::mergeReactRevision() const {
515555
// If the current react revision is the same as the one that was just
516556
// merged, clear it.
517557
if (currentReactRevision_.has_value() &&
518-
mergedRevisionNumber == currentReactRevision_.value().number) {
558+
lastMergedRevisionNumber == currentReactRevision_.value().number) {
519559
currentReactRevision_.reset();
520560
}
521561
}
522562
}
523563

524564
void ShadowTree::promoteReactRevision() const {
525-
ShadowTreeRevision currentReactRevision;
526-
{
527-
SharedLock lock = sharedRevisionLock();
528-
// Promotion happens at the end of the event loop tick, it's possible to
529-
// have more than one promotion in a row. In this case, all but the first
530-
// one should no-op.
531-
if (!currentReactRevision_.has_value()) {
532-
return;
565+
// Promote only when props updates accumulation is guaranteed. Otherwise,
566+
// queuedReactRevisions_ will be used instead.
567+
if (isPropsUpdatesAccumulationGuaranteed()) {
568+
ShadowTreeRevision currentReactRevision;
569+
{
570+
SharedLock lock = sharedRevisionLock();
571+
// Promotion happens at the end of the event loop tick, it's possible to
572+
// have more than one promotion in a row. In this case, all but the first
573+
// one should no-op.
574+
if (!currentReactRevision_.has_value()) {
575+
return;
576+
}
577+
currentReactRevision = currentReactRevision_.value();
533578
}
534-
currentReactRevision = currentReactRevision_.value();
535-
}
536579

537-
{
580+
{
581+
UniqueLock lock = uniqueRevisionLock(false);
582+
reactRevisionToBePromoted_ = std::move(currentReactRevision);
583+
}
584+
} else {
538585
UniqueLock lock = uniqueRevisionLock(false);
539-
reactRevisionToBePromoted_ = std::move(currentReactRevision);
586+
587+
if (queuedReactRevisions_.empty()) {
588+
return;
589+
}
590+
591+
// Move all queued revisions to the promoted revisions.
592+
promotedReactRevisions_.insert(
593+
promotedReactRevisions_.end(),
594+
std::make_move_iterator(queuedReactRevisions_.begin()),
595+
std::make_move_iterator(queuedReactRevisions_.end()));
596+
queuedReactRevisions_.clear();
540597
}
541598

542599
delegate_.shadowTreeDidPromoteReactRevision(*this);
543600
}
544601

545602
void ShadowTree::scheduleReactRevisionPromotion() const {
603+
if (!isPropsUpdatesAccumulationGuaranteed()) {
604+
// If props updates accumulation is not guaranteed, we need to store each
605+
// revision to be merged separately.
606+
UniqueLock lock = uniqueRevisionLock(false);
607+
if (currentReactRevision_.has_value()) {
608+
queuedReactRevisions_.push_back(currentReactRevision_.value());
609+
}
610+
}
611+
546612
delegate_.shadowTreeDidFinishReactCommit(*this);
547613
}
548614

packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ class ShadowTree final {
173173
mutable ShadowTreeRevision currentRevision_; // Protected by `revisionMutex_`.
174174
mutable std::optional<ShadowTreeRevision> currentReactRevision_; // Protected by `revisionMutex_`.
175175
mutable std::optional<ShadowTreeRevision> reactRevisionToBePromoted_; // Protected by `revisionMutex_`.
176+
mutable std::vector<ShadowTreeRevision> queuedReactRevisions_; // Protected by `revisionMutex_`.
177+
mutable std::vector<ShadowTreeRevision> promotedReactRevisions_; // Protected by `revisionMutex_`.
176178
std::shared_ptr<const MountingCoordinator> mountingCoordinator_;
177179

178180
using UniqueLock = std::variant<std::unique_lock<std::shared_mutex>, std::unique_lock<std::recursive_mutex>>;

0 commit comments

Comments
 (0)