Fabric: ShadowTree::synchronize and reliable constraintLayout

Summary:
New `ShadowTree::synchronize` method allows to perform operations on ShadowTree without a risk of an unsuccessful commit. To make it happen, the `commitMutex_` is now recursive and `synchronize` acquires it before calling the callback.
Using that we finally can implement reliable `constraintLayout`.

Reviewed By: mdvacca

Differential Revision: D10174281

fbshipit-source-id: 9864ebb5343d40e2da205272a834710f0ab730db
This commit is contained in:
Valentin Shergin 2018-10-09 16:25:03 -07:00 committed by Facebook Github Bot
parent b9850844a5
commit b8947c459f
8 changed files with 31 additions and 16 deletions

View File

@ -49,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN
layoutContext:(facebook::react::LayoutContext)layoutContext
surfaceId:(facebook::react::SurfaceId)surfaceId;
- (BOOL)constraintSurfaceLayoutWithLayoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints
- (void)constraintSurfaceLayoutWithLayoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints
layoutContext:(facebook::react::LayoutContext)layoutContext
surfaceId:(facebook::react::SurfaceId)surfaceId;

View File

@ -80,11 +80,11 @@ private:
return RCTCGSizeFromSize(_scheduler->measureSurface(surfaceId, layoutConstraints, layoutContext));
}
- (BOOL)constraintSurfaceLayoutWithLayoutConstraints:(LayoutConstraints)layoutConstraints
- (void)constraintSurfaceLayoutWithLayoutConstraints:(LayoutConstraints)layoutConstraints
layoutContext:(LayoutContext)layoutContext
surfaceId:(SurfaceId)surfaceId
{
return _scheduler->constraintSurfaceLayout(surfaceId, layoutConstraints, layoutContext);
_scheduler->constraintSurfaceLayout(surfaceId, layoutConstraints, layoutContext);
}
@end

View File

@ -57,9 +57,8 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Sets `minimumSize` and `maximumSize` layout constraints for the Surface.
* Returns `YES` if the operation finished successfully.
*/
- (BOOL)setMinimumSize:(CGSize)minimumSize
- (void)setMinimumSize:(CGSize)minimumSize
maximumSize:(CGSize)maximumSize
surface:(RCTFabricSurface *)surface;

View File

@ -119,7 +119,7 @@ using namespace facebook::react;
surfaceId:surface.rootTag];
}
- (BOOL)setMinimumSize:(CGSize)minimumSize
- (void)setMinimumSize:(CGSize)minimumSize
maximumSize:(CGSize)maximumSize
surface:(RCTFabricSurface *)surface
{
@ -129,9 +129,9 @@ using namespace facebook::react;
layoutConstraints.minimumSize = RCTSizeFromCGSize(minimumSize);
layoutConstraints.maximumSize = RCTSizeFromCGSize(maximumSize);
return [self._scheduler constraintSurfaceLayoutWithLayoutConstraints:layoutConstraints
layoutContext:layoutContext
surfaceId:surface.rootTag];
[self._scheduler constraintSurfaceLayoutWithLayoutConstraints:layoutConstraints
layoutContext:layoutContext
surfaceId:surface.rootTag];
}
#pragma mark - Private

View File

@ -94,7 +94,7 @@ Size Scheduler::measureSurface(
return shadowTree->measure(layoutConstraints, layoutContext);
}
bool Scheduler::constraintSurfaceLayout(
void Scheduler::constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext
@ -102,7 +102,9 @@ bool Scheduler::constraintSurfaceLayout(
std::lock_guard<std::mutex> lock(mutex_);
const auto &shadowTree = shadowTreeRegistry_.at(surfaceId);
assert(shadowTree);
return shadowTree->constraintLayout(layoutConstraints, layoutContext);
shadowTree->synchronize([&]() {
shadowTree->constraintLayout(layoutConstraints, layoutContext);
});
}
#pragma mark - Delegate

View File

@ -53,10 +53,9 @@ public:
* The user interface will be relaid out as a result. The operation will be
* performed synchronously (including mounting) if the method is called
* on the main thread.
* Returns `true` if the operation finished successfully.
* Can be called from any thread.
*/
bool constraintSurfaceLayout(
void constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext

View File

@ -39,10 +39,15 @@ Tag ShadowTree::getRootTag() const {
}
SharedRootShadowNode ShadowTree::getRootShadowNode() const {
std::lock_guard<std::mutex> lock(commitMutex_);
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
return rootShadowNode_;
}
void ShadowTree::synchronize(std::function<void(void)> function) const {
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
function();
}
#pragma mark - Layout
Size ShadowTree::measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
@ -109,7 +114,7 @@ bool ShadowTree::commit(
const SharedRootShadowNode &newRootShadowNode,
const ShadowViewMutationList &mutations
) const {
std::lock_guard<std::mutex> lock(commitMutex_);
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
if (oldRootShadowNode != rootShadowNode_) {
return false;

View File

@ -38,6 +38,16 @@ public:
*/
Tag getRootTag() const;
/*
* Synchronously runs `function` when `commitMutex_` is acquired.
* It is useful in cases when transactional consistency and/or successful
* commit are required. E.g. you might want to run `measure` and
* `constraintLayout` as part of a single congious transaction.
* Use this only if it is necessary. All public methods of the class are
* already thread-safe.
*/
void synchronize(std::function<void(void)> function) const;
#pragma mark - Layout
/*
@ -93,7 +103,7 @@ private:
const Tag rootTag_;
mutable SharedRootShadowNode rootShadowNode_; // Protected by `commitMutex_`.
ShadowTreeDelegate const *delegate_;
mutable std::mutex commitMutex_;
mutable std::recursive_mutex commitMutex_;
};
} // namespace react