Skip to content

Commit b8ca677

Browse files
sherginfacebook-github-bot
authored andcommitted
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
1 parent 28a5f12 commit b8ca677

File tree

3 files changed

+36
-32
lines changed

3 files changed

+36
-32
lines changed

ReactCommon/fabric/mounting/ShadowTree.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,6 @@ ShadowTree::ShadowTree(
103103
}
104104

105105
ShadowTree::~ShadowTree() {
106-
commit(
107-
[](const SharedRootShadowNode &oldRootShadowNode) {
108-
return std::make_shared<RootShadowNode>(
109-
*oldRootShadowNode,
110-
ShadowNodeFragment{
111-
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
112-
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
113-
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
114-
/* .eventEmitter = */
115-
ShadowNodeFragment::eventEmitterPlaceholder(),
116-
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
117-
});
118-
});
119106
mountingCoordinator_->revoke();
120107
}
121108

@@ -206,6 +193,23 @@ bool ShadowTree::tryCommit(ShadowTreeCommitTransaction transaction) const {
206193
return true;
207194
}
208195

196+
void ShadowTree::commitEmptyTree() const {
197+
commit(
198+
[](const SharedRootShadowNode &oldRootShadowNode)
199+
-> UnsharedRootShadowNode {
200+
return std::make_shared<RootShadowNode>(
201+
*oldRootShadowNode,
202+
ShadowNodeFragment{
203+
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
204+
/* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(),
205+
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
206+
/* .eventEmitter = */
207+
ShadowNodeFragment::eventEmitterPlaceholder(),
208+
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
209+
});
210+
});
211+
}
212+
209213
void ShadowTree::emitLayoutEvents(
210214
std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
211215
SystraceSection s("ShadowTree::emitLayoutEvents");

ReactCommon/fabric/mounting/ShadowTree.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ class ShadowTree final {
5858
*/
5959
void commit(ShadowTreeCommitTransaction transaction) const;
6060

61+
/*
62+
* Commit an empty tree (a new `RootShadowNode` with no children).
63+
*/
64+
void commitEmptyTree() const;
65+
6166
#pragma mark - Delegate
6267

6368
/*

ReactCommon/fabric/uimanager/Scheduler.cpp

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -151,29 +151,24 @@ void Scheduler::renderTemplateToSurface(
151151
void Scheduler::stopSurface(SurfaceId surfaceId) const {
152152
SystraceSection s("Scheduler::stopSurface");
153153

154+
// Note, we have to do in inside `visit` function while the Shadow Tree
155+
// is still being registered.
154156
uiManager_->getShadowTreeRegistry().visit(
155-
surfaceId, [](const ShadowTree &shadowTree) {
156-
// As part of stopping the Surface, we have to commit an empty tree.
157-
return shadowTree.tryCommit([&](const SharedRootShadowNode
158-
&oldRootShadowNode) {
159-
return std::make_shared<RootShadowNode>(
160-
*oldRootShadowNode,
161-
ShadowNodeFragment{
162-
/* .tag = */ ShadowNodeFragment::tagPlaceholder(),
163-
/* .surfaceId = */
164-
ShadowNodeFragment::surfaceIdPlaceholder(),
165-
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
166-
/* .eventEmitter = */
167-
ShadowNodeFragment::eventEmitterPlaceholder(),
168-
/* .children = */
169-
ShadowNode::emptySharedShadowNodeSharedList(),
170-
});
171-
});
157+
surfaceId, [](ShadowTree const &shadowTree) {
158+
// As part of stopping a Surface, we need to properly destroy all
159+
// mounted views, so we need to commit an empty tree to trigger all
160+
// side-effects that will perform that.
161+
shadowTree.commitEmptyTree();
172162
});
173163

174-
auto shadowTree = uiManager_->getShadowTreeRegistry().remove(surfaceId);
175-
shadowTree->setDelegate(nullptr);
164+
// Waiting for all concurrent commits to be finished and unregistering the
165+
// `ShadowTree`.
166+
uiManager_->getShadowTreeRegistry().remove(surfaceId);
176167

168+
// We execute JavaScript/React part of the process at the very end to minimize
169+
// any visible side-effects of stopping the Surface. Any possible commits from
170+
// the JavaScript side will not be able to reference a `ShadowTree` and will
171+
// fail silently.
177172
auto uiManager = uiManager_;
178173
runtimeExecutor_([=](jsi::Runtime &runtime) {
179174
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {

0 commit comments

Comments
 (0)