Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ std::string getShadowTreeCommitSourceName(ShadowTreeCommitSource source) {
return "ReactRevisionMerge";
}
}

inline bool isPropsUpdatesAccumulationGuaranteed() {
#ifdef __ANDROID__
return ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid();
#else
return true;
#endif
}
} // namespace

using CommitStatus = ShadowTree::CommitStatus;
Expand Down Expand Up @@ -482,31 +490,60 @@ void ShadowTree::mount(ShadowTreeRevision revision, bool mountSynchronously)

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

{
UniqueLock lock = uniqueRevisionLock(false);

if (!reactRevisionToBePromoted_.has_value()) {
return;
}
if (isPropsUpdatesAccumulationGuaranteed()) {
if (!reactRevisionToBePromoted_.has_value()) {
return;
}

promotedRevision =
std::exchange(reactRevisionToBePromoted_, std::nullopt).value();
}
promotedRevision =
std::exchange(reactRevisionToBePromoted_, std::nullopt).value();
} else {
if (queuedReactRevisions_.empty()) {
return;
}

const auto mergedRevisionNumber = promotedRevision.number;
queuedReactRevisions_.swap(queuedRevisions);
}
}

this->commit(
[revision = std::move(promotedRevision)](
const RootShadowNode& /*oldRootShadowNode*/) {
return std::make_shared<RootShadowNode>(
*revision.rootShadowNode, ShadowNodeFragment{});
},
{
.enableStateReconciliation = true,
.mountSynchronously = true,
.source = CommitSource::ReactRevisionMerge,
});
ShadowTreeRevision::Number lastMergedRevisionNumber;

if (isPropsUpdatesAccumulationGuaranteed()) {
lastMergedRevisionNumber = promotedRevision.number;
this->commit(
[revision = std::move(promotedRevision)](
const RootShadowNode& /*oldRootShadowNode*/) {
return std::make_shared<RootShadowNode>(
*revision.rootShadowNode, ShadowNodeFragment{});
},
{
.enableStateReconciliation = true,
.mountSynchronously = true,
.source = CommitSource::ReactRevisionMerge,
});
} else {
for (auto& revision : queuedRevisions) {
lastMergedRevisionNumber = revision.number;
this->commit(
[revision = std::move(revision)](
const RootShadowNode& /*oldRootShadowNode*/) {
return std::make_shared<RootShadowNode>(
*revision.rootShadowNode, ShadowNodeFragment{});
},
{
.enableStateReconciliation = true,
.mountSynchronously = true,
.source = CommitSource::ReactRevisionMerge,
});
}
}

{
UniqueLock commitLock = uniqueRevisionLock(
Expand All @@ -515,34 +552,47 @@ void ShadowTree::mergeReactRevision() const {
// If the current react revision is the same as the one that was just
// merged, clear it.
if (currentReactRevision_.has_value() &&
mergedRevisionNumber == currentReactRevision_.value().number) {
lastMergedRevisionNumber == currentReactRevision_.value().number) {
currentReactRevision_.reset();
}
}
}

void ShadowTree::promoteReactRevision() const {
ShadowTreeRevision currentReactRevision;
{
SharedLock lock = sharedRevisionLock();
// Promotion happens at the end of the event loop tick, it's possible to
// have more than one promotion in a row. In this case, all but the first
// one should no-op.
if (!currentReactRevision_.has_value()) {
return;
// Promote only when props updates accumulation is guaranteed. Otherwise,
// queuedReactRevisions_ will be used instead.
if (isPropsUpdatesAccumulationGuaranteed()) {
ShadowTreeRevision currentReactRevision;
{
SharedLock lock = sharedRevisionLock();
// Promotion happens at the end of the event loop tick, it's possible to
// have more than one promotion in a row. In this case, all but the first
// one should no-op.
if (!currentReactRevision_.has_value()) {
return;
}
currentReactRevision = currentReactRevision_.value();
}
currentReactRevision = currentReactRevision_.value();
}

{
UniqueLock lock = uniqueRevisionLock(false);
reactRevisionToBePromoted_ = std::move(currentReactRevision);
{
UniqueLock lock = uniqueRevisionLock(false);
reactRevisionToBePromoted_ = std::move(currentReactRevision);
}
}

delegate_.shadowTreeDidPromoteReactRevision(*this);
}

void ShadowTree::scheduleReactRevisionPromotion() const {
if (!isPropsUpdatesAccumulationGuaranteed()) {
// If props updates accumulation is not guaranteed, we need to store each
// revision to be merged separately.
UniqueLock lock = uniqueRevisionLock(false);
if (currentReactRevision_.has_value()) {
queuedReactRevisions_.push_back(currentReactRevision_.value());
}
}

delegate_.shadowTreeDidFinishReactCommit(*this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class ShadowTree final {
mutable ShadowTreeRevision currentRevision_; // Protected by `revisionMutex_`.
mutable std::optional<ShadowTreeRevision> currentReactRevision_; // Protected by `revisionMutex_`.
mutable std::optional<ShadowTreeRevision> reactRevisionToBePromoted_; // Protected by `revisionMutex_`.
mutable std::vector<ShadowTreeRevision> queuedReactRevisions_; // Protected by `revisionMutex_`.
std::shared_ptr<const MountingCoordinator> mountingCoordinator_;

using UniqueLock = std::variant<std::unique_lock<std::shared_mutex>, std::unique_lock<std::recursive_mutex>>;
Expand Down
Loading