PageRenderTime 50ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/Pods/Bolts/Bolts/Common/BFTask.m

https://gitlab.com/daniel.colceag/MonkeyNews
Objective C | 472 lines | 378 code | 81 blank | 13 comment | 54 complexity | 387531a1ec686c5123363fc1e5c57b9f MD5 | raw file
  1. /*
  2. * Copyright (c) 2014, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. #import "BFTask.h"
  11. #import <libkern/OSAtomic.h>
  12. #import "Bolts.h"
  13. __attribute__ ((noinline)) void warnBlockingOperationOnMainThread() {
  14. NSLog(@"Warning: A long-running operation is being executed on the main thread. \n"
  15. " Break on warnBlockingOperationOnMainThread() to debug.");
  16. }
  17. NSString *const BFTaskErrorDomain = @"bolts";
  18. NSInteger const kBFMultipleErrorsError = 80175001;
  19. NSString *const BFTaskMultipleExceptionsException = @"BFMultipleExceptionsException";
  20. @interface BFTask () {
  21. id _result;
  22. NSError *_error;
  23. NSException *_exception;
  24. }
  25. @property (nonatomic, assign, readwrite, getter=isCancelled) BOOL cancelled;
  26. @property (nonatomic, assign, readwrite, getter=isFaulted) BOOL faulted;
  27. @property (nonatomic, assign, readwrite, getter=isCompleted) BOOL completed;
  28. @property (nonatomic, strong) NSObject *lock;
  29. @property (nonatomic, strong) NSCondition *condition;
  30. @property (nonatomic, strong) NSMutableArray *callbacks;
  31. @end
  32. @implementation BFTask
  33. #pragma mark - Initializer
  34. - (instancetype)init {
  35. self = [super init];
  36. if (!self) return nil;
  37. _lock = [[NSObject alloc] init];
  38. _condition = [[NSCondition alloc] init];
  39. _callbacks = [NSMutableArray array];
  40. return self;
  41. }
  42. - (instancetype)initWithResult:(id)result {
  43. self = [super init];
  44. if (!self) return nil;
  45. [self trySetResult:result];
  46. return self;
  47. }
  48. - (instancetype)initWithError:(NSError *)error {
  49. self = [super init];
  50. if (!self) return nil;
  51. [self trySetError:error];
  52. return self;
  53. }
  54. - (instancetype)initWithException:(NSException *)exception {
  55. self = [super init];
  56. if (!self) return nil;
  57. [self trySetException:exception];
  58. return self;
  59. }
  60. - (instancetype)initCancelled {
  61. self = [super init];
  62. if (!self) return nil;
  63. [self trySetCancelled];
  64. return self;
  65. }
  66. #pragma mark - Task Class methods
  67. + (instancetype)taskWithResult:(id)result {
  68. return [[self alloc] initWithResult:result];
  69. }
  70. + (instancetype)taskWithError:(NSError *)error {
  71. return [[self alloc] initWithError:error];
  72. }
  73. + (instancetype)taskWithException:(NSException *)exception {
  74. return [[self alloc] initWithException:exception];
  75. }
  76. + (instancetype)cancelledTask {
  77. return [[self alloc] initCancelled];
  78. }
  79. + (instancetype)taskForCompletionOfAllTasks:(NSArray<BFTask *> *)tasks {
  80. __block int32_t total = (int32_t)tasks.count;
  81. if (total == 0) {
  82. return [self taskWithResult:nil];
  83. }
  84. __block int32_t cancelled = 0;
  85. NSObject *lock = [[NSObject alloc] init];
  86. NSMutableArray *errors = [NSMutableArray array];
  87. NSMutableArray *exceptions = [NSMutableArray array];
  88. BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
  89. for (BFTask *task in tasks) {
  90. [task continueWithBlock:^id(BFTask *task) {
  91. if (task.exception) {
  92. @synchronized (lock) {
  93. [exceptions addObject:task.exception];
  94. }
  95. } else if (task.error) {
  96. @synchronized (lock) {
  97. [errors addObject:task.error];
  98. }
  99. } else if (task.cancelled) {
  100. OSAtomicIncrement32(&cancelled);
  101. }
  102. if (OSAtomicDecrement32(&total) == 0) {
  103. if (exceptions.count > 0) {
  104. if (exceptions.count == 1) {
  105. tcs.exception = [exceptions firstObject];
  106. } else {
  107. NSException *exception =
  108. [NSException exceptionWithName:BFTaskMultipleExceptionsException
  109. reason:@"There were multiple exceptions."
  110. userInfo:@{ @"exceptions": exceptions }];
  111. tcs.exception = exception;
  112. }
  113. } else if (errors.count > 0) {
  114. if (errors.count == 1) {
  115. tcs.error = [errors firstObject];
  116. } else {
  117. NSError *error = [NSError errorWithDomain:BFTaskErrorDomain
  118. code:kBFMultipleErrorsError
  119. userInfo:@{ @"errors": errors }];
  120. tcs.error = error;
  121. }
  122. } else if (cancelled > 0) {
  123. [tcs cancel];
  124. } else {
  125. tcs.result = nil;
  126. }
  127. }
  128. return nil;
  129. }];
  130. }
  131. return tcs.task;
  132. }
  133. + (instancetype)taskForCompletionOfAllTasksWithResults:(NSArray<BFTask *> *)tasks {
  134. return [[self taskForCompletionOfAllTasks:tasks] continueWithSuccessBlock:^id(BFTask *task) {
  135. return [tasks valueForKey:@"result"];
  136. }];
  137. }
  138. + (instancetype)taskWithDelay:(int)millis {
  139. BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
  140. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC);
  141. dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
  142. tcs.result = nil;
  143. });
  144. return tcs.task;
  145. }
  146. + (instancetype)taskWithDelay:(int)millis
  147. cancellationToken:(BFCancellationToken *)token {
  148. if (token.cancellationRequested) {
  149. return [BFTask cancelledTask];
  150. }
  151. BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
  152. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC);
  153. dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
  154. if (token.cancellationRequested) {
  155. [tcs cancel];
  156. return;
  157. }
  158. tcs.result = nil;
  159. });
  160. return tcs.task;
  161. }
  162. + (instancetype)taskFromExecutor:(BFExecutor *)executor withBlock:(nullable id (^)())block {
  163. return [[self taskWithResult:nil] continueWithExecutor:executor withBlock:^id(BFTask *task) {
  164. return block();
  165. }];
  166. }
  167. #pragma mark - Custom Setters/Getters
  168. - (id)result {
  169. @synchronized(self.lock) {
  170. return _result;
  171. }
  172. }
  173. - (BOOL)trySetResult:(id)result {
  174. @synchronized(self.lock) {
  175. if (self.completed) {
  176. return NO;
  177. }
  178. self.completed = YES;
  179. _result = result;
  180. [self runContinuations];
  181. return YES;
  182. }
  183. }
  184. - (NSError *)error {
  185. @synchronized(self.lock) {
  186. return _error;
  187. }
  188. }
  189. - (BOOL)trySetError:(NSError *)error {
  190. @synchronized(self.lock) {
  191. if (self.completed) {
  192. return NO;
  193. }
  194. self.completed = YES;
  195. self.faulted = YES;
  196. _error = error;
  197. [self runContinuations];
  198. return YES;
  199. }
  200. }
  201. - (NSException *)exception {
  202. @synchronized(self.lock) {
  203. return _exception;
  204. }
  205. }
  206. - (BOOL)trySetException:(NSException *)exception {
  207. @synchronized(self.lock) {
  208. if (self.completed) {
  209. return NO;
  210. }
  211. self.completed = YES;
  212. self.faulted = YES;
  213. _exception = exception;
  214. [self runContinuations];
  215. return YES;
  216. }
  217. }
  218. - (BOOL)isCancelled {
  219. @synchronized(self.lock) {
  220. return _cancelled;
  221. }
  222. }
  223. - (BOOL)isFaulted {
  224. @synchronized(self.lock) {
  225. return _faulted;
  226. }
  227. }
  228. - (BOOL)trySetCancelled {
  229. @synchronized(self.lock) {
  230. if (self.completed) {
  231. return NO;
  232. }
  233. self.completed = YES;
  234. self.cancelled = YES;
  235. [self runContinuations];
  236. return YES;
  237. }
  238. }
  239. - (BOOL)isCompleted {
  240. @synchronized(self.lock) {
  241. return _completed;
  242. }
  243. }
  244. - (void)setCompleted {
  245. @synchronized(self.lock) {
  246. _completed = YES;
  247. }
  248. }
  249. - (void)runContinuations {
  250. @synchronized(self.lock) {
  251. [self.condition lock];
  252. [self.condition broadcast];
  253. [self.condition unlock];
  254. for (void (^callback)() in self.callbacks) {
  255. callback();
  256. }
  257. [self.callbacks removeAllObjects];
  258. }
  259. }
  260. #pragma mark - Chaining methods
  261. - (BFTask *)continueWithExecutor:(BFExecutor *)executor
  262. withBlock:(BFContinuationBlock)block {
  263. return [self continueWithExecutor:executor block:block cancellationToken:nil];
  264. }
  265. - (BFTask *)continueWithExecutor:(BFExecutor *)executor
  266. block:(BFContinuationBlock)block
  267. cancellationToken:(BFCancellationToken *)cancellationToken {
  268. BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
  269. // Capture all of the state that needs to used when the continuation is complete.
  270. void (^wrappedBlock)() = ^() {
  271. [executor execute:^{
  272. if (cancellationToken.cancellationRequested) {
  273. [tcs cancel];
  274. return;
  275. }
  276. id result = nil;
  277. @try {
  278. result = block(self);
  279. } @catch (NSException *exception) {
  280. tcs.exception = exception;
  281. return;
  282. }
  283. if ([result isKindOfClass:[BFTask class]]) {
  284. id (^setupWithTask) (BFTask *) = ^id(BFTask *task) {
  285. if (cancellationToken.cancellationRequested || task.cancelled) {
  286. [tcs cancel];
  287. } else if (task.exception) {
  288. tcs.exception = task.exception;
  289. } else if (task.error) {
  290. tcs.error = task.error;
  291. } else {
  292. tcs.result = task.result;
  293. }
  294. return nil;
  295. };
  296. BFTask *resultTask = (BFTask *)result;
  297. if (resultTask.completed) {
  298. setupWithTask(resultTask);
  299. } else {
  300. [resultTask continueWithBlock:setupWithTask];
  301. }
  302. } else {
  303. tcs.result = result;
  304. }
  305. }];
  306. };
  307. BOOL completed;
  308. @synchronized(self.lock) {
  309. completed = self.completed;
  310. if (!completed) {
  311. [self.callbacks addObject:[wrappedBlock copy]];
  312. }
  313. }
  314. if (completed) {
  315. wrappedBlock();
  316. }
  317. return tcs.task;
  318. }
  319. - (BFTask *)continueWithBlock:(BFContinuationBlock)block {
  320. return [self continueWithExecutor:[BFExecutor defaultExecutor] block:block cancellationToken:nil];
  321. }
  322. - (BFTask *)continueWithBlock:(BFContinuationBlock)block
  323. cancellationToken:(BFCancellationToken *)cancellationToken {
  324. return [self continueWithExecutor:[BFExecutor defaultExecutor] block:block cancellationToken:cancellationToken];
  325. }
  326. - (BFTask *)continueWithExecutor:(BFExecutor *)executor
  327. withSuccessBlock:(BFContinuationBlock)block {
  328. return [self continueWithExecutor:executor successBlock:block cancellationToken:nil];
  329. }
  330. - (BFTask *)continueWithExecutor:(BFExecutor *)executor
  331. successBlock:(BFContinuationBlock)block
  332. cancellationToken:(BFCancellationToken *)cancellationToken {
  333. if (cancellationToken.cancellationRequested) {
  334. return [BFTask cancelledTask];
  335. }
  336. return [self continueWithExecutor:executor block:^id(BFTask *task) {
  337. if (task.faulted || task.cancelled) {
  338. return task;
  339. } else {
  340. return block(task);
  341. }
  342. } cancellationToken:cancellationToken];
  343. }
  344. - (BFTask *)continueWithSuccessBlock:(BFContinuationBlock)block {
  345. return [self continueWithExecutor:[BFExecutor defaultExecutor] successBlock:block cancellationToken:nil];
  346. }
  347. - (BFTask *)continueWithSuccessBlock:(BFContinuationBlock)block
  348. cancellationToken:(BFCancellationToken *)cancellationToken {
  349. return [self continueWithExecutor:[BFExecutor defaultExecutor] successBlock:block cancellationToken:cancellationToken];
  350. }
  351. #pragma mark - Syncing Task (Avoid it)
  352. - (void)warnOperationOnMainThread {
  353. warnBlockingOperationOnMainThread();
  354. }
  355. - (void)waitUntilFinished {
  356. if ([NSThread isMainThread]) {
  357. [self warnOperationOnMainThread];
  358. }
  359. @synchronized(self.lock) {
  360. if (self.completed) {
  361. return;
  362. }
  363. [self.condition lock];
  364. }
  365. [self.condition wait];
  366. [self.condition unlock];
  367. }
  368. #pragma mark - NSObject
  369. - (NSString *)description {
  370. // Acquire the data from the locked properties
  371. BOOL completed;
  372. BOOL cancelled;
  373. BOOL faulted;
  374. NSString *resultDescription = nil;
  375. @synchronized(self.lock) {
  376. completed = self.completed;
  377. cancelled = self.cancelled;
  378. faulted = self.faulted;
  379. resultDescription = completed ? [NSString stringWithFormat:@" result = %@", self.result] : @"";
  380. }
  381. // Description string includes status information and, if available, the
  382. // result since in some ways this is what a promise actually "is".
  383. return [NSString stringWithFormat:@"<%@: %p; completed = %@; cancelled = %@; faulted = %@;%@>",
  384. NSStringFromClass([self class]),
  385. self,
  386. completed ? @"YES" : @"NO",
  387. cancelled ? @"YES" : @"NO",
  388. faulted ? @"YES" : @"NO",
  389. resultDescription];
  390. }
  391. @end