ensure operations run to completion on the PreKey operation queue

pull/1/head
Michael Kirk 7 years ago
parent 3df0e72eda
commit 619597cd61

@ -67,17 +67,6 @@ static const NSUInteger kMaxPrekeyUpdateFailureCount = 5;
[primaryStorage clearPrekeyUpdateFailureCount]; [primaryStorage clearPrekeyUpdateFailureCount];
} }
// We should never dispatch sync to this queue.
+ (dispatch_queue_t)prekeyQueue
{
static dispatch_once_t onceToken;
static dispatch_queue_t queue;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("org.whispersystems.signal.prekeyQueue", NULL);
});
return queue;
}
+ (NSOperationQueue *)operationQueue + (NSOperationQueue *)operationQueue
{ {
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
@ -100,41 +89,36 @@ static const NSUInteger kMaxPrekeyUpdateFailureCount = 5;
return; return;
} }
NSBlockOperation *checkIfPreKeyRefreshNecessaryOperation = [NSBlockOperation blockOperationWithBlock:^{ SSKRefreshPreKeysOperation *refreshOperation = [SSKRefreshPreKeysOperation new];
NSBlockOperation *checkIfRefreshNecessaryOperation = [NSBlockOperation blockOperationWithBlock:^{
BOOL shouldCheck = (lastPreKeyCheckTimestamp == nil BOOL shouldCheck = (lastPreKeyCheckTimestamp == nil
|| fabs([lastPreKeyCheckTimestamp timeIntervalSinceNow]) >= kPreKeyCheckFrequencySeconds); || fabs([lastPreKeyCheckTimestamp timeIntervalSinceNow]) >= kPreKeyCheckFrequencySeconds);
if (!shouldCheck) { if (!shouldCheck) {
return; [refreshOperation cancel];
} }
SSKRefreshPreKeysOperation *refreshOperation = [SSKRefreshPreKeysOperation new];
// Run inline instead of enqueuing to ensure we don't have multiple "check" operations
// that in turn enqueue multiple "refresh" operations.
[refreshOperation run];
}]; }];
[refreshOperation addDependency:checkIfRefreshNecessaryOperation];
NSBlockOperation *checkIfSignedRotationNecessaryOperation = [NSBlockOperation blockOperationWithBlock:^{
SSKRotateSignedPreKeyOperation *rotationOperation = [SSKRotateSignedPreKeyOperation new];
NSBlockOperation *checkIfRotationNecessaryOperation = [NSBlockOperation blockOperationWithBlock:^{
OWSPrimaryStorage *primaryStorage = [OWSPrimaryStorage sharedManager]; OWSPrimaryStorage *primaryStorage = [OWSPrimaryStorage sharedManager];
SignedPreKeyRecord *_Nullable signedPreKey = [primaryStorage currentSignedPreKey]; SignedPreKeyRecord *_Nullable signedPreKey = [primaryStorage currentSignedPreKey];
BOOL shouldCheck BOOL shouldCheck
= !signedPreKey || fabs(signedPreKey.generatedAt.timeIntervalSinceNow) >= kSignedPreKeyRotationTime; = !signedPreKey || fabs(signedPreKey.generatedAt.timeIntervalSinceNow) >= kSignedPreKeyRotationTime;
if (!shouldCheck) { if (!shouldCheck) {
return; [rotationOperation cancel];
} }
SSKRotateSignedPreKeyOperation *rotationOperation = [SSKRotateSignedPreKeyOperation new];
// Run inline instead of enqueuing to ensure we don't have multiple "check" operations
// that in turn enqueue multiple "refresh" operations.
[rotationOperation run];
}]; }];
[rotationOperation addDependency:checkIfRotationNecessaryOperation];
// Order matters here - if we rotated *before* refreshing, we'd risk uploading
// two SPK's in a row since RefreshPreKeysOperation can also upload a new SPK.
[checkIfRotationNecessaryOperation addDependency:refreshOperation];
// Order matters here. NSArray<NSOperation *> *operations =
// We add the PreKeyRefresh first - if it *does* upload new keys, it'll upload a new SignedPreKey @[ checkIfRefreshNecessaryOperation, refreshOperation, checkIfRotationNecessaryOperation, rotationOperation ];
// In that case our SPK rotation will be a no-op. [self.operationQueue addOperations:operations waitUntilFinished:NO];
[self.operationQueue
addOperations:@[ checkIfPreKeyRefreshNecessaryOperation, checkIfSignedRotationNecessaryOperation ]
waitUntilFinished:NO];
} }
+ (void)createPreKeysWithSuccess:(void (^)(void))successHandler failure:(void (^)(NSError *error))failureHandler + (void)createPreKeysWithSuccess:(void (^)(void))successHandler failure:(void (^)(NSError *error))failureHandler

@ -50,10 +50,12 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished";
{ {
for (NSOperation *dependency in self.dependencies) { for (NSOperation *dependency in self.dependencies) {
if (![dependency isKindOfClass:[OWSOperation class]]) { if (![dependency isKindOfClass:[OWSOperation class]]) {
NSString *errorDescription = // If you want an operation to have a cascading failure, it must
[NSString stringWithFormat:@"%@ unknown dependency: %@", self.logTag, dependency.class]; // subclass OWSOperation.
//
return OWSErrorMakeAssertionError(errorDescription); // Native operations like NSOperation or NSBlockOperation don't
// don't support this feature.
continue;
} }
OWSOperation *dependentOperation = (OWSOperation *)dependency; OWSOperation *dependentOperation = (OWSOperation *)dependency;
@ -107,6 +109,11 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished";
return; return;
} }
if (self.isCancelled) {
[self reportCancelled];
return;
}
[self run]; [self run];
} }

Loading…
Cancel
Save