Fabric: Introducing MountingTransaction

Summary:
`MountingTransaction` encapsulates all artifacts of `ShadowTree` commit, particularly list of mutations and meta-data.
We will rely on this heavily in the coming diffs.

Reviewed By: JoshuaGross

Differential Revision: D15021795

fbshipit-source-id: 811da7afd7b929a34a81aa66566193d46bbc34f8
This commit is contained in:
Valentin Shergin 2019-04-20 10:49:53 -07:00 коммит произвёл Facebook Github Bot
Родитель c257a57e8e
Коммит af0daaf583
16 изменённых файлов: 188 добавлений и 67 удалений

Просмотреть файл

@ -11,8 +11,8 @@
#import <React/RCTPrimitives.h>
#import <react/core/ComponentDescriptor.h>
#import <react/core/ReactPrimitives.h>
#import <react/mounting/MountingTransaction.h>
#import <react/mounting/ShadowView.h>
#import <react/mounting/ShadowViewMutation.h>
NS_ASSUME_NONNULL_BEGIN
@ -27,10 +27,10 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) RCTComponentViewRegistry *componentViewRegistry;
/**
* Schedule mutations to be performed on the main thread.
* Schedule a mounting transaction to be performed on the main thread.
* Can be called from any thread.
*/
- (void)scheduleMutations:(facebook::react::ShadowViewMutationList const &)mutations rootTag:(ReactTag)rootTag;
- (void)scheduleTransaction:(facebook::react::MountingTransaction &&)mountingTransaction;
/**
* Suggests preliminary creation of a component view of given type.

Просмотреть файл

@ -211,23 +211,23 @@ static void RNPerformMountInstructions(ShadowViewMutationList const &mutations,
return self;
}
- (void)scheduleMutations:(ShadowViewMutationList const &)mutations rootTag:(ReactTag)rootTag
- (void)scheduleTransaction:(facebook::react::MountingTransaction &&)mountingTransaction;
{
if (RCTIsMainQueue()) {
// Already on the proper thread, so:
// * No need to do a thread jump;
// * No need to do expensive copy of all mutations;
// * No need to allocate a block.
[self mountMutations:mutations rootTag:rootTag];
[self mountMutations:mountingTransaction.getMutations() rootTag:mountingTransaction.getSurfaceId()];
return;
}
// We need a non-reference for `mutations` to allow copy semantic.
auto mutationsCopy = mutations;
// We need a non-reference for `mountingTransaction` to allow copy semantic.
__block auto mountingTransactionCopy = MountingTransaction{mountingTransaction};
RCTExecuteOnMainQueue(^{
RCTAssertMainQueue();
[self mountMutations:mutationsCopy rootTag:rootTag];
[self mountMutations:mountingTransactionCopy.getMutations() rootTag:mountingTransactionCopy.getSurfaceId()];
});
}

Просмотреть файл

@ -12,6 +12,7 @@
#import <react/core/ComponentDescriptor.h>
#import <react/core/LayoutConstraints.h>
#import <react/core/LayoutContext.h>
#import <react/mounting/MountingTransaction.h>
#import <react/mounting/ShadowViewMutation.h>
#import <react/uimanager/ComponentDescriptorFactory.h>
#import <react/utils/ContextContainer.h>
@ -25,8 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
@protocol RCTSchedulerDelegate
- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList const &)mutations
rootTag:(ReactTag)rootTag;
- (void)schedulerDidFinishTransaction:(facebook::react::MountingTransaction &&)mountingTransaction;
- (void)schedulerOptimisticallyCreateComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle;

Просмотреть файл

@ -22,14 +22,10 @@ class SchedulerDelegateProxy : public SchedulerDelegate {
public:
SchedulerDelegateProxy(void *scheduler) : scheduler_(scheduler) {}
void schedulerDidFinishTransaction(
Tag rootTag,
const ShadowViewMutationList &mutations,
const long commitStartTime,
const long layoutTime) override
void schedulerDidFinishTransaction(MountingTransaction &&mountingTransaction) override
{
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
[scheduler.delegate schedulerDidFinishTransaction:mutations rootTag:rootTag];
[scheduler.delegate schedulerDidFinishTransaction:std::move(mountingTransaction)];
}
void schedulerDidRequestPreliminaryViewAllocation(SurfaceId surfaceId, const ShadowView &shadowView) override

Просмотреть файл

@ -309,14 +309,13 @@ using namespace facebook::react;
#pragma mark - RCTSchedulerDelegate
- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList const &)mutations
rootTag:(ReactTag)rootTag
- (void)schedulerDidFinishTransaction:(facebook::react::MountingTransaction &&)mountingTransaction
{
RCTFabricSurface *surface = [_surfaceRegistry surfaceForRootTag:rootTag];
RCTFabricSurface *surface = [_surfaceRegistry surfaceForRootTag:mountingTransaction.getSurfaceId()];
[surface _setStage:RCTSurfaceStagePrepared];
[_mountingManager scheduleMutations:mutations rootTag:rootTag];
[_mountingManager scheduleTransaction:std::move(mountingTransaction)];
}
- (void)schedulerOptimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle

Просмотреть файл

@ -329,11 +329,12 @@ local_ref<JMountItem::javaobject> createDeleteMountItem(
}
void Binding::schedulerDidFinishTransaction(
const Tag rootTag,
const ShadowViewMutationList& mutations,
const long commitStartTime,
const long layoutTime) {
MountingTransaction &&mountingTransaction) {
SystraceSection s("FabricUIManager::schedulerDidFinishTransaction");
auto telemetry = mountingTransaction.getTelemetry();
auto mutations = mountingTransaction.getMutations();
std::vector<local_ref<jobject>> queue;
// Upper bound estimation of mount items to be delivered to Java side.
int size = mutations.size() * 3 + 42;
@ -466,8 +467,8 @@ void Binding::schedulerDidFinishTransaction(
scheduleMountItems(
javaUIManager_,
batch.get(),
commitStartTime,
layoutTime,
telemetry.commitStartTime,
telemetry.layoutTime,
finishTransactionStartTime,
finishTransactionEndTime);
}

Просмотреть файл

@ -57,10 +57,7 @@ class Binding : public jni::HybridClass<Binding>, public SchedulerDelegate {
void stopSurface(jint surfaceId);
void schedulerDidFinishTransaction(
const Tag rootTag,
const ShadowViewMutationList& mutations,
const long commitStartTime,
const long layoutTime);
MountingTransaction &&mountingTransaction);
void schedulerDidRequestPreliminaryViewAllocation(
const SurfaceId surfaceId,

Просмотреть файл

@ -0,0 +1,47 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MountingTransaction.h"
namespace facebook {
namespace react {
using Telemetry = MountingTransaction::Telemetry;
using Revision = MountingTransaction::Revision;
MountingTransaction::MountingTransaction(
SurfaceId surfaceId,
Revision revision,
ShadowViewMutationList &&mutations,
Telemetry telemetry)
: surfaceId_(surfaceId),
revision_(revision),
mutations_(std::move(mutations)),
telemetry_(std::move(telemetry)) {}
ShadowViewMutationList const &MountingTransaction::getMutations() const & {
return mutations_;
}
ShadowViewMutationList MountingTransaction::getMutations() && {
return std::move(mutations_);
}
Telemetry const &MountingTransaction::getTelemetry() const {
return telemetry_;
}
SurfaceId MountingTransaction::getSurfaceId() const {
return surfaceId_;
}
Revision MountingTransaction::getRevision() const {
return revision_;
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -0,0 +1,95 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/mounting/ShadowViewMutation.h>
namespace facebook {
namespace react {
/*
* Encapsulates all artifacts of `ShadowTree` commit, particularly list of
* mutations and meta-data.
* Movable and copyable, but moving is strongly preferred.
* A moved-from object of this type has unspecified value and accessing that is
* UB.
*/
class MountingTransaction final {
public:
/*
* Revision grows continuously starting from `1`. Value `0` represents the
* state before the very first transaction happens.
*/
using Revision = int64_t;
/*
* Represent arbitrary telementry data that can be associated with the
* particular transaction.
*/
struct Telemetry final {
long commitStartTime{};
long layoutTime{};
};
/*
* Copying a list of `ShadowViewMutation` is expensive, so the constructor
* accepts it as rvalue reference to discourage copying.
*/
MountingTransaction(
SurfaceId surfaceId,
Revision revision,
ShadowViewMutationList &&mutations,
Telemetry telemetry);
/*
* Copy semantic.
* Copying of MountingTransaction is expensive, so copy-constructor is
* explicit and copy-assignment is deleted to prevent accidental copying.
*/
explicit MountingTransaction(const MountingTransaction &mountingTransaction) =
default;
MountingTransaction &operator=(const MountingTransaction &other) = delete;
/*
* Move semantic.
*/
MountingTransaction(MountingTransaction &&mountingTransaction) noexcept =
default;
MountingTransaction &operator=(MountingTransaction &&other) = default;
/*
* Returns a list of mutations that represent the transaction. The list can be
* empty (theoretically).
*/
ShadowViewMutationList const &getMutations() const &;
ShadowViewMutationList getMutations() &&;
/*
* Returns telemetry associated with this transaction.
*/
Telemetry const &getTelemetry() const;
/*
* Returns the id of the surface that the transaction belongs to.
*/
SurfaceId getSurfaceId() const;
/*
* Returns the revision of the ShadowTree that this transaction represents.
*/
Revision getRevision() const;
private:
SurfaceId surfaceId_;
Revision revision_;
ShadowViewMutationList mutations_;
Telemetry telemetry_;
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -127,15 +127,14 @@ Tag ShadowTree::getSurfaceId() const {
void ShadowTree::commit(
ShadowTreeCommitTransaction transaction,
long commitStartTime,
int *revision) const {
long commitStartTime) const {
SystraceSection s("ShadowTree::commit");
int attempts = 0;
while (true) {
attempts++;
if (tryCommit(transaction, commitStartTime, revision)) {
if (tryCommit(transaction, commitStartTime)) {
return;
}
@ -147,8 +146,7 @@ void ShadowTree::commit(
bool ShadowTree::tryCommit(
ShadowTreeCommitTransaction transaction,
long commitStartTime,
int *revision) const {
long commitStartTime) const {
SystraceSection s("ShadowTree::tryCommit");
SharedRootShadowNode oldRootShadowNode;
@ -170,6 +168,7 @@ bool ShadowTree::tryCommit(
layoutTime = getTime() - layoutTime;
newRootShadowNode->sealRecursive();
int revision;
auto mutations =
calculateShadowViewMutations(*oldRootShadowNode, *newRootShadowNode);
@ -190,13 +189,9 @@ bool ShadowTree::tryCommit(
oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
}
revision = revision_;
revision_++;
// Returning last revision if requested.
if (revision) {
*revision = revision_;
}
#ifdef RN_SHADOW_TREE_INTROSPECTION
stubViewTree_.mutate(mutations);
auto stubViewTree = stubViewTreeFromShadowNode(*rootShadowNode_);
@ -219,7 +214,11 @@ bool ShadowTree::tryCommit(
if (delegate_) {
delegate_->shadowTreeDidCommit(
*this, mutations, commitStartTime, layoutTime);
*this,
{surfaceId_,
revision,
std::move(mutations),
{commitStartTime, layoutTime}});
}
return true;

Просмотреть файл

@ -55,22 +55,16 @@ class ShadowTree final {
* Performs commit calling `transaction` function with a `oldRootShadowNode`
* and expecting a `newRootShadowNode` as a return value.
* The `transaction` function can abort commit returning `nullptr`.
* If a `revision` pointer is not null, the method will store there a
* contiguous revision number of the successfully performed transaction.
* Returns `true` if the operation finished successfully.
*/
bool tryCommit(
ShadowTreeCommitTransaction transaction,
long commitStartTime,
int *revision = nullptr) const;
bool tryCommit(ShadowTreeCommitTransaction transaction, long commitStartTime)
const;
/*
* Calls `tryCommit` in a loop until it finishes successfully.
*/
void commit(
ShadowTreeCommitTransaction transaction,
long commitStartTime,
int *revision = nullptr) const;
void commit(ShadowTreeCommitTransaction transaction, long commitStartTime)
const;
#pragma mark - Delegate

Просмотреть файл

@ -5,7 +5,7 @@
#pragma once
#include <react/mounting/ShadowViewMutation.h>
#include <react/mounting/MountingTransaction.h>
namespace facebook {
namespace react {
@ -22,9 +22,7 @@ class ShadowTreeDelegate {
*/
virtual void shadowTreeDidCommit(
const ShadowTree &shadowTree,
const ShadowViewMutationList &mutations,
long commitStartTime,
long layoutTime) const = 0;
MountingTransaction &&transaction) const = 0;
virtual ~ShadowTreeDelegate() noexcept = default;
};

Просмотреть файл

@ -23,6 +23,7 @@ namespace react {
struct ShadowView final {
ShadowView() = default;
ShadowView(const ShadowView &shadowView) = default;
ShadowView(ShadowView &&shadowView) noexcept = default;
~ShadowView(){};
@ -32,6 +33,7 @@ struct ShadowView final {
explicit ShadowView(const ShadowNode &shadowNode);
ShadowView &operator=(const ShadowView &other) = default;
ShadowView &operator=(ShadowView &&other) = default;
bool operator==(const ShadowView &rhs) const;
bool operator!=(const ShadowView &rhs) const;

Просмотреть файл

@ -242,14 +242,11 @@ SchedulerDelegate *Scheduler::getDelegate() const {
void Scheduler::shadowTreeDidCommit(
const ShadowTree &shadowTree,
const ShadowViewMutationList &mutations,
long commitStartTime,
long layoutTime) const {
MountingTransaction &&transaction) const {
SystraceSection s("Scheduler::shadowTreeDidCommit");
if (delegate_) {
delegate_->schedulerDidFinishTransaction(
shadowTree.getSurfaceId(), mutations, commitStartTime, layoutTime);
delegate_->schedulerDidFinishTransaction(std::move(transaction));
}
}

Просмотреть файл

@ -93,9 +93,7 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
void shadowTreeDidCommit(
const ShadowTree &shadowTree,
const ShadowViewMutationList &mutations,
long commitStartTime,
long layoutTime) const override;
MountingTransaction &&transaction) const override;
private:
SchedulerDelegate *delegate_;

Просмотреть файл

@ -9,6 +9,7 @@
#include <react/core/ReactPrimitives.h>
#include <react/core/ShadowNode.h>
#include <react/mounting/MountingTransaction.h>
#include <react/mounting/ShadowViewMutation.h>
namespace facebook {
@ -25,10 +26,7 @@ class SchedulerDelegate {
* to construct a new one.
*/
virtual void schedulerDidFinishTransaction(
Tag rootTag,
ShadowViewMutationList const &mutations,
const long commitStartTime,
const long layoutTime) = 0;
MountingTransaction &&mountingTransaction) = 0;
/*
* Called right after a new ShadowNode was created.