Files
firebase-ios-sdk/Firebase/Database/Core/Utilities/FIRRetryHelper.m
T

140 lines
4.0 KiB
Objective-C
Raw Normal View History

2017-05-15 12:27:07 -07:00
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRRetryHelper.h"
#import "FUtilities.h"
2019-08-05 13:27:47 -07:00
#import <FirebaseCore/FIRLogger.h>
2017-05-15 12:27:07 -07:00
@interface FIRRetryHelperTask : NSObject
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong) void (^block)(void);
2017-05-15 12:27:07 -07:00
@end
@implementation FIRRetryHelperTask
2019-08-05 13:27:47 -07:00
- (instancetype)initWithBlock:(void (^)(void))block {
2017-05-15 12:27:07 -07:00
self = [super init];
if (self != nil) {
self->_block = [block copy];
}
return self;
}
2019-08-05 13:27:47 -07:00
- (BOOL)isCanceled {
2017-05-15 12:27:07 -07:00
return self.block == nil;
}
2019-08-05 13:27:47 -07:00
- (void)cancel {
2017-05-15 12:27:07 -07:00
self.block = nil;
}
2019-08-05 13:27:47 -07:00
- (void)execute {
2017-05-15 12:27:07 -07:00
if (self.block) {
self.block();
}
}
@end
@interface FIRRetryHelper ()
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong) dispatch_queue_t dispatchQueue;
@property(nonatomic) NSTimeInterval minRetryDelayAfterFailure;
@property(nonatomic) NSTimeInterval maxRetryDelay;
@property(nonatomic) double retryExponent;
@property(nonatomic) double jitterFactor;
2017-05-15 12:27:07 -07:00
2019-08-05 13:27:47 -07:00
@property(nonatomic) BOOL lastWasSuccess;
@property(nonatomic) NSTimeInterval currentRetryDelay;
2017-05-15 12:27:07 -07:00
2019-08-05 13:27:47 -07:00
@property(nonatomic, strong) FIRRetryHelperTask *scheduledRetry;
2017-05-15 12:27:07 -07:00
@end
@implementation FIRRetryHelper
2019-08-05 13:27:47 -07:00
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue
minRetryDelayAfterFailure:(NSTimeInterval)minRetryDelayAfterFailure
maxRetryDelay:(NSTimeInterval)maxRetryDelay
retryExponent:(double)retryExponent
jitterFactor:(double)jitterFactor {
2017-05-15 12:27:07 -07:00
self = [super init];
if (self != nil) {
self->_dispatchQueue = dispatchQueue;
self->_minRetryDelayAfterFailure = minRetryDelayAfterFailure;
self->_maxRetryDelay = maxRetryDelay;
self->_retryExponent = retryExponent;
self->_jitterFactor = jitterFactor;
self->_lastWasSuccess = YES;
}
return self;
}
2019-08-05 13:27:47 -07:00
- (void)retry:(void (^)(void))block {
2017-05-15 12:27:07 -07:00
if (self.scheduledRetry != nil) {
FFLog(@"I-RDB054001", @"Canceling existing retry attempt");
[self.scheduledRetry cancel];
self.scheduledRetry = nil;
}
NSTimeInterval delay;
if (self.lastWasSuccess) {
delay = 0;
} else {
if (self.currentRetryDelay == 0) {
self.currentRetryDelay = self.minRetryDelayAfterFailure;
} else {
2019-08-05 13:27:47 -07:00
NSTimeInterval newDelay =
(self.currentRetryDelay * self.retryExponent);
2017-05-15 12:27:07 -07:00
self.currentRetryDelay = MIN(newDelay, self.maxRetryDelay);
}
delay = ((1 - self.jitterFactor) * self.currentRetryDelay) +
2019-08-05 13:27:47 -07:00
(self.jitterFactor * self.currentRetryDelay *
[FUtilities randomDouble]);
2017-05-15 12:27:07 -07:00
FFLog(@"I-RDB054002", @"Scheduling retry in %fs", delay);
}
self.lastWasSuccess = NO;
FIRRetryHelperTask *task = [[FIRRetryHelperTask alloc] initWithBlock:block];
self.scheduledRetry = task;
2019-08-05 13:27:47 -07:00
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, (long long)(delay * NSEC_PER_SEC));
2017-05-15 12:27:07 -07:00
dispatch_after(popTime, self.dispatchQueue, ^{
2019-08-05 13:27:47 -07:00
if (![task isCanceled]) {
self.scheduledRetry = nil;
[task execute];
}
2017-05-15 12:27:07 -07:00
});
}
2019-08-05 13:27:47 -07:00
- (void)signalSuccess {
2017-05-15 12:27:07 -07:00
self.lastWasSuccess = YES;
self.currentRetryDelay = 0;
}
2019-08-05 13:27:47 -07:00
- (void)cancel {
2017-05-15 12:27:07 -07:00
if (self.scheduledRetry != nil) {
FFLog(@"I-RDB054003", @"Canceling existing retry attempt");
[self.scheduledRetry cancel];
self.scheduledRetry = nil;
} else {
FFLog(@"I-RDB054004", @"No existing retry attempt to cancel");
}
self.currentRetryDelay = 0;
}
@end