Fabric: ShadowNode::children_ is now managed in copy-on-write manner

Summary:
@public
ShadowNode class is designed to share `props` and `children` objects between instances. Given that all *Props classes are immutable, it's very easy to share them and we do this from the day one. The `children_` collection is more tricky though because ShadowNode class has a couple of mutation methods. Previously, we dealt with it very simply by copying the whole vector in constructors, and that was far from optimal. Now we store a special flag that indicates that the children list is shared among nodes, and we clone this before the first mutation.
Sharing a `shared_ptr` should be much more efficient (cost of atomic refcount increment) than instantiating whole new collection (an allocation).

Reviewed By: mdvacca

Differential Revision: D8988386

fbshipit-source-id: cb2f6b2fccac70a35e070a1aa108d135f77cd041
This commit is contained in:
Valentin Shergin 2018-08-04 09:30:26 -07:00 committed by Facebook Github Bot
parent d74346b616
commit 938e1d51c4
2 changed files with 37 additions and 4 deletions

View File

@ -30,9 +30,14 @@ ShadowNode::ShadowNode(
rootTag_(fragment.rootTag),
props_(fragment.props),
eventEmitter_(fragment.eventEmitter),
children_(std::make_shared<SharedShadowNodeList>(*fragment.children)),
children_(fragment.children ?: emptySharedShadowNodeSharedList()),
cloneFunction_(cloneFunction),
revision_(1) {}
childrenAreShared_(true),
revision_(1) {
assert(props_);
assert(children_);
}
ShadowNode::ShadowNode(
const SharedShadowNode &sourceShadowNode,
@ -42,10 +47,15 @@ ShadowNode::ShadowNode(
rootTag_(fragment.rootTag ?: sourceShadowNode->rootTag_),
props_(fragment.props ?: sourceShadowNode->props_),
eventEmitter_(fragment.eventEmitter ?: sourceShadowNode->eventEmitter_),
children_(std::make_shared<SharedShadowNodeList>(*(fragment.children ?: sourceShadowNode->children_))),
children_(fragment.children ?: sourceShadowNode->children_),
localData_(fragment.localData ?: sourceShadowNode->localData_),
cloneFunction_(sourceShadowNode->cloneFunction_),
revision_(sourceShadowNode->revision_ + 1) {}
childrenAreShared_(true),
revision_(sourceShadowNode->revision_ + 1) {
assert(props_);
assert(children_);
}
UnsharedShadowNode ShadowNode::clone(const ShadowNodeFragment &fragment) const {
assert(cloneFunction_);
@ -97,6 +107,7 @@ void ShadowNode::sealRecursive() const {
void ShadowNode::appendChild(const SharedShadowNode &child) {
ensureUnsealed();
cloneChildrenIfShared();
auto nonConstChildren = std::const_pointer_cast<SharedShadowNodeList>(children_);
nonConstChildren->push_back(child);
}
@ -104,6 +115,8 @@ void ShadowNode::appendChild(const SharedShadowNode &child) {
void ShadowNode::replaceChild(const SharedShadowNode &oldChild, const SharedShadowNode &newChild, int suggestedIndex) {
ensureUnsealed();
cloneChildrenIfShared();
auto nonConstChildren = std::const_pointer_cast<SharedShadowNodeList>(children_);
if (suggestedIndex != -1 && suggestedIndex < nonConstChildren->size()) {
@ -121,6 +134,14 @@ void ShadowNode::setLocalData(const SharedLocalData &localData) {
localData_ = localData;
}
void ShadowNode::cloneChildrenIfShared() {
if (!childrenAreShared_) {
return;
}
childrenAreShared_ = false;
children_ = std::make_shared<SharedShadowNodeList>(*children_);
}
#pragma mark - Equality
bool ShadowNode::operator==(const ShadowNode& rhs) const {

View File

@ -137,12 +137,24 @@ protected:
private:
/*
* Clones the list of children (and creates a new `shared_ptr` to it) if
* `childrenAreShared_` flag is `true`.
*/
void cloneChildrenIfShared();
/*
* A reference to a cloning function that understands how to clone
* the specific type of ShadowNode.
*/
ShadowNodeCloneFunction cloneFunction_;
/*
* Indicates that `children` list is shared between nodes and need
* to be cloned before the first mutation.
*/
bool childrenAreShared_;
/*
* A number of the generation of the ShadowNode instance;
* is used and useful for debug-printing purposes *only*.