18
18
19
19
#include < random>
20
20
21
+ #import " Firestore/Source/Util/FSTClasses.h"
21
22
#import " Firestore/Source/Util/FSTDispatchQueue.h"
22
23
23
24
#include " Firestore/core/src/firebase/firestore/util/log.h"
@@ -33,6 +34,7 @@ @interface FSTExponentialBackoff ()
33
34
@property (nonatomic ) NSTimeInterval initialDelay;
34
35
@property (nonatomic ) NSTimeInterval maxDelay;
35
36
@property (nonatomic ) NSTimeInterval currentBase;
37
+ @property (nonatomic ) NSTimeInterval lastAttemptTime;
36
38
@property (nonatomic , strong , nullable ) FSTDelayedCallback *timerCallback;
37
39
@end
38
40
@@ -51,6 +53,7 @@ - (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue
51
53
_initialDelay = initialDelay;
52
54
_backoffFactor = backoffFactor;
53
55
_maxDelay = maxDelay;
56
+ _lastAttemptTime = [[NSDate date ] timeIntervalSince1970 ];
54
57
55
58
[self reset ];
56
59
}
@@ -69,13 +72,32 @@ - (void)backoffAndRunBlock:(void (^)(void))block {
69
72
[self cancel ];
70
73
71
74
// First schedule the block using the current base (which may be 0 and should be honored as such).
72
- NSTimeInterval delayWithJitter = _currentBase + [self jitterDelay ];
75
+ NSTimeInterval desiredDelayWithJitter = _currentBase + [self jitterDelay ];
76
+
77
+ // Guard against lastAttemptTime being in the future due to a clock change.
78
+ NSTimeInterval delaySoFar = MAX (0 , [[NSDate date ] timeIntervalSince1970 ] - self.lastAttemptTime );
79
+
80
+ // Guard against the backoff delay already being past.
81
+ NSTimeInterval remainingDelay = MAX (0 , desiredDelayWithJitter - delaySoFar);
82
+
73
83
if (_currentBase > 0 ) {
74
- LOG_DEBUG (" Backing off for %s seconds (base delay: %s seconds)" , delayWithJitter, _currentBase);
84
+ LOG_DEBUG (
85
+ " Backing off for %s seconds ("
86
+ " base delay: %s seconds, "
87
+ " delay with jitter: %s seconds, "
88
+ " last attempt: %s seconds ago)" ,
89
+ remainingDelay, _currentBase, desiredDelayWithJitter, delaySoFar);
75
90
}
76
91
77
- self.timerCallback =
78
- [self .dispatchQueue dispatchAfterDelay: delayWithJitter timerID: self .timerID block: block];
92
+ FSTWeakify (self);
93
+ self.timerCallback = [self .dispatchQueue dispatchAfterDelay: remainingDelay
94
+ timerID: self .timerID
95
+ block: ^{
96
+ FSTStrongify (self);
97
+ self.lastAttemptTime =
98
+ [[NSDate date ] timeIntervalSince1970 ];
99
+ block ();
100
+ }];
79
101
80
102
// Apply backoff factor to determine next delay and ensure it is within bounds.
81
103
_currentBase *= _backoffFactor;
0 commit comments