diff --git a/android/src/main/java/com/swmansion/reanimated/NodesManager.java b/android/src/main/java/com/swmansion/reanimated/NodesManager.java index 1d18851ec00..eaf7fd3c2c3 100644 --- a/android/src/main/java/com/swmansion/reanimated/NodesManager.java +++ b/android/src/main/java/com/swmansion/reanimated/NodesManager.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; @@ -218,13 +219,11 @@ public void runGuarded() { } }); if (trySynchronously) { - while (true) { - try { - semaphore.acquire(); - break; - } catch (InterruptedException e) { - // noop - } + try { + semaphore.tryAcquire(16, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // if the thread is interruped we just continue and let the layout update happen + // asynchronously } } } diff --git a/ios/REANodesManager.mm b/ios/REANodesManager.mm index da95472d254..1dc006d7588 100644 --- a/ios/REANodesManager.mm +++ b/ios/REANodesManager.mm @@ -96,6 +96,8 @@ @implementation REANodesManager { BOOL _tryRunBatchUpdatesSynchronously; REAEventHandler _eventHandler; volatile void (^_mounting)(void); + NSObject *_syncLayoutUpdatesWaitLock; + volatile BOOL _syncLayoutUpdatesWaitTimedOut; NSMutableDictionary *_componentUpdateBuffer; NSMutableDictionary *_viewRegistry; #ifdef RCT_NEW_ARCH_ENABLED @@ -123,6 +125,7 @@ - (nonnull instancetype)initWithModule:(REAModule *)reanimatedModule _operationsInBatch = [NSMutableDictionary new]; _componentUpdateBuffer = [NSMutableDictionary new]; _viewRegistry = [_uiManager valueForKey:@"_viewRegistry"]; + _syncLayoutUpdatesWaitLock = [NSObject new]; } _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onAnimationFrame:)]; @@ -241,8 +244,14 @@ - (void)onAnimationFrame:(CADisplayLink *)displayLink - (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block { RCTAssert(_mounting == nil, @"Mouting block is expected to not be set"); - _mounting = block; - return YES; + @synchronized(_syncLayoutUpdatesWaitLock) { + if (_syncLayoutUpdatesWaitTimedOut) { + return NO; + } else { + _mounting = block; + return YES; + } + } } - (void)performOperations @@ -260,6 +269,7 @@ - (void)performOperations __weak __typeof__(self) weakSelf = self; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + _syncLayoutUpdatesWaitTimedOut = NO; RCTExecuteOnUIManagerQueue(^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { @@ -276,7 +286,7 @@ - (void)performOperations } if (canUpdateSynchronously) { - [strongSelf.uiManager runSyncUIUpdatesWithObserver:self]; + [strongSelf.uiManager runSyncUIUpdatesWithObserver:strongSelf]; dispatch_semaphore_signal(semaphore); } // In case canUpdateSynchronously=true we still have to send uiManagerWillPerformMounting event @@ -284,7 +294,16 @@ - (void)performOperations [strongSelf.uiManager setNeedsLayout]; }); if (trySynchronously) { - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + // The 16ms timeout here aims to match the frame duration. It may make sense to read that parameter + // 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) {