Skip to content

Commit

Permalink
Fabric: Introducing ShadowTreeRegistry
Browse files Browse the repository at this point in the history
Summary:
Why do we need a dedicated registry class?
* We need to simplify registry-related logic in Scheduler.
* We need to couple threading aspect of the registry with the registry itself, otherwise it's not clear why exactly we acquire the mutex. We also should not acquire the mutex in a per-method way (as we did before), because it's incorrect and misleading (only lines that access the registry should by protected).
* We need to have a way to share the registry with other classes (e.g. UIManager) without passing a reference to the whole Scheduler.

Reviewed By: mdvacca

Differential Revision: D13036550

fbshipit-source-id: 644da910e823666c586834a3da2b4cdcb90eebb2
  • Loading branch information
shergin authored and facebook-github-bot committed Nov 22, 2018
1 parent 71208f0 commit b4fa1fa
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 39 deletions.
61 changes: 27 additions & 34 deletions ReactCommon/fabric/uimanager/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,11 @@ void Scheduler::startSurface(
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
std::lock_guard<std::mutex> lock(mutex_);

auto shadowTree =
std::make_unique<ShadowTree>(surfaceId, layoutConstraints, layoutContext);
shadowTree->setDelegate(this);
shadowTreeRegistry_.emplace(surfaceId, std::move(shadowTree));

shadowTreeRegistry_.add(std::move(shadowTree));

#ifndef ANDROID
runtimeExecutor_([=](jsi::Runtime &runtime) {
Expand All @@ -96,26 +95,25 @@ void Scheduler::renderTemplateToSurface(
*componentDescriptorRegistry_,
nMR);

std::lock_guard<std::mutex> lock(mutex_);
const auto &shadowTree = shadowTreeRegistry_.at(surfaceId);
assert(shadowTree);
shadowTree->complete(
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{tree}));
shadowTreeRegistry_.get(surfaceId, [=](const ShadowTree &shadowTree) {
shadowTree.complete(
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList{tree}));
});
} catch (const std::exception &e) {
LOG(ERROR) << " >>>> EXCEPTION <<< rendering uiTemplate in "
<< "Scheduler::renderTemplateToSurface: " << e.what();
}
}

void Scheduler::stopSurface(SurfaceId surfaceId) const {
std::lock_guard<std::mutex> lock(mutex_);
const auto &iterator = shadowTreeRegistry_.find(surfaceId);
auto &shadowTree = *iterator->second;
// As part of stopping the Surface, we have to commit an empty tree.
shadowTree.complete(std::const_pointer_cast<SharedShadowNodeList>(
ShadowNode::emptySharedShadowNodeSharedList()));
shadowTree.setDelegate(nullptr);
shadowTreeRegistry_.erase(iterator);
shadowTreeRegistry_.get(surfaceId, [](const ShadowTree &shadowTree) {
// As part of stopping the Surface, we have to commit an empty tree.
shadowTree.complete(std::const_pointer_cast<SharedShadowNodeList>(
ShadowNode::emptySharedShadowNodeSharedList()));
});

auto shadowTree = shadowTreeRegistry_.remove(surfaceId);
shadowTree->setDelegate(nullptr);

#ifndef ANDROID
runtimeExecutor_([=](jsi::Runtime &runtime) {
Expand All @@ -128,21 +126,21 @@ Size Scheduler::measureSurface(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
std::lock_guard<std::mutex> lock(mutex_);
const auto &shadowTree = shadowTreeRegistry_.at(surfaceId);
assert(shadowTree);
return shadowTree->measure(layoutConstraints, layoutContext);
Size size;
shadowTreeRegistry_.get(surfaceId, [&](const ShadowTree &shadowTree) {
size = shadowTree.measure(layoutConstraints, layoutContext);
});
return size;
}

void Scheduler::constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
std::lock_guard<std::mutex> lock(mutex_);
const auto &shadowTree = shadowTreeRegistry_.at(surfaceId);
assert(shadowTree);
shadowTree->synchronize([&]() {
shadowTree->constraintLayout(layoutConstraints, layoutContext);
shadowTreeRegistry_.get(surfaceId, [&](const ShadowTree &shadowTree) {
shadowTree.synchronize([&]() {
shadowTree.constraintLayout(layoutConstraints, layoutContext);
});
});
}

Expand Down Expand Up @@ -170,16 +168,11 @@ void Scheduler::shadowTreeDidCommit(
#pragma mark - UIManagerDelegate

void Scheduler::uiManagerDidFinishTransaction(
Tag rootTag,
SurfaceId surfaceId,
const SharedShadowNodeUnsharedList &rootChildNodes) {
std::lock_guard<std::mutex> lock(mutex_);
const auto iterator = shadowTreeRegistry_.find(rootTag);
if (iterator == shadowTreeRegistry_.end()) {
// This might happen during surface unmounting/deallocation process
// due to the asynchronous nature of JS calls.
return;
}
iterator->second->complete(rootChildNodes);
shadowTreeRegistry_.get(surfaceId, [&](const ShadowTree &shadowTree) {
shadowTree.complete(rootChildNodes);
});
}

void Scheduler::uiManagerDidCreateShadowNode(
Expand Down
7 changes: 3 additions & 4 deletions ReactCommon/fabric/uimanager/Scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <react/uimanager/SchedulerDelegate.h>
#include <react/uimanager/ShadowTree.h>
#include <react/uimanager/ShadowTreeDelegate.h>
#include <react/uimanager/ShadowTreeRegistry.h>
#include <react/uimanager/UIManagerBinding.h>
#include <react/uimanager/UIManagerDelegate.h>
#include <react/uimanager/primitives.h>
Expand Down Expand Up @@ -75,7 +76,7 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
#pragma mark - UIManagerDelegate

void uiManagerDidFinishTransaction(
Tag rootTag,
SurfaceId surfaceId,
const SharedShadowNodeUnsharedList &rootChildNodes) override;
void uiManagerDidCreateShadowNode(
const SharedShadowNode &shadowNode) override;
Expand All @@ -89,9 +90,7 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
private:
SchedulerDelegate *delegate_;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
mutable std::mutex mutex_;
mutable std::unordered_map<SurfaceId, std::unique_ptr<ShadowTree>>
shadowTreeRegistry_; // Protected by `mutex_`.
ShadowTreeRegistry shadowTreeRegistry_;
SharedContextContainer contextContainer_;
RuntimeExecutor runtimeExecutor_;
std::shared_ptr<UIManagerBinding> uiManagerBinding_;
Expand Down
40 changes: 40 additions & 0 deletions ReactCommon/fabric/uimanager/ShadowTreeRegistry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Facebook, Inc. and its affiliates.

// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

#include "ShadowTreeRegistry.h"

namespace facebook {
namespace react {

void ShadowTreeRegistry::add(std::unique_ptr<ShadowTree> &&shadowTree) const {
std::lock_guard<std::mutex> lock(mutex_);
registry_.emplace(shadowTree->getSurfaceId(), std::move(shadowTree));
}

std::unique_ptr<ShadowTree> ShadowTreeRegistry::remove(
SurfaceId surfaceId) const {
std::lock_guard<std::mutex> lock(mutex_);
auto iterator = registry_.find(surfaceId);
auto shadowTree = std::unique_ptr<ShadowTree>(iterator->second.release());
registry_.erase(iterator);
return shadowTree;
}

bool ShadowTreeRegistry::get(
SurfaceId surfaceId,
std::function<void(const ShadowTree &shadowTree)> callback) const {
std::lock_guard<std::mutex> lock(mutex_);
auto iterator = registry_.find(surfaceId);

if (iterator == registry_.end()) {
return false;
}

callback(*iterator->second);
return true;
}

} // namespace react
} // namespace facebook
55 changes: 55 additions & 0 deletions ReactCommon/fabric/uimanager/ShadowTreeRegistry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) Facebook, Inc. and its 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/core/ReactPrimitives.h>
#include <react/uimanager/ShadowTree.h>

namespace facebook {
namespace react {

/*
* Owning registry of `ShadowTree`s.
*/
class ShadowTreeRegistry final {
public:
ShadowTreeRegistry() = default;

/*
* Adds a `ShadowTree` instance to the registry.
* The ownership of the instance is also transferred to the registry.
* Can be called from any thread.
*/
void add(std::unique_ptr<ShadowTree> &&shadowTree) const;

/*
* Removes a `ShadowTree` instance with given `surfaceId` from the registry
* and returns it as a result.
* The ownership of the instance is also transferred to the caller.
* Can be called from any thread.
*/
std::unique_ptr<ShadowTree> remove(SurfaceId surfaceId) const;

/*
* Finds a `ShadowTree` instance with a given `surfaceId` in the registry and
* synchronously calls the `callback` with a reference to the instance while
* the mutex is being acquired.
* Returns `true` if the registry has `ShadowTree` instance with corresponding
* `surfaceId`, otherwise returns `false` without calling the `callback`.
* Can be called from any thread.
*/
bool get(
SurfaceId surfaceId,
std::function<void(const ShadowTree &shadowTree)> callback) const;

private:
mutable std::mutex mutex_;
mutable std::unordered_map<SurfaceId, std::unique_ptr<ShadowTree>>
registry_; // Protected by `mutex_`.
};

} // namespace react
} // namespace facebook
2 changes: 1 addition & 1 deletion ReactCommon/fabric/uimanager/UIManagerDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class UIManagerDelegate {
* The tree is not layed out and not sealed at this time.
*/
virtual void uiManagerDidFinishTransaction(
Tag rootTag,
SurfaceId surfaceId,
const SharedShadowNodeUnsharedList &rootChildNodes) = 0;

/*
Expand Down

0 comments on commit b4fa1fa

Please sign in to comment.