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:
Родитель
28a5f122a8
Коммит
b8ca677d70
|
@ -103,19 +103,6 @@ ShadowTree::ShadowTree(
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
mountingCoordinator_->revoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +193,23 @@ bool ShadowTree::tryCommit(ShadowTreeCommitTransaction transaction) const {
|
||||||
return true;
|
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(
|
void ShadowTree::emitLayoutEvents(
|
||||||
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
|
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
|
||||||
SystraceSection s("ShadowTree::emitLayoutEvents");
|
SystraceSection s("ShadowTree::emitLayoutEvents");
|
||||||
|
|
|
@ -58,6 +58,11 @@ class ShadowTree final {
|
||||||
*/
|
*/
|
||||||
void commit(ShadowTreeCommitTransaction transaction) const;
|
void commit(ShadowTreeCommitTransaction transaction) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commit an empty tree (a new `RootShadowNode` with no children).
|
||||||
|
*/
|
||||||
|
void commitEmptyTree() const;
|
||||||
|
|
||||||
#pragma mark - Delegate
|
#pragma mark - Delegate
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -151,29 +151,24 @@ void Scheduler::renderTemplateToSurface(
|
||||||
void Scheduler::stopSurface(SurfaceId surfaceId) const {
|
void Scheduler::stopSurface(SurfaceId surfaceId) const {
|
||||||
SystraceSection s("Scheduler::stopSurface");
|
SystraceSection s("Scheduler::stopSurface");
|
||||||
|
|
||||||
|
// Note, we have to do in inside `visit` function while the Shadow Tree
|
||||||
|
// is still being registered.
|
||||||
uiManager_->getShadowTreeRegistry().visit(
|
uiManager_->getShadowTreeRegistry().visit(
|
||||||
surfaceId, [](const ShadowTree &shadowTree) {
|
surfaceId, [](ShadowTree const &shadowTree) {
|
||||||
// As part of stopping the Surface, we have to commit an empty tree.
|
// As part of stopping a Surface, we need to properly destroy all
|
||||||
return shadowTree.tryCommit([&](const SharedRootShadowNode
|
// mounted views, so we need to commit an empty tree to trigger all
|
||||||
&oldRootShadowNode) {
|
// side-effects that will perform that.
|
||||||
return std::make_shared<RootShadowNode>(
|
shadowTree.commitEmptyTree();
|
||||||
*oldRootShadowNode,
|
|
||||||
ShadowNodeFragment{
|
|
||||||
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
|
|
||||||
/* .surfaceId = */
|
|
||||||
ShadowNodeFragment::surfaceIdPlaceholder(),
|
|
||||||
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
|
|
||||||
/* .eventEmitter = */
|
|
||||||
ShadowNodeFragment::eventEmitterPlaceholder(),
|
|
||||||
/* .children = */
|
|
||||||
ShadowNode::emptySharedShadowNodeSharedList(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
auto shadowTree = uiManager_->getShadowTreeRegistry().remove(surfaceId);
|
// Waiting for all concurrent commits to be finished and unregistering the
|
||||||
shadowTree->setDelegate(nullptr);
|
// `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_;
|
auto uiManager = uiManager_;
|
||||||
runtimeExecutor_([=](jsi::Runtime &runtime) {
|
runtimeExecutor_([=](jsi::Runtime &runtime) {
|
||||||
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {
|
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче