Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Removed locks && ensure everything runs on JS Thread #25164

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 38 additions & 47 deletions React/Modules/RCTTiming.m
Original file line number Diff line number Diff line change
Expand Up @@ -173,26 +173,32 @@ - (dispatch_queue_t)methodQueue

- (void)invalidate
{
[self stopTimers];
_bridge = nil;
[_bridge dispatchBlock:^{
[self stopTimers];
self->_bridge = nil;
} queue:RCTJSThread];
}

- (void)appDidMoveToBackground
{
// Deactivate the CADisplayLink while in the background.
[self stopTimers];
_inBackground = YES;

// Issue one final timer callback, which will schedule a
// background NSTimer, if needed.
[self didUpdateFrame:nil];
[_bridge dispatchBlock:^{
// Deactivate the CADisplayLink while in the background.
[self stopTimers];
self->_inBackground = YES;

// Issue one final timer callback, which will schedule a
// background NSTimer, if needed.
[self didUpdateFrame:nil];
} queue:RCTJSThread];
}

- (void)appDidMoveToForeground
{
[self markEndOfBackgroundTaskIfNeeded];
_inBackground = NO;
[self startTimers];
[_bridge dispatchBlock:^{
[self markEndOfBackgroundTaskIfNeeded];
self->_inBackground = NO;
[self startTimers];
} queue:RCTJSThread];
}

- (void)stopTimers
Expand Down Expand Up @@ -225,23 +231,19 @@ - (void)startTimers

- (BOOL)hasPendingTimers
{
@synchronized (_timers) {
return _sendIdleEvents || _timers.count > 0;
}
return _sendIdleEvents || _timers.count > 0;
}

- (void)didUpdateFrame:(RCTFrameUpdate *)update
{
NSDate *nextScheduledTarget = [NSDate distantFuture];
NSMutableArray<_RCTTimer *> *timersToCall = [NSMutableArray new];
NSDate *now = [NSDate date]; // compare all the timers to the same base time
@synchronized (_timers) {
for (_RCTTimer *timer in _timers.allValues) {
if ([timer shouldFire:now]) {
[timersToCall addObject:timer];
} else {
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
}
for (_RCTTimer *timer in _timers.allValues) {
if ([timer shouldFire:now]) {
[timersToCall addObject:timer];
} else {
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
}
}

Expand All @@ -261,9 +263,7 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
[timer reschedule];
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
} else {
@synchronized (_timers) {
[_timers removeObjectForKey:timer.callbackID];
}
[_timers removeObjectForKey:timer.callbackID];
}
}

Expand All @@ -282,10 +282,7 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
// Switch to a paused state only if we didn't call any timer this frame, so if
// in response to this timer another timer is scheduled, we don't pause and unpause
// the displaylink frivolously.
NSUInteger timerCount;
@synchronized (_timers) {
timerCount = _timers.count;
}
NSUInteger timerCount = _timers.count;
if (_inBackground) {
if (timerCount) {
[self markStartOfBackgroundTaskIfNeeded];
Expand All @@ -307,18 +304,16 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update

- (void)scheduleSleepTimer:(NSDate *)sleepTarget
{
@synchronized (self) {
if (!_sleepTimer || !_sleepTimer.valid) {
_sleepTimer = [[NSTimer alloc] initWithFireDate:sleepTarget
interval:0
target:[_RCTTimingProxy proxyWithTarget:self]
selector:@selector(timerDidFire)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:_sleepTimer forMode:NSDefaultRunLoopMode];
} else {
_sleepTimer.fireDate = [_sleepTimer.fireDate earlierDate:sleepTarget];
}
if (!_sleepTimer || !_sleepTimer.valid) {
_sleepTimer = [[NSTimer alloc] initWithFireDate:sleepTarget
interval:0
target:[_RCTTimingProxy proxyWithTarget:self]
selector:@selector(timerDidFire)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:_sleepTimer forMode:NSDefaultRunLoopMode];
} else {
_sleepTimer.fireDate = [_sleepTimer.fireDate earlierDate:sleepTarget];
}
}

Expand Down Expand Up @@ -362,9 +357,7 @@ - (void)timerDidFire
interval:jsDuration
targetTime:targetTime
repeats:repeats];
@synchronized (_timers) {
_timers[callbackID] = timer;
}
_timers[callbackID] = timer;

if (_inBackground) {
[self markStartOfBackgroundTaskIfNeeded];
Expand All @@ -380,9 +373,7 @@ - (void)timerDidFire

RCT_EXPORT_METHOD(deleteTimer:(nonnull NSNumber *)timerID)
{
@synchronized (_timers) {
[_timers removeObjectForKey:timerID];
}
[_timers removeObjectForKey:timerID];
if (![self hasPendingTimers]) {
[self stopTimers];
}
Expand Down