diff --git a/SignalServiceKit/src/Util/OWSOperation.m b/SignalServiceKit/src/Util/OWSOperation.m index df6daf11f..444d9c886 100644 --- a/SignalServiceKit/src/Util/OWSOperation.m +++ b/SignalServiceKit/src/Util/OWSOperation.m @@ -18,8 +18,9 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished"; @property (nullable) NSError *failingError; @property (atomic) OWSOperationState operationState; @property (nonatomic) OWSBackgroundTask *backgroundTask; + +// This property should only be accessed on the main queue. @property (nonatomic) NSTimer *_Nullable retryTimer; -@property (nonatomic, readonly) dispatch_queue_t retryTimerSerialQueue; @end @@ -34,7 +35,6 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished"; _operationState = OWSOperationStateNew; _backgroundTask = [OWSBackgroundTask backgroundTaskWithLabel:self.logTag]; - _retryTimerSerialQueue = dispatch_queue_create("SignalServiceKit.OWSOperation.retryTimer", DISPATCH_QUEUE_SERIAL); // Operations are not retryable by default. _remainingRetries = 0; @@ -131,16 +131,15 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished"; - (void)runAnyQueuedRetry { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - __block NSTimer *_Nullable retryTimer; - dispatch_sync(self.retryTimerSerialQueue, ^{ - retryTimer = self.retryTimer; - self.retryTimer = nil; - [retryTimer invalidate]; - }); + dispatch_async(dispatch_get_main_queue(), ^{ + NSTimer *_Nullable retryTimer = self.retryTimer; + self.retryTimer = nil; + [retryTimer invalidate]; if (retryTimer != nil) { - [self run]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self run]; + }); } else { OWSLogVerbose(@"not re-running since operation is already running."); } @@ -193,22 +192,21 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished"; self.remainingRetries--; dispatch_async(dispatch_get_main_queue(), ^{ - dispatch_sync(self.retryTimerSerialQueue, ^{ - OWSAssertDebug(self.retryTimer == nil); - [self.retryTimer invalidate]; - // The `scheduledTimerWith*` methods add the timer to the current thread's RunLoop. - // Since Operations typically run on a background thread, that would mean the background - // thread's RunLoop. However, the OS can spin down background threads if there's no work - // being done, so we run the risk of the timer's RunLoop being deallocated before it's - // fired. - // - // To ensure the timer's thread sticks around, we schedule it while on the main RunLoop. - self.retryTimer = [NSTimer weakScheduledTimerWithTimeInterval:self.retryInterval - target:self - selector:@selector(runAnyQueuedRetry) - userInfo:nil - repeats:NO]; - }); + OWSAssertDebug(self.retryTimer == nil); + [self.retryTimer invalidate]; + + // The `scheduledTimerWith*` methods add the timer to the current thread's RunLoop. + // Since Operations typically run on a background thread, that would mean the background + // thread's RunLoop. However, the OS can spin down background threads if there's no work + // being done, so we run the risk of the timer's RunLoop being deallocated before it's + // fired. + // + // To ensure the timer's thread sticks around, we schedule it while on the main RunLoop. + self.retryTimer = [NSTimer weakScheduledTimerWithTimeInterval:self.retryInterval + target:self + selector:@selector(runAnyQueuedRetry) + userInfo:nil + repeats:NO]; }); }