Fabric: Stop commiting an empty tree in ShadowTree destructor

Summary:
The code is moved from the destructor a separate method that we now call in the Scheduler::stopSurface.
That makes the code more readable and resilient to possible races and ownership-related changes.

Reviewed By: JoshuaGross

Differential Revision: D17272294

fbshipit-source-id: 948d76d074577beb3dda6defdf09261b5c8abb98
This commit is contained in:
Valentin Shergin 2019-09-09 20:22:55 -07:00 коммит произвёл Facebook Github Bot
Родитель 28a5f122a8
Коммит b8ca677d70
3 изменённых файлов: 36 добавлений и 32 удалений

Просмотреть файл

@ -103,19 +103,6 @@ ShadowTree::ShadowTree(
}
ShadowTree::~ShadowTree() {
commit(
[](const SharedRootShadowNode &oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
ShadowNodeFragment{
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .eventEmitter = */
ShadowNodeFragment::eventEmitterPlaceholder(),
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
});
});
mountingCoordinator_->revoke();
}
@ -206,6 +193,23 @@ bool ShadowTree::tryCommit(ShadowTreeCommitTransaction transaction) const {
return true;
}
void ShadowTree::commitEmptyTree() const {
commit(
[](const SharedRootShadowNode &oldRootShadowNode)
-> UnsharedRootShadowNode {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
ShadowNodeFragment{
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .eventEmitter = */
ShadowNodeFragment::eventEmitterPlaceholder(),
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
});
});
}
void ShadowTree::emitLayoutEvents(
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
SystraceSection s("ShadowTree::emitLayoutEvents");

Просмотреть файл

@ -58,6 +58,11 @@ class ShadowTree final {
*/
void commit(ShadowTreeCommitTransaction transaction) const;
/*
* Commit an empty tree (a new `RootShadowNode` with no children).
*/
void commitEmptyTree() const;
#pragma mark - Delegate
/*

Просмотреть файл

@ -151,29 +151,24 @@ void Scheduler::renderTemplateToSurface(
void Scheduler::stopSurface(SurfaceId surfaceId) const {
SystraceSection s("Scheduler::stopSurface");
// Note, we have to do in inside `visit` function while the Shadow Tree
// is still being registered.
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [](const ShadowTree &shadowTree) {
// As part of stopping the Surface, we have to commit an empty tree.
return shadowTree.tryCommit([&](const SharedRootShadowNode
&oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
ShadowNodeFragment{
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
/* .surfaceId = */
ShadowNodeFragment::surfaceIdPlaceholder(),
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .eventEmitter = */
ShadowNodeFragment::eventEmitterPlaceholder(),
/* .children = */
ShadowNode::emptySharedShadowNodeSharedList(),
});
});
surfaceId, [](ShadowTree const &shadowTree) {
// As part of stopping a Surface, we need to properly destroy all
// mounted views, so we need to commit an empty tree to trigger all
// side-effects that will perform that.
shadowTree.commitEmptyTree();
});
auto shadowTree = uiManager_->getShadowTreeRegistry().remove(surfaceId);
shadowTree->setDelegate(nullptr);
// Waiting for all concurrent commits to be finished and unregistering the
// `ShadowTree`.
uiManager_->getShadowTreeRegistry().remove(surfaceId);
// We execute JavaScript/React part of the process at the very end to minimize
// any visible side-effects of stopping the Surface. Any possible commits from
// the JavaScript side will not be able to reference a `ShadowTree` and will
// fail silently.
auto uiManager = uiManager_;
runtimeExecutor_([=](jsi::Runtime &runtime) {
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {