Cancel quickly if dependent operation fails

pull/1/head
Michael Kirk 7 years ago
parent 90214ae578
commit eb4c62593b

@ -5,7 +5,7 @@
import Foundation import Foundation
@objc(OWSContactDiscoveryOperation) @objc(OWSContactDiscoveryOperation)
class ContactDiscoveryOperation: OWSOperation { class ContactDiscoveryOperation: OWSOperation, LegacyContactDiscoveryBatchOperationDelegate {
let batchSize = 2048 let batchSize = 2048
let recipientIdsToLookup: [String] let recipientIdsToLookup: [String]
@ -23,6 +23,7 @@ class ContactDiscoveryOperation: OWSOperation {
Logger.debug("\(logTag) in \(#function) with recipientIdsToLookup: \(recipientIdsToLookup.count)") Logger.debug("\(logTag) in \(#function) with recipientIdsToLookup: \(recipientIdsToLookup.count)")
for batchIds in recipientIdsToLookup.chunked(by: batchSize) { for batchIds in recipientIdsToLookup.chunked(by: batchSize) {
let batchOperation = LegacyContactDiscoveryBatchOperation(recipientIdsToLookup: batchIds) let batchOperation = LegacyContactDiscoveryBatchOperation(recipientIdsToLookup: batchIds)
batchOperation.delegate = self
self.addDependency(batchOperation) self.addDependency(batchOperation)
} }
} }
@ -44,11 +45,24 @@ class ContactDiscoveryOperation: OWSOperation {
self.reportSuccess() self.reportSuccess()
} }
// MARK: LegacyContactDiscoveryBatchOperationDelegate
func contactDiscoverBatchOperation(_ contactDiscoverBatchOperation: LegacyContactDiscoveryBatchOperation, didFailWithError error: Error) {
Logger.debug("\(logTag) in \(#function) canceling self and all dependencies.")
self.dependencies.forEach { $0.cancel() }
self.cancel()
}
}
protocol LegacyContactDiscoveryBatchOperationDelegate: class {
func contactDiscoverBatchOperation(_ contactDiscoverBatchOperation: LegacyContactDiscoveryBatchOperation, didFailWithError error: Error)
} }
class LegacyContactDiscoveryBatchOperation: OWSOperation { class LegacyContactDiscoveryBatchOperation: OWSOperation {
var registeredRecipientIds: Set<String> var registeredRecipientIds: Set<String>
weak var delegate: LegacyContactDiscoveryBatchOperationDelegate?
private let recipientIdsToLookup: [String] private let recipientIdsToLookup: [String]
private var networkManager: TSNetworkManager { private var networkManager: TSNetworkManager {
@ -72,10 +86,17 @@ class LegacyContactDiscoveryBatchOperation: OWSOperation {
override func run() { override func run() {
Logger.debug("\(logTag) in \(#function)") Logger.debug("\(logTag) in \(#function)")
guard !isCancelled else {
Logger.info("\(logTag) in \(#function) no work to do, since we were canceled")
self.reportCancelled()
return
}
var phoneNumbersByHashes: [String: String] = [:] var phoneNumbersByHashes: [String: String] = [:]
for recipientId in recipientIdsToLookup { for recipientId in recipientIdsToLookup {
let hash = Cryptography.truncatedSHA1Base64EncodedWithoutPadding(recipientId) let hash = Cryptography.truncatedSHA1Base64EncodedWithoutPadding(recipientId)
assert(phoneNumbersByHashes[hash] == nil)
phoneNumbersByHashes[hash] = recipientId phoneNumbersByHashes[hash] = recipientId
} }
@ -121,6 +142,11 @@ class LegacyContactDiscoveryBatchOperation: OWSOperation {
CDSFeedbackOperation.operationQueue.addOperations([newCDSBatchOperation, cdsFeedbackOperation], waitUntilFinished: false) CDSFeedbackOperation.operationQueue.addOperations([newCDSBatchOperation, cdsFeedbackOperation], waitUntilFinished: false)
} }
// Called at most one time.
override func didFail(error: Error) {
self.delegate?.contactDiscoverBatchOperation(self, didFailWithError: error)
}
// MARK: Private Helpers // MARK: Private Helpers
private func parse(response: Any?, phoneNumbersByHashes: [String: String]) throws -> Set<String> { private func parse(response: Any?, phoneNumbersByHashes: [String: String]) throws -> Set<String> {

@ -42,17 +42,27 @@ typedef NS_ENUM(NSInteger, OWSOperationState) {
// Called at most one time. // Called at most one time.
- (void)didSucceed; - (void)didSucceed;
// Called at most one time.
- (void)didCancel;
// Called at most one time, once retry is no longer possible. // Called at most one time, once retry is no longer possible.
- (void)didFailWithError:(NSError *)error NS_SWIFT_NAME(didFail(error:)); - (void)didFailWithError:(NSError *)error NS_SWIFT_NAME(didFail(error:));
#pragma mark - Success/Error - Do Not Override #pragma mark - Success/Error - Do Not Override
// Complete the operation successfully. // Report that the operation completed successfully.
// Should be called at most once per operation instance. //
// You must ensure that `run` cannot fail after calling `reportSuccess`. // Each invocation of `run` must make exactly one call to one of: `reportSuccess`, `reportCancelled`, or `reportError:`
- (void)reportSuccess; - (void)reportSuccess;
// Should be called at most once per `run`. // Call this when you abort before completion due to being cancelled.
//
// Each invocation of `run` must make exactly one call to one of: `reportSuccess`, `reportCancelled`, or `reportError:`
- (void)reportCancelled;
// Report that the operation failed to complete due to an error.
//
// Each invocation of `run` must make exactly one call to one of: `reportSuccess`, `reportCancelled`, or `reportError:`
// You must ensure that `run` cannot succeed after calling `reportError`, e.g. generally you'll write something like // You must ensure that `run` cannot succeed after calling `reportError`, e.g. generally you'll write something like
// this: // this:
// //

@ -81,6 +81,13 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished";
// Override in subclass if necessary // Override in subclass if necessary
} }
// Called at most one time.
- (void)didCancel
{
// no-op
// Override in subclass if necessary
}
// Called at most one time, once retry is no longer possible. // Called at most one time, once retry is no longer possible.
- (void)didFailWithError:(NSError *)error - (void)didFailWithError:(NSError *)error
{ {
@ -113,6 +120,14 @@ NSString *const OWSOperationKeyIsFinished = @"isFinished";
[self markAsComplete]; [self markAsComplete];
} }
// These methods are not intended to be subclassed
- (void)reportCancelled
{
DDLogDebug(@"%@ cancelled.", self.logTag);
[self didCancel];
[self markAsComplete];
}
- (void)reportError:(NSError *)error - (void)reportError:(NSError *)error
{ {
DDLogDebug(@"%@ reportError: %@, fatal?: %d, retryable?: %d, remainingRetries: %lu", DDLogDebug(@"%@ reportError: %@, fatal?: %d, retryable?: %d, remainingRetries: %lu",

Loading…
Cancel
Save