diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index ff2d44a47cc..b98e44e1880 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -44,10 +44,11 @@ using namespace facebook::react; RCTMountingManager *_mountingManager; // Thread-safe. RCTSurfaceRegistry *_surfaceRegistry; // Thread-safe. - better::shared_mutex _schedulerMutex; - RCTScheduler *_Nullable _scheduler; // Thread-safe. Pointer is protected by `_schedulerMutex`. - ContextContainer::Shared _contextContainer; // Protected by `_schedulerMutex`. - RuntimeExecutor _runtimeExecutor; // Protected by `_schedulerMutex`. + std::mutex _schedulerAccessMutex; + std::mutex _schedulerLifeCycleMutex; + RCTScheduler *_Nullable _scheduler; // Thread-safe. Pointer is protected by `_schedulerAccessMutex`. + ContextContainer::Shared _contextContainer; // Protected by `_schedulerLifeCycleMutex`. + RuntimeExecutor _runtimeExecutor; // Protected by `_schedulerLifeCycleMutex`. better::shared_mutex _observerListMutex; NSMutableArray> *_observers; @@ -74,27 +75,33 @@ using namespace facebook::react; return self; } +- (RCTScheduler *_Nullable)_scheduler +{ + std::lock_guard lock(_schedulerAccessMutex); + return _scheduler; +} + - (ContextContainer::Shared)contextContainer { - std::shared_lock lock(_schedulerMutex); + std::lock_guard lock(_schedulerLifeCycleMutex); return _contextContainer; } - (void)setContextContainer:(ContextContainer::Shared)contextContainer { - std::unique_lock lock(_schedulerMutex); + std::lock_guard lock(_schedulerLifeCycleMutex); _contextContainer = contextContainer; } - (RuntimeExecutor)runtimeExecutor { - std::shared_lock lock(_schedulerMutex); + std::lock_guard lock(_schedulerLifeCycleMutex); return _runtimeExecutor; } - (void)setRuntimeExecutor:(RuntimeExecutor)runtimeExecutor { - std::unique_lock lock(_schedulerMutex); + std::lock_guard lock(_schedulerLifeCycleMutex); _runtimeExecutor = runtimeExecutor; } @@ -102,28 +109,29 @@ using namespace facebook::react; - (void)registerSurface:(RCTFabricSurface *)surface { - std::shared_lock lock(_schedulerMutex); + RCTScheduler *scheduler = [self _scheduler]; [_surfaceRegistry registerSurface:surface]; - if (_scheduler) { - [self _startSurface:surface]; + if (scheduler) { + [self _startSurface:surface scheduler:scheduler]; } } - (void)unregisterSurface:(RCTFabricSurface *)surface { - std::shared_lock lock(_schedulerMutex); - if (_scheduler) { - [self _stopSurface:surface]; + RCTScheduler *scheduler = [self _scheduler]; + if (scheduler) { + [self _stopSurface:surface scheduler:scheduler]; } [_surfaceRegistry unregisterSurface:surface]; } - (void)setProps:(NSDictionary *)props surface:(RCTFabricSurface *)surface { - std::shared_lock lock(_schedulerMutex); - // This implementation is suboptimal indeed but still better than nothing for now. - [self _stopSurface:surface]; - [self _startSurface:surface]; + RCTScheduler *scheduler = [self _scheduler]; + if (scheduler) { + [self _stopSurface:surface scheduler:scheduler]; + [self _startSurface:surface scheduler:scheduler]; + } } - (RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag @@ -135,31 +143,42 @@ using namespace facebook::react; maximumSize:(CGSize)maximumSize surface:(RCTFabricSurface *)surface { - std::shared_lock lock(_schedulerMutex); + RCTScheduler *scheduler = [self _scheduler]; + if (!scheduler) { + return minimumSize; + } LayoutContext layoutContext = {.pointScaleFactor = RCTScreenScale()}; LayoutConstraints layoutConstraints = {.minimumSize = RCTSizeFromCGSize(minimumSize), .maximumSize = RCTSizeFromCGSize(maximumSize), .layoutDirection = RCTLayoutDirection([[RCTI18nUtil sharedInstance] isRTL])}; - return [_scheduler measureSurfaceWithLayoutConstraints:layoutConstraints - layoutContext:layoutContext - surfaceId:surface.rootTag]; + return [scheduler measureSurfaceWithLayoutConstraints:layoutConstraints + layoutContext:layoutContext + surfaceId:surface.rootTag]; } - (void)setMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize surface:(RCTFabricSurface *)surface { - std::shared_lock lock(_schedulerMutex); + RCTScheduler *scheduler = [self _scheduler]; + if (!scheduler) { + return; + } + LayoutContext layoutContext = {.pointScaleFactor = RCTScreenScale()}; LayoutConstraints layoutConstraints = {.minimumSize = RCTSizeFromCGSize(minimumSize), .maximumSize = RCTSizeFromCGSize(maximumSize), .layoutDirection = RCTLayoutDirection([[RCTI18nUtil sharedInstance] isRTL])}; - [_scheduler constraintSurfaceLayoutWithLayoutConstraints:layoutConstraints - layoutContext:layoutContext - surfaceId:surface.rootTag]; + [scheduler constraintSurfaceLayoutWithLayoutConstraints:layoutConstraints + layoutContext:layoutContext + surfaceId:surface.rootTag]; } - (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props { - std::shared_lock lock(_schedulerMutex); + RCTScheduler *scheduler = [self _scheduler]; + if (!scheduler) { + return NO; + } + ReactTag tag = [reactTag integerValue]; UIView *componentView = [_mountingManager.componentViewRegistry findComponentViewWithTag:tag]; @@ -167,7 +186,7 @@ using namespace facebook::react; return NO; // This view probably isn't managed by Fabric } ComponentHandle handle = [[componentView class] componentDescriptorProvider].handle; - auto *componentDescriptor = [_scheduler findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN:handle]; + auto *componentDescriptor = [scheduler findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN:handle]; if (!componentDescriptor) { return YES; @@ -179,7 +198,12 @@ using namespace facebook::react; - (BOOL)synchronouslyWaitSurface:(RCTFabricSurface *)surface timeout:(NSTimeInterval)timeout { - auto mountingCoordinator = [_scheduler mountingCoordinatorWithSurfaceId:surface.rootTag]; + RCTScheduler *scheduler = [self _scheduler]; + if (!scheduler) { + return NO; + } + + auto mountingCoordinator = [scheduler mountingCoordinatorWithSurfaceId:surface.rootTag]; if (!mountingCoordinator->waitForTransaction(std::chrono::duration(timeout))) { return NO; @@ -192,28 +216,44 @@ using namespace facebook::react; - (BOOL)suspend { - std::unique_lock lock(_schedulerMutex); + std::lock_guard lock(_schedulerLifeCycleMutex); - if (!_scheduler) { - return NO; + RCTScheduler *scheduler; + { + std::lock_guard accessLock(_schedulerAccessMutex); + + if (!_scheduler) { + return NO; + } + scheduler = _scheduler; + _scheduler = nil; } - [self _stopAllSurfaces]; - _scheduler = nil; + [self _stopAllSurfacesWithScheduler:scheduler]; return YES; } - (BOOL)resume { - std::unique_lock lock(_schedulerMutex); + std::lock_guard lock(_schedulerLifeCycleMutex); - if (_scheduler) { - return NO; + RCTScheduler *scheduler; + { + std::lock_guard accessLock(_schedulerAccessMutex); + + if (_scheduler) { + return NO; + } + scheduler = [self _createScheduler]; } - _scheduler = [self _createScheduler]; - [self _startAllSurfaces]; + [self _startAllSurfacesWithScheduler:scheduler]; + + { + std::lock_guard accessLock(_schedulerAccessMutex); + _scheduler = scheduler; + } return YES; } @@ -250,7 +290,7 @@ using namespace facebook::react; return scheduler; } -- (void)_startSurface:(RCTFabricSurface *)surface +- (void)_startSurface:(RCTFabricSurface *)surface scheduler:(RCTScheduler *)scheduler { RCTMountingManager *mountingManager = _mountingManager; RCTExecuteOnMainQueue(^{ @@ -264,16 +304,16 @@ using namespace facebook::react; .maximumSize = RCTSizeFromCGSize(surface.maximumSize), .layoutDirection = RCTLayoutDirection([[RCTI18nUtil sharedInstance] isRTL])}; - [_scheduler startSurfaceWithSurfaceId:surface.rootTag - moduleName:surface.moduleName - initialProps:surface.properties - layoutConstraints:layoutConstraints - layoutContext:layoutContext]; + [scheduler startSurfaceWithSurfaceId:surface.rootTag + moduleName:surface.moduleName + initialProps:surface.properties + layoutConstraints:layoutConstraints + layoutContext:layoutContext]; } -- (void)_stopSurface:(RCTFabricSurface *)surface +- (void)_stopSurface:(RCTFabricSurface *)surface scheduler:(RCTScheduler *)scheduler { - [_scheduler stopSurfaceWithSurfaceId:surface.rootTag]; + [scheduler stopSurfaceWithSurfaceId:surface.rootTag]; RCTMountingManager *mountingManager = _mountingManager; RCTExecuteOnMainQueue(^{ @@ -287,20 +327,20 @@ using namespace facebook::react; [surface _unsetStage:(RCTSurfaceStagePrepared | RCTSurfaceStageMounted)]; } -- (void)_startAllSurfaces +- (void)_startAllSurfacesWithScheduler:(RCTScheduler *)scheduler { [_surfaceRegistry enumerateWithBlock:^(NSEnumerator *enumerator) { for (RCTFabricSurface *surface in enumerator) { - [self _startSurface:surface]; + [self _startSurface:surface scheduler:scheduler]; } }]; } -- (void)_stopAllSurfaces +- (void)_stopAllSurfacesWithScheduler:(RCTScheduler *)scheduler { [_surfaceRegistry enumerateWithBlock:^(NSEnumerator *enumerator) { for (RCTFabricSurface *surface in enumerator) { - [self _stopSurface:surface]; + [self _stopSurface:surface scheduler:scheduler]; } }]; }