]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSResultOperation.m
Security-58286.31.2.tar.gz
[apple/security.git] / keychain / ckks / CKKSResultOperation.m
1 /*
2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import "keychain/ckks/CKKSResultOperation.h"
25 #import "keychain/ckks/CKKSCondition.h"
26 #include <utilities/debugging.h>
27
28 @interface CKKSResultOperation()
29 @property NSMutableArray<CKKSResultOperation*>* successDependencies;
30 @property bool timeoutCanOccur;
31 @property dispatch_queue_t timeoutQueue;
32 @property void (^finishingBlock)(void);
33 @end
34
35 @implementation CKKSResultOperation
36 - (instancetype)init {
37 if(self = [super init]) {
38 _error = nil;
39 _successDependencies = [[NSMutableArray alloc] init];
40 _timeoutCanOccur = true;
41 _timeoutQueue = dispatch_queue_create("result-operation-timeout", DISPATCH_QUEUE_SERIAL);
42 _completionHandlerDidRunCondition = [[CKKSCondition alloc] init];
43
44 __weak __typeof(self) weakSelf = self;
45 _finishingBlock = ^(void) {
46 weakSelf.finishDate = [NSDate dateWithTimeIntervalSinceNow:0];
47 };
48 self.completionBlock = ^{}; // our _finishing block gets added in the method override
49 }
50 return self;
51 }
52
53 - (NSString*)description {
54 NSString* state = ([self isFinished] ? [NSString stringWithFormat:@"finished %@", self.finishDate] :
55 [self isCancelled] ? @"cancelled" :
56 [self isExecuting] ? @"executing" :
57 [self isReady] ? @"ready" :
58 @"pending");
59
60 if(self.error) {
61 return [NSString stringWithFormat: @"<%@: %@ error:%@>", [self selfname], state, self.error];
62 } else {
63 return [NSString stringWithFormat: @"<%@: %@%@>", [self selfname], state, [self pendingDependenciesString:@" dep:"]];
64 }
65 }
66
67 - (NSString*)debugDescription {
68 return [self description];
69 }
70
71 - (void)setCompletionBlock:(void (^)(void))completionBlock
72 {
73 __weak __typeof(self) weakSelf = self;
74 [super setCompletionBlock:^(void) {
75 __strong __typeof(self) strongSelf = weakSelf;
76 if (!strongSelf) {
77 secerror("ckksresultoperation: completion handler called on deallocated operation instance");
78 completionBlock(); // go ahead and still behave as things would if this method override were not here
79 return;
80 }
81
82 strongSelf.finishingBlock();
83 completionBlock();
84 [strongSelf.completionHandlerDidRunCondition fulfill];
85 }];
86 }
87
88 - (void)start {
89 if(![self allDependentsSuccessful]) {
90 secdebug("ckksresultoperation", "Not running due to some failed dependent: %@", self.error);
91 [self cancel];
92 } else {
93 [self invalidateTimeout];
94
95 }
96
97 [super start];
98 }
99
100 - (void)invalidateTimeout {
101 dispatch_sync(self.timeoutQueue, ^{
102 if(![self isCancelled]) {
103 self.timeoutCanOccur = false;
104 };
105 });
106 }
107
108 - (instancetype)timeout:(dispatch_time_t)timeout {
109 __weak __typeof(self) weakSelf = self;
110 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout), self.timeoutQueue, ^{
111 __strong __typeof(self) strongSelf = weakSelf;
112 if(strongSelf.timeoutCanOccur) {
113 strongSelf.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultTimedOut userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Operation timed out waiting to start for [%@]", [self pendingDependenciesString:@""]]}];
114 strongSelf.timeoutCanOccur = false;
115 [strongSelf cancel];
116 }
117 });
118
119 return self;
120 }
121
122 - (void)addSuccessDependency:(CKKSResultOperation *)operation {
123 [self addNullableSuccessDependency:operation];
124 }
125
126 - (void)addNullableSuccessDependency:(CKKSResultOperation *)operation {
127 if(!operation) {
128 return;
129 }
130 @synchronized(self) {
131 [self.successDependencies addObject: operation];
132 [self addDependency: operation];
133 }
134 }
135
136 - (bool)allDependentsSuccessful {
137 return [self allSuccessful: self.successDependencies];
138 }
139
140 - (bool)allSuccessful: (NSArray<CKKSResultOperation*>*) operations {
141 @synchronized(self) {
142 bool result = false;
143
144 bool finished = true; // all dependents must be finished
145 bool cancelled = false; // no dependents can be cancelled
146 bool failed = false; // no dependents can have failed
147
148 for(CKKSResultOperation* op in operations) {
149 finished &= !!([op isFinished]);
150 cancelled |= !!([op isCancelled]);
151 failed |= (op.error != nil);
152
153 // TODO: combine suberrors
154 if(op.error != nil) {
155 if([op.error.domain isEqual: CKKSResultErrorDomain] && op.error.code == CKKSResultSubresultError) {
156 // Already a subresult, just copy it on in
157 self.error = op.error;
158 } else {
159 self.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultSubresultError userInfo:@{ NSUnderlyingErrorKey: op.error}];
160 }
161 }
162 }
163
164 result = finished && !( cancelled || failed );
165
166 if(!result && self.error == nil) {
167 self.error = [NSError errorWithDomain:CKKSResultErrorDomain code: CKKSResultSubresultCancelled userInfo:nil];
168 }
169 return result;
170 }
171 }
172
173 + (CKKSResultOperation*)operationWithBlock:(void (^)(void))block {
174 CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
175 [op addExecutionBlock: block];
176 return op;
177 }
178
179 +(instancetype)named:(NSString*)name withBlock:(void(^)(void)) block {
180 CKKSResultOperation* blockOp = [CKKSResultOperation operationWithBlock: block];
181 blockOp.name = name;
182 return blockOp;
183 }
184 @end