diff --git a/ios/REANodesManager.m b/ios/REANodesManager.m index fda5bb2b8f6..b02f3e355c8 100644 --- a/ios/REANodesManager.m +++ b/ios/REANodesManager.m @@ -92,10 +92,71 @@ - (void)runSyncUIUpdatesWithObserver:(id)observer @end -@interface REANodesManager () +#ifndef RCT_NEW_ARCH_ENABLED +@interface REASyncUpdateObserver : NSObject @end +@implementation REASyncUpdateObserver { + volatile void (^_mounting)(void); + volatile BOOL _waitTimedOut; + dispatch_semaphore_t _semaphore; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _mounting = nil; + _waitTimedOut = NO; + _semaphore = dispatch_semaphore_create(0); + } + return self; +} + +- (void)dealloc +{ + RCTAssert(_mounting == nil, @"Mouting block was set but never executed. This may lead to UI inconsistencies"); +} + +- (void)unblockUIThread +{ + RCTAssertUIManagerQueue(); + dispatch_semaphore_signal(_semaphore); +} + +- (void)waitAndMountWithTimeout:(NSTimeInterval)timeout +{ + RCTAssertMainQueue(); + long result = dispatch_semaphore_wait(_semaphore, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC)); + if (result != 0) { + @synchronized(self) { + _waitTimedOut = YES; + } + } + if (_mounting) { + _mounting(); + _mounting = nil; + } +} + +- (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block +{ + RCTAssertUIManagerQueue(); + @synchronized(self) { + if (_waitTimedOut) { + return NO; + } else { + _mounting = block; + return YES; + } + } +} + +@end + +#endif + @implementation REANodesManager { NSMutableDictionary *_nodes; NSMapTable *_eventMapping; @@ -108,9 +169,6 @@ @implementation REANodesManager { NSMutableArray *_operationsInBatch; BOOL _tryRunBatchUpdatesSynchronously; REAEventHandler _eventHandler; - volatile void (^_mounting)(void); - NSObject *_syncLayoutUpdatesWaitLock; - volatile BOOL _syncLayoutUpdatesWaitTimedOut; NSMutableDictionary *_componentUpdateBuffer; volatile atomic_bool _shouldFlushUpdateBuffer; NSMutableDictionary *_viewRegistry; @@ -130,7 +188,6 @@ - (instancetype)initWithModule:(REAModule *)reanimatedModule uiManager:(RCTUIMan _operationsInBatch = [NSMutableArray new]; _componentUpdateBuffer = [NSMutableDictionary new]; _viewRegistry = [_uiManager valueForKey:@"_viewRegistry"]; - _syncLayoutUpdatesWaitLock = [NSObject new]; _shouldFlushUpdateBuffer = false; } @@ -232,19 +289,6 @@ - (void)onAnimationFrame:(CADisplayLink *)displayLink } } -- (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block -{ - RCTAssert(_mounting == nil, @"Mouting block is expected to not be set"); - @synchronized(_syncLayoutUpdatesWaitLock) { - if (_syncLayoutUpdatesWaitTimedOut) { - return NO; - } else { - _mounting = block; - return YES; - } - } -} - - (void)performOperations { if (_wantRunUpdates) { @@ -258,8 +302,7 @@ - (void)performOperations _tryRunBatchUpdatesSynchronously = NO; __weak typeof(self) weakSelf = self; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - _syncLayoutUpdatesWaitTimedOut = NO; + REASyncUpdateObserver *syncUpdateObserver = [REASyncUpdateObserver new]; RCTExecuteOnUIManagerQueue(^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { @@ -268,7 +311,7 @@ - (void)performOperations BOOL canUpdateSynchronously = trySynchronously && ![strongSelf.uiManager hasEnqueuedUICommands]; if (!canUpdateSynchronously) { - dispatch_semaphore_signal(semaphore); + [syncUpdateObserver unblockUIThread]; } for (int i = 0; i < copiedOperationsQueue.count; i++) { @@ -277,7 +320,7 @@ - (void)performOperations if (canUpdateSynchronously) { [strongSelf.uiManager runSyncUIUpdatesWithObserver:strongSelf]; - dispatch_semaphore_signal(semaphore); + [syncUpdateObserver unblockUIThread]; } // In case canUpdateSynchronously=true we still have to send uiManagerWillPerformMounting event // to observers because some components (e.g. TextInput) update their UIViews only on that event. @@ -288,17 +331,7 @@ - (void)performOperations // from CADisplayLink but it is easier to hardcode it for the time being. // The reason why we use frame duration here is that if takes longer than one frame to complete layout tasks // there is no point of synchronizing layout with the UI interaction as we get that one frame delay anyways. - long result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC)); - if (result != 0) { - @synchronized(_syncLayoutUpdatesWaitLock) { - _syncLayoutUpdatesWaitTimedOut = YES; - } - } - } - - if (_mounting) { - _mounting(); - _mounting = nil; + [syncUpdateObserver waitAndMountWithTimeout:0.016]; } } _wantRunUpdates = NO;