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

  1. // Copyright 2019 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FIRCLSCompoundOperation.h"
  15. #import "FIRCLSFABAsyncOperation_Private.h"
  16. #define FIRCLS_DISPATCH_QUEUES_AS_OBJECTS OS_OBJECT_USE_OBJC_RETAIN_RELEASE
  17. const NSUInteger FIRCLSCompoundOperationErrorCodeCancelled = UINT_MAX - 1;
  18. const NSUInteger FIRCLSCompoundOperationErrorCodeSuboperationFailed = UINT_MAX - 2;
  19. NSString *const FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors =
  20. @"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error.user-info-key.underlying-"
  21. @"errors";
  22. static NSString *const FIRCLSCompoundOperationErrorDomain =
  23. @"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error";
  24. static char *const FIRCLSCompoundOperationCountingQueueLabel =
  25. "com.google.firebase.crashlytics.FIRCLSCompoundOperation.dispatch-queue.counting-queue";
  26. @interface FIRCLSCompoundOperation ()
  27. @property(strong, nonatomic, readwrite) NSOperationQueue *compoundQueue;
  28. @property(assign, nonatomic) NSUInteger completedOperations;
  29. @property(strong, nonatomic) NSMutableArray *errors;
  30. #if FIRCLS_DISPATCH_QUEUES_AS_OBJECTS
  31. @property(strong, nonatomic) dispatch_queue_t countingQueue;
  32. #else
  33. @property(assign, nonatomic) dispatch_queue_t countingQueue;
  34. #endif
  35. @end
  36. @implementation FIRCLSCompoundOperation
  37. - (instancetype)init {
  38. self = [super init];
  39. if (!self) {
  40. return nil;
  41. }
  42. _compoundQueue = [[NSOperationQueue alloc] init];
  43. _completedOperations = 0;
  44. _errors = [NSMutableArray array];
  45. _countingQueue =
  46. dispatch_queue_create(FIRCLSCompoundOperationCountingQueueLabel, DISPATCH_QUEUE_SERIAL);
  47. return self;
  48. }
  49. #if !FIRCLS_DISPATCH_QUEUES_AS_OBJECTS
  50. - (void)dealloc {
  51. if (_countingQueue) {
  52. dispatch_release(_countingQueue);
  53. }
  54. }
  55. #endif
  56. - (void)main {
  57. for (FIRCLSFABAsyncOperation *operation in self.operations) {
  58. [self injectCompoundAsyncCompletionInOperation:operation];
  59. [self injectCompoundSyncCompletionInOperation:operation];
  60. [self.compoundQueue addOperation:operation];
  61. }
  62. }
  63. - (void)cancel {
  64. if (self.compoundQueue.operations.count > 0) {
  65. [self.compoundQueue cancelAllOperations];
  66. dispatch_sync(self.countingQueue, ^{
  67. [self attemptCompoundCompletion];
  68. });
  69. } else {
  70. for (NSOperation *operation in self.operations) {
  71. [operation cancel];
  72. }
  73. // we have to add the operations to the queue in order for their isFinished property to be set
  74. // to true.
  75. [self.compoundQueue addOperations:self.operations waitUntilFinished:NO];
  76. }
  77. [super cancel];
  78. }
  79. - (void)injectCompoundAsyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation {
  80. __weak FIRCLSCompoundOperation *weakSelf = self;
  81. FIRCLSFABAsyncOperationCompletionBlock originalAsyncCompletion = [operation.asyncCompletion copy];
  82. FIRCLSFABAsyncOperationCompletionBlock completion = ^(NSError *error) {
  83. __strong FIRCLSCompoundOperation *strongSelf = weakSelf;
  84. if (originalAsyncCompletion) {
  85. dispatch_sync(strongSelf.countingQueue, ^{
  86. originalAsyncCompletion(error);
  87. });
  88. }
  89. [strongSelf updateCompletionCountsWithError:error];
  90. };
  91. operation.asyncCompletion = completion;
  92. }
  93. - (void)injectCompoundSyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation {
  94. __weak FIRCLSCompoundOperation *weakSelf = self;
  95. void (^originalSyncCompletion)(void) = [operation.completionBlock copy];
  96. void (^completion)(void) = ^{
  97. __strong FIRCLSCompoundOperation *strongSelf = weakSelf;
  98. if (originalSyncCompletion) {
  99. dispatch_sync(strongSelf.countingQueue, ^{
  100. originalSyncCompletion();
  101. });
  102. }
  103. dispatch_sync(strongSelf.countingQueue, ^{
  104. [strongSelf attemptCompoundCompletion];
  105. });
  106. };
  107. operation.completionBlock = completion;
  108. }
  109. - (void)updateCompletionCountsWithError:(NSError *)error {
  110. dispatch_sync(self.countingQueue, ^{
  111. if (!error) {
  112. self.completedOperations += 1;
  113. } else {
  114. [self.errors addObject:error];
  115. }
  116. });
  117. }
  118. - (void)attemptCompoundCompletion {
  119. if (self.isCancelled) {
  120. [self finishWithError:[NSError errorWithDomain:FIRCLSCompoundOperationErrorDomain
  121. code:FIRCLSCompoundOperationErrorCodeCancelled
  122. userInfo:@{
  123. NSLocalizedDescriptionKey : [NSString
  124. stringWithFormat:@"%@ cancelled", self.name]
  125. }]];
  126. self.asyncCompletion = nil;
  127. } else if (self.completedOperations + self.errors.count == self.operations.count) {
  128. NSError *error = nil;
  129. if (self.errors.count > 0) {
  130. error = [NSError
  131. errorWithDomain:FIRCLSCompoundOperationErrorDomain
  132. code:FIRCLSCompoundOperationErrorCodeSuboperationFailed
  133. userInfo:@{FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors : self.errors}];
  134. }
  135. [self finishWithError:error];
  136. }
  137. }
  138. @end