Fabric: Lock-free events 5/n: New implementation of `toggleEventEmitters` (which does not rely on mutations)
Summary: This diff implements a new algorithm that effectively marks `EventEmitter`s enabled or disabled. The previous implementation relied on a list of mutation instructions whereas the new one analyzes the shadow trees. The mutations-based approach didn't work well because mutations describe `ShadowView`s whereas some `ShadowNode`s are simply not views (like VirtualText), but we have to enable/disable them anyway. Reviewed By: sahrens Differential Revision: D13642594 fbshipit-source-id: 12169e11d5685e50bcd0d8c410498c594df744b4
This commit is contained in:
parent
cdb983d339
commit
7d630b92dc
|
@ -146,6 +146,10 @@ void ShadowNode::cloneChildrenIfShared() {
|
||||||
children_ = std::make_shared<SharedShadowNodeList>(*children_);
|
children_ = std::make_shared<SharedShadowNodeList>(*children_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShadowNode::setMounted(bool mounted) const {
|
||||||
|
eventEmitter_->setEnabled(mounted);
|
||||||
|
}
|
||||||
|
|
||||||
bool ShadowNode::constructAncestorPath(
|
bool ShadowNode::constructAncestorPath(
|
||||||
const ShadowNode &ancestorShadowNode,
|
const ShadowNode &ancestorShadowNode,
|
||||||
std::vector<std::reference_wrapper<const ShadowNode>> &ancestors) const {
|
std::vector<std::reference_wrapper<const ShadowNode>> &ancestors) const {
|
||||||
|
|
|
@ -101,6 +101,13 @@ class ShadowNode : public virtual Sealable,
|
||||||
*/
|
*/
|
||||||
void setLocalData(const SharedLocalData &localData);
|
void setLocalData(const SharedLocalData &localData);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Performs all side effects associated with mounting/unmounting in one place.
|
||||||
|
* This is not `virtual` on purpose, do not override this.
|
||||||
|
* `EventEmitter::DispatchMutex()` must be acquired before calling.
|
||||||
|
*/
|
||||||
|
void setMounted(bool mounted) const;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forms a list of all ancestors of the node relative to the given ancestor.
|
* Forms a list of all ancestors of the node relative to the given ancestor.
|
||||||
* The list starts from the parent node and ends with the given ancestor node.
|
* The list starts from the parent node and ends with the given ancestor node.
|
||||||
|
|
|
@ -16,6 +16,65 @@
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
namespace react {
|
||||||
|
|
||||||
|
static void updateMountedFlag(
|
||||||
|
const SharedShadowNodeList &oldChildren,
|
||||||
|
const SharedShadowNodeList &newChildren) {
|
||||||
|
// This is a simplified version of Diffing algorithm that only updates
|
||||||
|
// `mounted` flag on `ShadowNode`s. The algorithm sets "mounted" flag before
|
||||||
|
// "unmounted" to allow `ShadowNode` detect a situation where the node was
|
||||||
|
// remounted.
|
||||||
|
|
||||||
|
if (&oldChildren == &newChildren) {
|
||||||
|
// Lists are identical, nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldChildren.size() == 0 && newChildren.size() == 0) {
|
||||||
|
// Both lists are empty, nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index;
|
||||||
|
|
||||||
|
// Stage 1: Mount and unmount "updated" children.
|
||||||
|
for (index = 0; index < oldChildren.size() && index < newChildren.size();
|
||||||
|
index++) {
|
||||||
|
const auto &oldChild = oldChildren[index];
|
||||||
|
const auto &newChild = newChildren[index];
|
||||||
|
|
||||||
|
if (oldChild == newChild) {
|
||||||
|
// Nodes are identical, skipping the subtree.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldChild->getTag() != newChild->getTag()) {
|
||||||
|
// Totally different nodes, updating is impossible.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newChild->setMounted(true);
|
||||||
|
oldChild->setMounted(false);
|
||||||
|
|
||||||
|
updateMountedFlag(oldChild->getChildren(), newChild->getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastIndexAfterFirstStage = index;
|
||||||
|
|
||||||
|
// State 2: Mount new children.
|
||||||
|
for (index = lastIndexAfterFirstStage; index < newChildren.size(); index++) {
|
||||||
|
const auto &newChild = newChildren[index];
|
||||||
|
newChild->setMounted(true);
|
||||||
|
updateMountedFlag({}, newChild->getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
|
// State 3: Unmount old children.
|
||||||
|
for (index = lastIndexAfterFirstStage; index < oldChildren.size(); index++) {
|
||||||
|
const auto &oldChild = oldChildren[index];
|
||||||
|
oldChild->setMounted(false);
|
||||||
|
updateMountedFlag(oldChild->getChildren(), {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShadowTree::ShadowTree(
|
ShadowTree::ShadowTree(
|
||||||
SurfaceId surfaceId,
|
SurfaceId surfaceId,
|
||||||
const LayoutConstraints &layoutConstraints,
|
const LayoutConstraints &layoutConstraints,
|
||||||
|
@ -90,7 +149,12 @@ bool ShadowTree::commit(
|
||||||
|
|
||||||
rootShadowNode_ = newRootShadowNode;
|
rootShadowNode_ = newRootShadowNode;
|
||||||
|
|
||||||
toggleEventEmitters(mutations);
|
{
|
||||||
|
std::lock_guard<std::mutex> dispatchLock(EventEmitter::DispatchMutex());
|
||||||
|
|
||||||
|
updateMountedFlag(
|
||||||
|
oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
revision_++;
|
revision_++;
|
||||||
|
|
||||||
|
@ -153,23 +217,6 @@ void ShadowTree::emitLayoutEvents(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShadowTree::toggleEventEmitters(
|
|
||||||
const ShadowViewMutationList &mutations) const {
|
|
||||||
std::lock_guard<std::mutex> lock(EventEmitter::DispatchMutex());
|
|
||||||
|
|
||||||
for (const auto &mutation : mutations) {
|
|
||||||
if (mutation.type == ShadowViewMutation::Create) {
|
|
||||||
mutation.newChildShadowView.eventEmitter->setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &mutation : mutations) {
|
|
||||||
if (mutation.type == ShadowViewMutation::Delete) {
|
|
||||||
mutation.oldChildShadowView.eventEmitter->setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Delegate
|
#pragma mark - Delegate
|
||||||
|
|
||||||
void ShadowTree::setDelegate(ShadowTreeDelegate const *delegate) {
|
void ShadowTree::setDelegate(ShadowTreeDelegate const *delegate) {
|
||||||
|
|
|
@ -70,7 +70,6 @@ class ShadowTree final {
|
||||||
const LayoutConstraints &layoutConstraints,
|
const LayoutConstraints &layoutConstraints,
|
||||||
const LayoutContext &layoutContext) const;
|
const LayoutContext &layoutContext) const;
|
||||||
|
|
||||||
void toggleEventEmitters(const ShadowViewMutationList &mutations) const;
|
|
||||||
void emitLayoutEvents(const ShadowViewMutationList &mutations) const;
|
void emitLayoutEvents(const ShadowViewMutationList &mutations) const;
|
||||||
|
|
||||||
const SurfaceId surfaceId_;
|
const SurfaceId surfaceId_;
|
||||||
|
|
Loading…
Reference in New Issue