166 lines
5.4 KiB
Objective-C
166 lines
5.4 KiB
Objective-C
// 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
|