ShadowTree.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include "ShadowTree.h"
  8. #include <react/components/root/RootComponentDescriptor.h>
  9. #include <react/components/view/ViewShadowNode.h>
  10. #include <react/core/LayoutContext.h>
  11. #include <react/core/LayoutPrimitives.h>
  12. #include <react/debug/SystraceSection.h>
  13. #include <react/mounting/MountingTelemetry.h>
  14. #include <react/mounting/ShadowTreeRevision.h>
  15. #include <react/mounting/ShadowViewMutation.h>
  16. #include "ShadowTreeDelegate.h"
  17. #include "TreeStateReconciliation.h"
  18. namespace facebook {
  19. namespace react {
  20. static void updateMountedFlag(
  21. const SharedShadowNodeList &oldChildren,
  22. const SharedShadowNodeList &newChildren) {
  23. // This is a simplified version of Diffing algorithm that only updates
  24. // `mounted` flag on `ShadowNode`s. The algorithm sets "mounted" flag before
  25. // "unmounted" to allow `ShadowNode` detect a situation where the node was
  26. // remounted.
  27. if (&oldChildren == &newChildren) {
  28. // Lists are identical, nothing to do.
  29. return;
  30. }
  31. if (oldChildren.size() == 0 && newChildren.size() == 0) {
  32. // Both lists are empty, nothing to do.
  33. return;
  34. }
  35. int index;
  36. // Stage 1: Mount and unmount "updated" children.
  37. for (index = 0; index < oldChildren.size() && index < newChildren.size();
  38. index++) {
  39. const auto &oldChild = oldChildren[index];
  40. const auto &newChild = newChildren[index];
  41. if (oldChild == newChild) {
  42. // Nodes are identical, skipping the subtree.
  43. continue;
  44. }
  45. if (!ShadowNode::sameFamily(*oldChild, *newChild)) {
  46. // Totally different nodes, updating is impossible.
  47. break;
  48. }
  49. newChild->setMounted(true);
  50. oldChild->setMounted(false);
  51. updateMountedFlag(oldChild->getChildren(), newChild->getChildren());
  52. }
  53. int lastIndexAfterFirstStage = index;
  54. // State 2: Mount new children.
  55. for (index = lastIndexAfterFirstStage; index < newChildren.size(); index++) {
  56. const auto &newChild = newChildren[index];
  57. newChild->setMounted(true);
  58. updateMountedFlag({}, newChild->getChildren());
  59. }
  60. // State 3: Unmount old children.
  61. for (index = lastIndexAfterFirstStage; index < oldChildren.size(); index++) {
  62. const auto &oldChild = oldChildren[index];
  63. oldChild->setMounted(false);
  64. updateMountedFlag(oldChild->getChildren(), {});
  65. }
  66. }
  67. ShadowTree::ShadowTree(
  68. SurfaceId surfaceId,
  69. LayoutConstraints const &layoutConstraints,
  70. LayoutContext const &layoutContext,
  71. RootComponentDescriptor const &rootComponentDescriptor,
  72. ShadowTreeDelegate const &delegate)
  73. : surfaceId_(surfaceId), delegate_(delegate) {
  74. const auto noopEventEmitter = std::make_shared<const ViewEventEmitter>(
  75. nullptr, -1, std::shared_ptr<const EventDispatcher>());
  76. const auto props = std::make_shared<const RootProps>(
  77. *RootShadowNode::defaultSharedProps(), layoutConstraints, layoutContext);
  78. auto family = rootComponentDescriptor.createFamily(
  79. ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter},
  80. nullptr);
  81. rootShadowNode_ = std::static_pointer_cast<const RootShadowNode>(
  82. rootComponentDescriptor.createShadowNode(
  83. ShadowNodeFragment{
  84. /* .props = */ props,
  85. },
  86. family));
  87. mountingCoordinator_ = std::make_shared<MountingCoordinator const>(
  88. ShadowTreeRevision{rootShadowNode_, 0, {}});
  89. }
  90. ShadowTree::~ShadowTree() {
  91. mountingCoordinator_->revoke();
  92. }
  93. Tag ShadowTree::getSurfaceId() const {
  94. return surfaceId_;
  95. }
  96. MountingCoordinator::Shared ShadowTree::getMountingCoordinator() const {
  97. return mountingCoordinator_;
  98. }
  99. void ShadowTree::commit(
  100. ShadowTreeCommitTransaction transaction,
  101. bool enableStateReconciliation) const {
  102. SystraceSection s("ShadowTree::commit");
  103. int attempts = 0;
  104. while (true) {
  105. attempts++;
  106. if (tryCommit(transaction, enableStateReconciliation)) {
  107. return;
  108. }
  109. // After multiple attempts, we failed to commit the transaction.
  110. // Something internally went terribly wrong.
  111. assert(attempts < 1024);
  112. }
  113. }
  114. bool ShadowTree::tryCommit(
  115. ShadowTreeCommitTransaction transaction,
  116. bool enableStateReconciliation) const {
  117. SystraceSection s("ShadowTree::tryCommit");
  118. auto telemetry = MountingTelemetry{};
  119. telemetry.willCommit();
  120. RootShadowNode::Shared oldRootShadowNode;
  121. {
  122. // Reading `rootShadowNode_` in shared manner.
  123. std::shared_lock<better::shared_mutex> lock(commitMutex_);
  124. oldRootShadowNode = rootShadowNode_;
  125. }
  126. RootShadowNode::Unshared newRootShadowNode = transaction(oldRootShadowNode);
  127. if (!newRootShadowNode) {
  128. return false;
  129. }
  130. // Compare state revisions of old and new root
  131. // Children of the root node may be mutated in-place
  132. if (enableStateReconciliation) {
  133. UnsharedShadowNode reconciledNode =
  134. reconcileStateWithTree(newRootShadowNode.get(), oldRootShadowNode);
  135. if (reconciledNode != nullptr) {
  136. newRootShadowNode = std::make_shared<RootShadowNode>(
  137. *reconciledNode, ShadowNodeFragment{});
  138. }
  139. }
  140. // Layout nodes
  141. std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
  142. affectedLayoutableNodes.reserve(1024);
  143. telemetry.willLayout();
  144. newRootShadowNode->layoutIfNeeded(&affectedLayoutableNodes);
  145. telemetry.didLayout();
  146. // Seal the shadow node so it can no longer be mutated
  147. newRootShadowNode->sealRecursive();
  148. auto revisionNumber = ShadowTreeRevision::Number{};
  149. {
  150. // Updating `rootShadowNode_` in unique manner if it hasn't changed.
  151. std::unique_lock<better::shared_mutex> lock(commitMutex_);
  152. if (rootShadowNode_ != oldRootShadowNode) {
  153. return false;
  154. }
  155. rootShadowNode_ = newRootShadowNode;
  156. {
  157. std::lock_guard<std::mutex> dispatchLock(EventEmitter::DispatchMutex());
  158. updateMountedFlag(
  159. oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
  160. }
  161. revisionNumber_++;
  162. revisionNumber = revisionNumber_;
  163. }
  164. emitLayoutEvents(affectedLayoutableNodes);
  165. telemetry.didCommit();
  166. mountingCoordinator_->push(
  167. ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry});
  168. delegate_.shadowTreeDidFinishTransaction(*this, mountingCoordinator_);
  169. return true;
  170. }
  171. void ShadowTree::commitEmptyTree() const {
  172. commit(
  173. [](RootShadowNode::Shared const &oldRootShadowNode)
  174. -> RootShadowNode::Unshared {
  175. return std::make_shared<RootShadowNode>(
  176. *oldRootShadowNode,
  177. ShadowNodeFragment{
  178. /* .props = */ ShadowNodeFragment::propsPlaceholder(),
  179. /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
  180. });
  181. });
  182. }
  183. void ShadowTree::emitLayoutEvents(
  184. std::vector<LayoutableShadowNode const *> &affectedLayoutableNodes) const {
  185. SystraceSection s("ShadowTree::emitLayoutEvents");
  186. for (auto const *layoutableNode : affectedLayoutableNodes) {
  187. // Only instances of `ViewShadowNode` (and subclasses) are supported.
  188. auto const &viewShadowNode =
  189. static_cast<ViewShadowNode const &>(*layoutableNode);
  190. auto const &viewEventEmitter = static_cast<ViewEventEmitter const &>(
  191. *viewShadowNode.getEventEmitter());
  192. // Checking if the `onLayout` event was requested for the particular Shadow
  193. // Node.
  194. auto const &viewProps =
  195. static_cast<ViewProps const &>(*viewShadowNode.getProps());
  196. if (!viewProps.onLayout) {
  197. continue;
  198. }
  199. viewEventEmitter.onLayout(layoutableNode->getLayoutMetrics());
  200. }
  201. }
  202. } // namespace react
  203. } // namespace facebook