Fabric: Improved thread-safety in ShadowTree

Summary: With new `ShadowTree::getRootShadowNode()` method all access to rootShadowNode_ is protected by commit mutex.

Reviewed By: mdvacca

Differential Revision: D10159456

fbshipit-source-id: 0bc8676ca2564a8ef95d60e912356e99d9f172c1
This commit is contained in:
Valentin Shergin 2018-10-09 16:24:57 -07:00 committed by Facebook Github Bot
parent e90f5fa263
commit 4876928f9e
2 changed files with 29 additions and 16 deletions

View File

@ -38,6 +38,11 @@ Tag ShadowTree::getRootTag() const {
return rootTag_; return rootTag_;
} }
SharedRootShadowNode ShadowTree::getRootShadowNode() const {
std::lock_guard<std::mutex> lock(commitMutex_);
return rootShadowNode_;
}
#pragma mark - Layout #pragma mark - Layout
Size ShadowTree::measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const { Size ShadowTree::measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
@ -46,7 +51,7 @@ Size ShadowTree::measure(const LayoutConstraints &layoutConstraints, const Layou
return newRootShadowNode->getLayoutMetrics().frame.size; return newRootShadowNode->getLayoutMetrics().frame.size;
} }
void ShadowTree::constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) { void ShadowTree::constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
auto newRootShadowNode = cloneRootShadowNode(layoutConstraints, layoutContext); auto newRootShadowNode = cloneRootShadowNode(layoutConstraints, layoutContext);
complete(newRootShadowNode); complete(newRootShadowNode);
} }
@ -54,15 +59,15 @@ void ShadowTree::constraintLayout(const LayoutConstraints &layoutConstraints, co
#pragma mark - Commiting #pragma mark - Commiting
UnsharedRootShadowNode ShadowTree::cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const { UnsharedRootShadowNode ShadowTree::cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
auto oldRootShadowNode = rootShadowNode_; auto oldRootShadowNode = getRootShadowNode();
const auto &props = std::make_shared<const RootProps>(*oldRootShadowNode->getProps(), layoutConstraints, layoutContext); const auto &props = std::make_shared<const RootProps>(*oldRootShadowNode->getProps(), layoutConstraints, layoutContext);
auto newRootShadowNode = auto newRootShadowNode =
std::make_shared<RootShadowNode>(*oldRootShadowNode, ShadowNodeFragment {.props = props}); std::make_shared<RootShadowNode>(*oldRootShadowNode, ShadowNodeFragment {.props = props});
return newRootShadowNode; return newRootShadowNode;
} }
void ShadowTree::complete(const SharedShadowNodeUnsharedList &rootChildNodes) { void ShadowTree::complete(const SharedShadowNodeUnsharedList &rootChildNodes) const {
auto oldRootShadowNode = rootShadowNode_; auto oldRootShadowNode = getRootShadowNode();
auto newRootShadowNode = auto newRootShadowNode =
std::make_shared<RootShadowNode>( std::make_shared<RootShadowNode>(
*oldRootShadowNode, *oldRootShadowNode,
@ -74,8 +79,8 @@ void ShadowTree::complete(const SharedShadowNodeUnsharedList &rootChildNodes) {
complete(newRootShadowNode); complete(newRootShadowNode);
} }
void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) { void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) const {
SharedRootShadowNode oldRootShadowNode = rootShadowNode_; SharedRootShadowNode oldRootShadowNode = getRootShadowNode();
newRootShadowNode->layout(); newRootShadowNode->layout();
@ -99,7 +104,7 @@ bool ShadowTree::commit(
const SharedRootShadowNode &oldRootShadowNode, const SharedRootShadowNode &oldRootShadowNode,
const SharedRootShadowNode &newRootShadowNode, const SharedRootShadowNode &newRootShadowNode,
const ShadowViewMutationList &mutations const ShadowViewMutationList &mutations
) { ) const {
std::lock_guard<std::mutex> lock(commitMutex_); std::lock_guard<std::mutex> lock(commitMutex_);
if (oldRootShadowNode != rootShadowNode_) { if (oldRootShadowNode != rootShadowNode_) {
@ -109,10 +114,11 @@ bool ShadowTree::commit(
rootShadowNode_ = newRootShadowNode; rootShadowNode_ = newRootShadowNode;
toggleEventEmitters(mutations); toggleEventEmitters(mutations);
return true; return true;
} }
void ShadowTree::emitLayoutEvents(const ShadowViewMutationList &mutations) { void ShadowTree::emitLayoutEvents(const ShadowViewMutationList &mutations) const {
for (const auto &mutation : mutations) { for (const auto &mutation : mutations) {
// Only `Insert` and `Update` mutations can affect layout metrics. // Only `Insert` and `Update` mutations can affect layout metrics.
if ( if (
@ -147,7 +153,7 @@ void ShadowTree::emitLayoutEvents(const ShadowViewMutationList &mutations) {
} }
} }
void ShadowTree::toggleEventEmitters(const ShadowViewMutationList &mutations) { void ShadowTree::toggleEventEmitters(const ShadowViewMutationList &mutations) const {
std::lock_guard<std::recursive_mutex> lock(EventEmitter::DispatchMutex()); std::lock_guard<std::recursive_mutex> lock(EventEmitter::DispatchMutex());
for (const auto &mutation : mutations) { for (const auto &mutation : mutations) {

View File

@ -49,17 +49,19 @@ public:
/* /*
* Applies given `layoutConstraints` and `layoutContext` and commit * Applies given `layoutConstraints` and `layoutContext` and commit
* the new shadow tree. * the new shadow tree.
* Returns `true` if the operation is finished successfully.
* Can be called from any thread. * Can be called from any thread.
*/ */
void constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext); void constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
#pragma mark - Application #pragma mark - Application
/* /*
* Create a new shadow tree with given `rootChildNodes` and commit. * Create a new shadow tree with given `rootChildNodes` and commit.
* Can be called from any thread. * Can be called from any thread.
* Returns `true` if the operation is finished successfully.
*/ */
void complete(const SharedShadowNodeUnsharedList &rootChildNodes); void complete(const SharedShadowNodeUnsharedList &rootChildNodes) const;
#pragma mark - Delegate #pragma mark - Delegate
@ -74,17 +76,22 @@ public:
private: private:
UnsharedRootShadowNode cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const; UnsharedRootShadowNode cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
void complete(UnsharedRootShadowNode newRootShadowNode); void complete(UnsharedRootShadowNode newRootShadowNode) const;
bool commit( bool commit(
const SharedRootShadowNode &oldRootShadowNode, const SharedRootShadowNode &oldRootShadowNode,
const SharedRootShadowNode &newRootShadowNode, const SharedRootShadowNode &newRootShadowNode,
const ShadowViewMutationList &mutations const ShadowViewMutationList &mutations
); ) const;
void toggleEventEmitters(const ShadowViewMutationList &mutations); void toggleEventEmitters(const ShadowViewMutationList &mutations) const;
void emitLayoutEvents(const ShadowViewMutationList &mutations); void emitLayoutEvents(const ShadowViewMutationList &mutations) const;
/*
* Return `rootShadowNodeMutex_` protected by `commitMutex_`.
*/
SharedRootShadowNode getRootShadowNode() const;
const Tag rootTag_; const Tag rootTag_;
SharedRootShadowNode rootShadowNode_; mutable SharedRootShadowNode rootShadowNode_; // Protected by `commitMutex_`.
ShadowTreeDelegate const *delegate_; ShadowTreeDelegate const *delegate_;
mutable std::mutex commitMutex_; mutable std::mutex commitMutex_;
}; };