| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // 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 "Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.h"
- #import "Crashlytics/Shared/FIRCLSOperation/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
|