first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB react_renderer_uimanager_consistency_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_renderer_uimanager_consistency OBJECT ${react_renderer_uimanager_consistency_SRC})
target_include_directories(react_renderer_uimanager_consistency PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_renderer_uimanager_consistency
glog
rrc_root
react_renderer_consistency
react_renderer_mounting)
target_compile_reactnative_options(react_renderer_uimanager_consistency PRIVATE)
target_compile_options(react_renderer_uimanager_consistency PRIVATE -Wpedantic)

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "LazyShadowTreeRevisionConsistencyManager.h"
#include <glog/logging.h>
namespace facebook::react {
LazyShadowTreeRevisionConsistencyManager::
LazyShadowTreeRevisionConsistencyManager(
ShadowTreeRegistry& shadowTreeRegistry)
: shadowTreeRegistry_(shadowTreeRegistry) {}
void LazyShadowTreeRevisionConsistencyManager::updateCurrentRevision(
SurfaceId surfaceId,
RootShadowNode::Shared rootShadowNode) {
std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_);
// We don't need to store the revision if we haven't locked.
// We can resolve lazily when requested.
if (lockCount > 0) {
capturedRootShadowNodesForConsistency_[surfaceId] =
std::move(rootShadowNode);
}
}
#pragma mark - ShadowTreeRevisionProvider
RootShadowNode::Shared
LazyShadowTreeRevisionConsistencyManager::getCurrentRevision(
SurfaceId surfaceId) {
{
std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_);
if (lockCount > 0) {
auto it = capturedRootShadowNodesForConsistency_.find(surfaceId);
if (it != capturedRootShadowNodesForConsistency_.end()) {
return it->second;
}
}
}
// This method is only going to be called from JS, so we don't need to protect
// the access to the shadow tree registry as well.
// If this was multi-threaded, we would need to protect it to avoid capturing
// root shadow nodes concurrently.
RootShadowNode::Shared rootShadowNode;
shadowTreeRegistry_.visit(surfaceId, [&](const ShadowTree& shadowTree) {
rootShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
});
{
std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_);
if (lockCount > 0) {
capturedRootShadowNodesForConsistency_[surfaceId] = rootShadowNode;
}
}
return rootShadowNode;
}
#pragma mark - ConsistentShadowTreeRevisionProvider
void LazyShadowTreeRevisionConsistencyManager::lockRevisions() {
std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_);
// We actually capture the state lazily the first time we access it, so we
// don't need to do anything here.
lockCount++;
}
void LazyShadowTreeRevisionConsistencyManager::unlockRevisions() {
std::unique_lock lock(capturedRootShadowNodesForConsistencyMutex_);
if (lockCount == 0) {
LOG(WARNING)
<< "LazyShadowTreeRevisionConsistencyManager::unlockRevisions() called without a previous lock";
} else {
lockCount--;
}
if (lockCount == 0) {
capturedRootShadowNodesForConsistency_.clear();
}
}
} // namespace facebook::react

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/consistency/ShadowTreeRevisionConsistencyManager.h>
#include <react/renderer/mounting/ShadowTreeRegistry.h>
#include <react/renderer/uimanager/consistency/ShadowTreeRevisionProvider.h>
#include <cstdint>
#include <memory>
#include <shared_mutex>
namespace facebook::react {
/**
* This class implements UI consistency for the JavaScript thread.
* This implementation forces JavaScript to see a stable revision of the shadow
* tree for a given surface ID, only updating it when React commits a new tree
* or between JS tasks.
*/
class LazyShadowTreeRevisionConsistencyManager : public ShadowTreeRevisionConsistencyManager,
public ShadowTreeRevisionProvider {
public:
explicit LazyShadowTreeRevisionConsistencyManager(ShadowTreeRegistry &shadowTreeRegistry);
void updateCurrentRevision(SurfaceId surfaceId, RootShadowNode::Shared rootShadowNode);
#pragma mark - ShadowTreeRevisionProvider
RootShadowNode::Shared getCurrentRevision(SurfaceId surfaceId) override;
#pragma mark - ShadowTreeRevisionConsistencyManager
void lockRevisions() override;
void unlockRevisions() override;
private:
std::mutex capturedRootShadowNodesForConsistencyMutex_;
std::unordered_map<SurfaceId, RootShadowNode::Shared> capturedRootShadowNodesForConsistency_;
ShadowTreeRegistry &shadowTreeRegistry_;
uint_fast32_t lockCount{0};
};
} // namespace facebook::react

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/core/ReactPrimitives.h>
#include <memory>
namespace facebook::react {
/**
* This interface is used for UI consistency, indicating the revision of the
* shadow tree that a caller should have access to.
*/
class ShadowTreeRevisionProvider {
public:
virtual ~ShadowTreeRevisionProvider() = default;
virtual RootShadowNode::Shared getCurrentRevision(SurfaceId surfaceId) = 0;
};
} // namespace facebook::react

View File

@@ -0,0 +1,396 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <gtest/gtest.h>
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/element/testUtils.h>
#include <react/renderer/mounting/ShadowTree.h>
#include <react/renderer/mounting/ShadowTreeRegistry.h>
#include <react/renderer/uimanager/consistency/LazyShadowTreeRevisionConsistencyManager.h>
namespace facebook::react {
class FakeShadowTreeDelegate : public ShadowTreeDelegate {
public:
RootShadowNode::Unshared shadowTreeWillCommit(
const ShadowTree& /*shadowTree*/,
const std::shared_ptr<const RootShadowNode>& /*oldRootShadowNode*/,
const RootShadowNode::Unshared& newRootShadowNode,
const ShadowTree::CommitOptions& /*commitOptions*/) const override {
return newRootShadowNode;
}
void shadowTreeDidFinishTransaction(
std::shared_ptr<const MountingCoordinator> mountingCoordinator,
bool /*mountSynchronously*/) const override {}
};
class LazyShadowTreeRevisionConsistencyManagerTest : public ::testing::Test {
public:
LazyShadowTreeRevisionConsistencyManagerTest()
: consistencyManager_(shadowTreeRegistry_) {}
void TearDown() override {
// this is necessary because otherwise the test will crash with an assertion
// preventing the deallocation of the registry with registered shadow trees.
auto ids = std::vector<SurfaceId>();
shadowTreeRegistry_.enumerate(
[&ids](const ShadowTree& shadowTree, bool& /*stop*/) {
ids.push_back(shadowTree.getSurfaceId());
});
for (auto id : ids) {
shadowTreeRegistry_.remove(id);
}
}
std::unique_ptr<ShadowTree> createShadowTree(SurfaceId surfaceId) {
return std::make_unique<ShadowTree>(
surfaceId,
layoutConstraints_,
layoutContext_,
shadowTreeDelegate_,
contextContainer_);
}
ShadowTreeRegistry shadowTreeRegistry_{};
LazyShadowTreeRevisionConsistencyManager consistencyManager_;
LayoutConstraints layoutConstraints_{};
LayoutContext layoutContext_{};
FakeShadowTreeDelegate shadowTreeDelegate_{};
ContextContainer contextContainer_{};
};
TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testLockedOnNoRevision) {
consistencyManager_.lockRevisions();
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
shadowTreeRegistry_.add(createShadowTree(0));
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
consistencyManager_.unlockRevisions();
}
TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testNotLocked) {
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
shadowTreeRegistry_.add(createShadowTree(0));
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), newRootShadowNode);
}
TEST_F(
LazyShadowTreeRevisionConsistencyManagerTest,
testLockedOnNoRevisionWithUpdate) {
consistencyManager_.lockRevisions();
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
shadowTreeRegistry_.add(createShadowTree(0));
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
consistencyManager_.updateCurrentRevision(0, newRootShadowNode);
EXPECT_NE(consistencyManager_.getCurrentRevision(0), nullptr);
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
consistencyManager_.unlockRevisions();
}
TEST_F(
LazyShadowTreeRevisionConsistencyManagerTest,
testLockedOnNoRevisionWithMultipleUpdates) {
consistencyManager_.lockRevisions();
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
shadowTreeRegistry_.add(createShadowTree(0));
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
consistencyManager_.updateCurrentRevision(0, newRootShadowNode);
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
auto newRootShadowNode2 = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode2](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode2;
},
{});
});
consistencyManager_.updateCurrentRevision(0, newRootShadowNode2);
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(),
newRootShadowNode2.get());
consistencyManager_.unlockRevisions();
}
TEST_F(
LazyShadowTreeRevisionConsistencyManagerTest,
testLockedOnExistingRevision) {
shadowTreeRegistry_.add(createShadowTree(0));
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
consistencyManager_.lockRevisions();
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
consistencyManager_.unlockRevisions();
}
TEST_F(
LazyShadowTreeRevisionConsistencyManagerTest,
testLockedOnExistingRevisionWithUpdates) {
shadowTreeRegistry_.add(createShadowTree(0));
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
consistencyManager_.lockRevisions();
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
auto newRootShadowNode2 = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode2](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode2;
},
{});
});
// Not updated
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
consistencyManager_.updateCurrentRevision(0, newRootShadowNode2);
// Updated
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(),
newRootShadowNode2.get());
consistencyManager_.unlockRevisions();
}
TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testLockAfterUnlock) {
shadowTreeRegistry_.add(createShadowTree(0));
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
consistencyManager_.lockRevisions();
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
auto newRootShadowNode2 = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode2](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode2;
},
{});
});
// Not updated
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
consistencyManager_.unlockRevisions();
consistencyManager_.lockRevisions();
// Updated
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(),
newRootShadowNode2.get());
consistencyManager_.unlockRevisions();
}
TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testUpdateToUnmounted) {
shadowTreeRegistry_.add(createShadowTree(0));
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
consistencyManager_.lockRevisions();
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
consistencyManager_.updateCurrentRevision(0, nullptr);
// Updated
EXPECT_EQ(consistencyManager_.getCurrentRevision(0).get(), nullptr);
consistencyManager_.unlockRevisions();
}
TEST_F(LazyShadowTreeRevisionConsistencyManagerTest, testReentrance) {
consistencyManager_.lockRevisions();
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
shadowTreeRegistry_.add(createShadowTree(0));
auto element = Element<RootShadowNode>();
auto builder = simpleComponentBuilder();
auto newRootShadowNode = builder.build(element);
shadowTreeRegistry_.visit(
0, [newRootShadowNode](const ShadowTree& shadowTree) {
shadowTree.commit(
[&](const RootShadowNode& /*oldRootShadowNode*/) {
return newRootShadowNode;
},
{});
});
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
// Re-entrance
consistencyManager_.lockRevisions();
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
// Exit second lock
consistencyManager_.unlockRevisions();
EXPECT_EQ(consistencyManager_.getCurrentRevision(0), nullptr);
// Exit first lock
consistencyManager_.unlockRevisions();
// Updated!
EXPECT_EQ(
consistencyManager_.getCurrentRevision(0).get(), newRootShadowNode.get());
}
} // namespace facebook::react