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_);
|
||||
}
|
||||
|
||||
void ShadowNode::setMounted(bool mounted) const {
|
||||
eventEmitter_->setEnabled(mounted);
|
||||
}
|
||||
|
||||
bool ShadowNode::constructAncestorPath(
|
||||
const ShadowNode &ancestorShadowNode,
|
||||
std::vector<std::reference_wrapper<const ShadowNode>> &ancestors) const {
|
||||
|
|
|
@ -101,6 +101,13 @@ class ShadowNode : public virtual Sealable,
|
|||
*/
|
||||
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.
|
||||
* The list starts from the parent node and ends with the given ancestor node.
|
||||
|
|
|
@ -16,6 +16,65 @@
|
|||
namespace facebook {
|
||||
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(
|
||||
SurfaceId surfaceId,
|
||||
const LayoutConstraints &layoutConstraints,
|
||||
|
@ -90,7 +149,12 @@ bool ShadowTree::commit(
|
|||
|
||||
rootShadowNode_ = newRootShadowNode;
|
||||
|
||||
toggleEventEmitters(mutations);
|
||||
{
|
||||
std::lock_guard<std::mutex> dispatchLock(EventEmitter::DispatchMutex());
|
||||
|
||||
updateMountedFlag(
|
||||
oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
void ShadowTree::setDelegate(ShadowTreeDelegate const *delegate) {
|
||||
|
|
|
@ -70,7 +70,6 @@ class ShadowTree final {
|
|||
const LayoutConstraints &layoutConstraints,
|
||||
const LayoutContext &layoutContext) const;
|
||||
|
||||
void toggleEventEmitters(const ShadowViewMutationList &mutations) const;
|
||||
void emitLayoutEvents(const ShadowViewMutationList &mutations) const;
|
||||
|
||||
const SurfaceId surfaceId_;
|
||||
|
|
Loading…
Reference in New Issue