Reverts "Timing: Fixes timer when app get into background (#24649)" (#27073)

Summary:
This PR reverts commit 338298417f that is causing https://github.com/facebook/react-native/issues/26696 #26995.
> app would be closed immediately after going to background on iOS 13.1/13.2 and was investigated by minhtc https://github.com/facebook/react-native/issues/26696#issuecomment-548056694. The commit that is being reverted is apparently causing the app to be closed immediately. This has to be reverted in master separately as the file differs there.

Similar PR for 0.61. branch https://github.com/facebook/react-native/pull/27065

## Changelog

[iOS] [Fixed] - Fix apps crashing on iOS 13.x when running timer in the background
Pull Request resolved: https://github.com/facebook/react-native/pull/27073

Test Plan: Try [this](338298417f (commitcomment-35745287)) snippet on iOS 13.1/13.2, the app should not crash anymore

Differential Revision: D18323679

Pulled By: cpojer

fbshipit-source-id: 3af7036a0e1d3811924e581c649b16e5a4667e83
This commit is contained in:
Radek Czemerys 2019-11-05 03:29:08 -08:00 коммит произвёл Facebook Github Bot
Родитель 1df8bd4932
Коммит e1d03b4cc0
1 изменённых файлов: 38 добавлений и 70 удалений

Просмотреть файл

@ -12,9 +12,9 @@
#import <React/RCTAssert.h> #import <React/RCTAssert.h>
#import <React/RCTBridge+Private.h> #import <React/RCTBridge+Private.h>
#import <React/RCTBridge.h> #import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTLog.h> #import <React/RCTLog.h>
#import <React/RCTUtils.h> #import <React/RCTUtils.h>
#import <React/RCTConvert.h>
#import "CoreModulesPlugins.h" #import "CoreModulesPlugins.h"
@ -75,8 +75,7 @@ static const NSTimeInterval kIdleCallbackFrameDeadline = 0.001;
@end @end
// NSTimer retains its target, insert this class to break potential retain cycles // NSTimer retains its target, insert this class to break potential retain cycles
@implementation _RCTTimingProxy @implementation _RCTTimingProxy {
{
__weak id _target; __weak id _target;
} }
@ -96,16 +95,14 @@ static const NSTimeInterval kIdleCallbackFrameDeadline = 0.001;
@end @end
@interface RCTTiming() <NativeTimingSpec> @interface RCTTiming () <NativeTimingSpec>
@end @end
@implementation RCTTiming @implementation RCTTiming {
{
NSMutableDictionary<NSNumber *, _RCTTimer *> *_timers; NSMutableDictionary<NSNumber *, _RCTTimer *> *_timers;
NSTimer *_sleepTimer; NSTimer *_sleepTimer;
BOOL _sendIdleEvents; BOOL _sendIdleEvents;
BOOL _inBackground; BOOL _inBackground;
UIBackgroundTaskIdentifier _backgroundTaskIdentifier;
id<RCTTimingDelegate> _timingDelegate; id<RCTTimingDelegate> _timingDelegate;
} }
@ -115,7 +112,7 @@ static const NSTimeInterval kIdleCallbackFrameDeadline = 0.001;
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE()
- (instancetype)initWithDelegate:(id<RCTTimingDelegate>) delegate - (instancetype)initWithDelegate:(id<RCTTimingDelegate>)delegate
{ {
if (self = [super init]) { if (self = [super init]) {
[self setup]; [self setup];
@ -136,19 +133,19 @@ RCT_EXPORT_MODULE()
_paused = YES; _paused = YES;
_timers = [NSMutableDictionary new]; _timers = [NSMutableDictionary new];
_inBackground = NO; _inBackground = NO;
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
for (NSString *name in @[UIApplicationWillResignActiveNotification, for (NSString *name in @[
UIApplicationDidEnterBackgroundNotification, UIApplicationWillResignActiveNotification,
UIApplicationWillTerminateNotification]) { UIApplicationDidEnterBackgroundNotification,
UIApplicationWillTerminateNotification
]) {
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidMoveToBackground) selector:@selector(appDidMoveToBackground)
name:name name:name
object:nil]; object:nil];
} }
for (NSString *name in @[UIApplicationDidBecomeActiveNotification, for (NSString *name in @[ UIApplicationDidBecomeActiveNotification, UIApplicationWillEnterForegroundNotification ]) {
UIApplicationWillEnterForegroundNotification]) {
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidMoveToForeground) selector:@selector(appDidMoveToForeground)
name:name name:name
@ -158,34 +155,9 @@ RCT_EXPORT_MODULE()
- (void)dealloc - (void)dealloc
{ {
[self markEndOfBackgroundTaskIfNeeded];
[_sleepTimer invalidate]; [_sleepTimer invalidate];
} }
- (void)markStartOfBackgroundTaskIfNeeded
{
if (_backgroundTaskIdentifier == UIBackgroundTaskInvalid) {
__weak RCTTiming *weakSelf = self;
// Marks the beginning of a new long-running background task. We can run the timer in the background.
_backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"rct.timing.gb.task" expirationHandler:^{
RCTTiming *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
// Mark the end of background task
[strongSelf markEndOfBackgroundTaskIfNeeded];
}];
}
}
- (void)markEndOfBackgroundTaskIfNeeded
{
if (_backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
}
- (dispatch_queue_t)methodQueue - (dispatch_queue_t)methodQueue
{ {
return RCTJSThread; return RCTJSThread;
@ -211,7 +183,6 @@ RCT_EXPORT_MODULE()
- (void)appDidMoveToForeground - (void)appDidMoveToForeground
{ {
[self markEndOfBackgroundTaskIfNeeded];
_inBackground = NO; _inBackground = NO;
[self startTimers]; [self startTimers];
} }
@ -246,7 +217,7 @@ RCT_EXPORT_MODULE()
- (BOOL)hasPendingTimers - (BOOL)hasPendingTimers
{ {
@synchronized (_timers) { @synchronized(_timers) {
return _sendIdleEvents || _timers.count > 0; return _sendIdleEvents || _timers.count > 0;
} }
} }
@ -256,7 +227,7 @@ RCT_EXPORT_MODULE()
NSDate *nextScheduledTarget = [NSDate distantFuture]; NSDate *nextScheduledTarget = [NSDate distantFuture];
NSMutableArray<_RCTTimer *> *timersToCall = [NSMutableArray new]; NSMutableArray<_RCTTimer *> *timersToCall = [NSMutableArray new];
NSDate *now = [NSDate date]; // compare all the timers to the same base time NSDate *now = [NSDate date]; // compare all the timers to the same base time
@synchronized (_timers) { @synchronized(_timers) {
for (_RCTTimer *timer in _timers.allValues) { for (_RCTTimer *timer in _timers.allValues) {
if ([timer shouldFire:now]) { if ([timer shouldFire:now]) {
[timersToCall addObject:timer]; [timersToCall addObject:timer];
@ -272,7 +243,7 @@ RCT_EXPORT_MODULE()
return [a.target compare:b.target]; return [a.target compare:b.target];
}] valueForKey:@"callbackID"]; }] valueForKey:@"callbackID"];
if (_bridge) { if (_bridge) {
[_bridge enqueueJSCall:@"JSTimers" method:@"callTimers" args:@[sortedTimers] completion:NULL]; [_bridge enqueueJSCall:@"JSTimers" method:@"callTimers" args:@[ sortedTimers ] completion:NULL];
} else { } else {
[_timingDelegate callTimers:sortedTimers]; [_timingDelegate callTimers:sortedTimers];
} }
@ -283,7 +254,7 @@ RCT_EXPORT_MODULE()
[timer reschedule]; [timer reschedule];
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target]; nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
} else { } else {
@synchronized (_timers) { @synchronized(_timers) {
[_timers removeObjectForKey:timer.callbackID]; [_timers removeObjectForKey:timer.callbackID];
} }
} }
@ -294,12 +265,11 @@ RCT_EXPORT_MODULE()
NSTimeInterval frameElapsed = currentTimestamp - update.timestamp; NSTimeInterval frameElapsed = currentTimestamp - update.timestamp;
if (kFrameDuration - frameElapsed >= kIdleCallbackFrameDeadline) { if (kFrameDuration - frameElapsed >= kIdleCallbackFrameDeadline) {
NSNumber *absoluteFrameStartMS = @((currentTimestamp - frameElapsed) * 1000); NSNumber *absoluteFrameStartMS = @((currentTimestamp - frameElapsed) * 1000);
if (_bridge){ if (_bridge) {
[_bridge enqueueJSCall:@"JSTimers" method:@"callIdleCallbacks" args:@[absoluteFrameStartMS] completion:NULL]; [_bridge enqueueJSCall:@"JSTimers" method:@"callIdleCallbacks" args:@[ absoluteFrameStartMS ] completion:NULL];
} else { } else {
[_timingDelegate callIdleCallbacks:absoluteFrameStartMS]; [_timingDelegate callIdleCallbacks:absoluteFrameStartMS];
} }
} }
} }
@ -307,12 +277,11 @@ RCT_EXPORT_MODULE()
// in response to this timer another timer is scheduled, we don't pause and unpause // in response to this timer another timer is scheduled, we don't pause and unpause
// the displaylink frivolously. // the displaylink frivolously.
NSUInteger timerCount; NSUInteger timerCount;
@synchronized (_timers) { @synchronized(_timers) {
timerCount = _timers.count; timerCount = _timers.count;
} }
if (_inBackground) { if (_inBackground) {
if (timerCount) { if (timerCount) {
[self markStartOfBackgroundTaskIfNeeded];
[self scheduleSleepTimer:nextScheduledTarget]; [self scheduleSleepTimer:nextScheduledTarget];
} }
} else if (!_sendIdleEvents && timersToCall.count == 0) { } else if (!_sendIdleEvents && timersToCall.count == 0) {
@ -331,13 +300,13 @@ RCT_EXPORT_MODULE()
- (void)scheduleSleepTimer:(NSDate *)sleepTarget - (void)scheduleSleepTimer:(NSDate *)sleepTarget
{ {
@synchronized (self) { @synchronized(self) {
if (!_sleepTimer || !_sleepTimer.valid) { if (!_sleepTimer || !_sleepTimer.valid) {
_sleepTimer = [[NSTimer alloc] initWithFireDate:sleepTarget _sleepTimer = [[NSTimer alloc] initWithFireDate:sleepTarget
interval:0 interval:0
target:[_RCTTimingProxy proxyWithTarget:self] target:[_RCTTimingProxy proxyWithTarget:self]
selector:@selector(timerDidFire) selector:@selector(timerDidFire)
userInfo:nil userInfo:nil
repeats:NO]; repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:_sleepTimer forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer:_sleepTimer forMode:NSDefaultRunLoopMode];
} else { } else {
@ -367,13 +336,14 @@ RCT_EXPORT_MODULE()
* calculating the timer's target time. We calculate this by passing in * calculating the timer's target time. We calculate this by passing in
* Date.now() from JS and then subtracting that from the current time here. * Date.now() from JS and then subtracting that from the current time here.
*/ */
RCT_EXPORT_METHOD(createTimer:(double)callbackID RCT_EXPORT_METHOD(createTimer
duration:(NSTimeInterval)jsDuration : (double)callbackID duration
jsSchedulingTime:(double)jsSchedulingTime : (NSTimeInterval)jsDuration jsSchedulingTime
repeats:(BOOL)repeats) : (double)jsSchedulingTime repeats
: (BOOL)repeats)
{ {
NSNumber *callbackIdObjc = [NSNumber numberWithDouble:callbackID]; NSNumber *callbackIdObjc = [NSNumber numberWithDouble:callbackID];
NSDate *schedulingTime = [RCTConvert NSDate:[NSNumber numberWithDouble: jsSchedulingTime]]; NSDate *schedulingTime = [RCTConvert NSDate:[NSNumber numberWithDouble:jsSchedulingTime]];
if (jsDuration == 0 && repeats == NO) { if (jsDuration == 0 && repeats == NO) {
// For super fast, one-off timers, just enqueue them immediately rather than waiting a frame. // For super fast, one-off timers, just enqueue them immediately rather than waiting a frame.
if (_bridge) { if (_bridge) {
@ -384,10 +354,7 @@ RCT_EXPORT_METHOD(createTimer:(double)callbackID
return; return;
} }
[self createTimerForNextFrame:callbackIdObjc [self createTimerForNextFrame:callbackIdObjc duration:jsDuration jsSchedulingTime:schedulingTime repeats:repeats];
duration:jsDuration
jsSchedulingTime:schedulingTime
repeats:repeats];
} }
/** /**
@ -410,12 +377,11 @@ RCT_EXPORT_METHOD(createTimer:(double)callbackID
interval:jsDuration interval:jsDuration
targetTime:targetTime targetTime:targetTime
repeats:repeats]; repeats:repeats];
@synchronized (_timers) { @synchronized(_timers) {
_timers[callbackID] = timer; _timers[callbackID] = timer;
} }
if (_inBackground) { if (_inBackground) {
[self markStartOfBackgroundTaskIfNeeded];
[self scheduleSleepTimer:timer.target]; [self scheduleSleepTimer:timer.target];
} else if (_paused) { } else if (_paused) {
if ([timer.target timeIntervalSinceNow] > kMinimumSleepInterval) { if ([timer.target timeIntervalSinceNow] > kMinimumSleepInterval) {
@ -426,9 +392,9 @@ RCT_EXPORT_METHOD(createTimer:(double)callbackID
} }
} }
RCT_EXPORT_METHOD(deleteTimer:(double)timerID) RCT_EXPORT_METHOD(deleteTimer : (double)timerID)
{ {
@synchronized (_timers) { @synchronized(_timers) {
[_timers removeObjectForKey:[NSNumber numberWithDouble:timerID]]; [_timers removeObjectForKey:[NSNumber numberWithDouble:timerID]];
} }
if (![self hasPendingTimers]) { if (![self hasPendingTimers]) {
@ -436,7 +402,7 @@ RCT_EXPORT_METHOD(deleteTimer:(double)timerID)
} }
} }
RCT_EXPORT_METHOD(setSendIdleEvents:(BOOL)sendIdleEvents) RCT_EXPORT_METHOD(setSendIdleEvents : (BOOL)sendIdleEvents)
{ {
_sendIdleEvents = sendIdleEvents; _sendIdleEvents = sendIdleEvents;
if (sendIdleEvents) { if (sendIdleEvents) {
@ -446,13 +412,15 @@ RCT_EXPORT_METHOD(setSendIdleEvents:(BOOL)sendIdleEvents)
} }
} }
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModuleWithJsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker - (std::shared_ptr<facebook::react::TurboModule>)getTurboModuleWithJsInvoker:
(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{ {
return std::make_shared<facebook::react::NativeTimingSpecJSI>(self, jsInvoker); return std::make_shared<facebook::react::NativeTimingSpecJSI>(self, jsInvoker);
} }
@end @end
Class RCTTimingCls(void) { Class RCTTimingCls(void)
{
return RCTTiming.class; return RCTTiming.class;
} }