You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

165 lines
5.4 KiB

// Copyright 2019 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 "FIRCLSCompoundOperation.h"
#import "FIRCLSFABAsyncOperation_Private.h"
#define FIRCLS_DISPATCH_QUEUES_AS_OBJECTS OS_OBJECT_USE_OBJC_RETAIN_RELEASE
const NSUInteger FIRCLSCompoundOperationErrorCodeCancelled = UINT_MAX - 1;
const NSUInteger FIRCLSCompoundOperationErrorCodeSuboperationFailed = UINT_MAX - 2;
NSString *const FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors =
@"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error.user-info-key.underlying-"
@"errors";
static NSString *const FIRCLSCompoundOperationErrorDomain =
@"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error";
static char *const FIRCLSCompoundOperationCountingQueueLabel =
"com.google.firebase.crashlytics.FIRCLSCompoundOperation.dispatch-queue.counting-queue";
@interface FIRCLSCompoundOperation ()
@property(strong, nonatomic, readwrite) NSOperationQueue *compoundQueue;
@property(assign, nonatomic) NSUInteger completedOperations;
@property(strong, nonatomic) NSMutableArray *errors;
#if FIRCLS_DISPATCH_QUEUES_AS_OBJECTS
@property(strong, nonatomic) dispatch_queue_t countingQueue;
#else
@property(assign, nonatomic) dispatch_queue_t countingQueue;
#endif
@end
@implementation FIRCLSCompoundOperation
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
_compoundQueue = [[NSOperationQueue alloc] init];
_completedOperations = 0;
_errors = [NSMutableArray array];
_countingQueue =
dispatch_queue_create(FIRCLSCompoundOperationCountingQueueLabel, DISPATCH_QUEUE_SERIAL);
return self;
}
#if !FIRCLS_DISPATCH_QUEUES_AS_OBJECTS
- (void)dealloc {
if (_countingQueue) {
dispatch_release(_countingQueue);
}
}
#endif
- (void)main {
for (FIRCLSFABAsyncOperation *operation in self.operations) {
[self injectCompoundAsyncCompletionInOperation:operation];
[self injectCompoundSyncCompletionInOperation:operation];
[self.compoundQueue addOperation:operation];
}
}
- (void)cancel {
if (self.compoundQueue.operations.count > 0) {
[self.compoundQueue cancelAllOperations];
dispatch_sync(self.countingQueue, ^{
[self attemptCompoundCompletion];
});
} else {
for (NSOperation *operation in self.operations) {
[operation cancel];
}
// we have to add the operations to the queue in order for their isFinished property to be set
// to true.
[self.compoundQueue addOperations:self.operations waitUntilFinished:NO];
}
[super cancel];
}
- (void)injectCompoundAsyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation {
__weak FIRCLSCompoundOperation *weakSelf = self;
FIRCLSFABAsyncOperationCompletionBlock originalAsyncCompletion = [operation.asyncCompletion copy];
FIRCLSFABAsyncOperationCompletionBlock completion = ^(NSError *error) {
__strong FIRCLSCompoundOperation *strongSelf = weakSelf;
if (originalAsyncCompletion) {
dispatch_sync(strongSelf.countingQueue, ^{
originalAsyncCompletion(error);
});
}
[strongSelf updateCompletionCountsWithError:error];
};
operation.asyncCompletion = completion;
}
- (void)injectCompoundSyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation {
__weak FIRCLSCompoundOperation *weakSelf = self;
void (^originalSyncCompletion)(void) = [operation.completionBlock copy];
void (^completion)(void) = ^{
__strong FIRCLSCompoundOperation *strongSelf = weakSelf;
if (originalSyncCompletion) {
dispatch_sync(strongSelf.countingQueue, ^{
originalSyncCompletion();
});
}
dispatch_sync(strongSelf.countingQueue, ^{
[strongSelf attemptCompoundCompletion];
});
};
operation.completionBlock = completion;
}
- (void)updateCompletionCountsWithError:(NSError *)error {
dispatch_sync(self.countingQueue, ^{
if (!error) {
self.completedOperations += 1;
} else {
[self.errors addObject:error];
}
});
}
- (void)attemptCompoundCompletion {
if (self.isCancelled) {
[self finishWithError:[NSError errorWithDomain:FIRCLSCompoundOperationErrorDomain
code:FIRCLSCompoundOperationErrorCodeCancelled
userInfo:@{
NSLocalizedDescriptionKey : [NSString
stringWithFormat:@"%@ cancelled", self.name]
}]];
self.asyncCompletion = nil;
} else if (self.completedOperations + self.errors.count == self.operations.count) {
NSError *error = nil;
if (self.errors.count > 0) {
error = [NSError
errorWithDomain:FIRCLSCompoundOperationErrorDomain
code:FIRCLSCompoundOperationErrorCodeSuboperationFailed
userInfo:@{FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors : self.errors}];
}
[self finishWithError:error];
}
}
@end