OSSTask.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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 "OSSTask.h"
  11. #import "OSSLog.h"
  12. #import <libkern/OSAtomic.h>
  13. #import "OSSBolts.h"
  14. NS_ASSUME_NONNULL_BEGIN
  15. __attribute__ ((noinline)) void ossWarnBlockingOperationOnMainThread() {
  16. NSLog(@"Warning: A long-running operation is being executed on the main thread. \n"
  17. " Break on warnBlockingOperationOnMainThread() to debug.");
  18. }
  19. NSString *const OSSTaskErrorDomain = @"bolts";
  20. NSInteger const kOSSMultipleErrorsError = 80175001;
  21. NSString *const OSSTaskMultipleExceptionsException = @"OSSMultipleExceptionsException";
  22. NSString *const OSSTaskMultipleErrorsUserInfoKey = @"errors";
  23. NSString *const OSSTaskMultipleExceptionsUserInfoKey = @"exceptions";
  24. @interface OSSTask () {
  25. id _result;
  26. NSError *_error;
  27. NSException *_exception;
  28. }
  29. @property (nonatomic, assign, readwrite, getter=isCancelled) BOOL cancelled;
  30. @property (nonatomic, assign, readwrite, getter=isFaulted) BOOL faulted;
  31. @property (nonatomic, assign, readwrite, getter=isCompleted) BOOL completed;
  32. @property (nonatomic, strong) NSObject *lock;
  33. @property (nonatomic, strong) NSCondition *condition;
  34. @property (nonatomic, strong) NSMutableArray *callbacks;
  35. @end
  36. @implementation OSSTask
  37. #pragma mark - Initializer
  38. - (instancetype)init {
  39. self = [super init];
  40. if (!self) return self;
  41. _lock = [[NSObject alloc] init];
  42. _condition = [[NSCondition alloc] init];
  43. _callbacks = [NSMutableArray array];
  44. return self;
  45. }
  46. - (instancetype)initWithResult:(_Nullable id)result {
  47. self = [super init];
  48. if (self) {
  49. [self trySetResult:result];
  50. }
  51. return self;
  52. }
  53. - (instancetype)initWithError:(NSError *)error {
  54. self = [super init];
  55. if (!self) return self;
  56. [self trySetError:error];
  57. return self;
  58. }
  59. - (instancetype)initWithException:(NSException *)exception {
  60. self = [super init];
  61. if (!self) return self;
  62. [self trySetException:exception];
  63. return self;
  64. }
  65. - (instancetype)initCancelled {
  66. self = [super init];
  67. if (!self) return self;
  68. [self trySetCancelled];
  69. return self;
  70. }
  71. #pragma mark - Task Class methods
  72. + (instancetype)taskWithResult:(_Nullable id)result {
  73. return [[self alloc] initWithResult:result];
  74. }
  75. + (instancetype)taskWithError:(NSError *)error {
  76. return [[self alloc] initWithError:error];
  77. }
  78. + (instancetype)taskWithException:(NSException *)exception {
  79. return [[self alloc] initWithException:exception];
  80. }
  81. + (instancetype)cancelledTask {
  82. return [[self alloc] initCancelled];
  83. }
  84. + (instancetype)taskForCompletionOfAllTasks:(nullable NSArray<OSSTask *> *)tasks {
  85. __block int32_t total = (int32_t)tasks.count;
  86. if (total == 0) {
  87. return [self taskWithResult:nil];
  88. }
  89. __block int32_t cancelled = 0;
  90. NSObject *lock = [[NSObject alloc] init];
  91. NSMutableArray *errors = [NSMutableArray array];
  92. NSMutableArray *exceptions = [NSMutableArray array];
  93. OSSTaskCompletionSource *tcs = [OSSTaskCompletionSource taskCompletionSource];
  94. for (OSSTask *task in tasks) {
  95. [task continueWithBlock:^id(OSSTask *task) {
  96. if (task.exception) {
  97. @synchronized (lock) {
  98. [exceptions addObject:task.exception];
  99. }
  100. } else if (task.error) {
  101. @synchronized (lock) {
  102. [errors addObject:task.error];
  103. }
  104. } else if (task.cancelled) {
  105. OSAtomicIncrement32Barrier(&cancelled);
  106. }
  107. if (OSAtomicDecrement32Barrier(&total) == 0) {
  108. if (exceptions.count > 0) {
  109. if (exceptions.count == 1) {
  110. tcs.exception = [exceptions firstObject];
  111. } else {
  112. NSException *exception =
  113. [NSException exceptionWithName:OSSTaskMultipleExceptionsException
  114. reason:@"There were multiple exceptions."
  115. userInfo:@{ OSSTaskMultipleExceptionsUserInfoKey: exceptions }];
  116. tcs.exception = exception;
  117. }
  118. } else if (errors.count > 0) {
  119. if (errors.count == 1) {
  120. tcs.error = [errors firstObject];
  121. } else {
  122. NSError *error = [NSError errorWithDomain:OSSTaskErrorDomain
  123. code:kOSSMultipleErrorsError
  124. userInfo:@{ OSSTaskMultipleErrorsUserInfoKey: errors }];
  125. tcs.error = error;
  126. }
  127. } else if (cancelled > 0) {
  128. [tcs cancel];
  129. } else {
  130. tcs.result = nil;
  131. }
  132. }
  133. return nil;
  134. }];
  135. }
  136. return tcs.task;
  137. }
  138. + (instancetype)taskForCompletionOfAllTasksWithResults:(nullable NSArray<OSSTask *> *)tasks {
  139. return [[self taskForCompletionOfAllTasks:tasks] continueWithSuccessBlock:^id(OSSTask *task) {
  140. return [tasks valueForKey:@"result"];
  141. }];
  142. }
  143. + (instancetype)taskForCompletionOfAnyTask:(nullable NSArray<OSSTask *> *)tasks
  144. {
  145. __block int32_t total = (int32_t)tasks.count;
  146. if (total == 0) {
  147. return [self taskWithResult:nil];
  148. }
  149. __block int completed = 0;
  150. __block int32_t cancelled = 0;
  151. NSObject *lock = [NSObject new];
  152. NSMutableArray<NSError *> *errors = [NSMutableArray new];
  153. NSMutableArray<NSException *> *exceptions = [NSMutableArray new];
  154. OSSTaskCompletionSource *source = [OSSTaskCompletionSource taskCompletionSource];
  155. for (OSSTask *task in tasks) {
  156. [task continueWithBlock:^id(OSSTask *task) {
  157. if (task.exception != nil) {
  158. @synchronized(lock) {
  159. [exceptions addObject:task.exception];
  160. }
  161. } else if (task.error != nil) {
  162. @synchronized(lock) {
  163. [errors addObject:task.error];
  164. }
  165. } else if (task.cancelled) {
  166. OSAtomicIncrement32Barrier(&cancelled);
  167. } else {
  168. if(OSAtomicCompareAndSwap32Barrier(0, 1, &completed)) {
  169. [source setResult:task.result];
  170. }
  171. }
  172. if (OSAtomicDecrement32Barrier(&total) == 0 &&
  173. OSAtomicCompareAndSwap32Barrier(0, 1, &completed)) {
  174. if (cancelled > 0) {
  175. [source cancel];
  176. } else if (exceptions.count > 0) {
  177. if (exceptions.count == 1) {
  178. source.exception = exceptions.firstObject;
  179. } else {
  180. NSException *exception =
  181. [NSException exceptionWithName:OSSTaskMultipleExceptionsException
  182. reason:@"There were multiple exceptions."
  183. userInfo:@{ @"exceptions": exceptions }];
  184. source.exception = exception;
  185. }
  186. } else if (errors.count > 0) {
  187. if (errors.count == 1) {
  188. source.error = errors.firstObject;
  189. } else {
  190. NSError *error = [NSError errorWithDomain:OSSTaskErrorDomain
  191. code:kOSSMultipleErrorsError
  192. userInfo:@{ @"errors": errors }];
  193. source.error = error;
  194. }
  195. }
  196. }
  197. // Abort execution of per tasks continuations
  198. return nil;
  199. }];
  200. }
  201. return source.task;
  202. }
  203. + (instancetype)taskWithDelay:(int)millis {
  204. OSSTaskCompletionSource *tcs = [OSSTaskCompletionSource taskCompletionSource];
  205. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC);
  206. dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
  207. tcs.result = nil;
  208. });
  209. return tcs.task;
  210. }
  211. + (instancetype)taskWithDelay:(int)millis cancellationToken:(nullable OSSCancellationToken *)token {
  212. if (token.cancellationRequested) {
  213. return [OSSTask cancelledTask];
  214. }
  215. OSSTaskCompletionSource *tcs = [OSSTaskCompletionSource taskCompletionSource];
  216. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC);
  217. dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
  218. if (token.cancellationRequested) {
  219. [tcs cancel];
  220. return;
  221. }
  222. tcs.result = nil;
  223. });
  224. return tcs.task;
  225. }
  226. + (instancetype)taskFromExecutor:(OSSExecutor *)executor withBlock:(nullable id (^)(void))block {
  227. return [[self taskWithResult:nil] continueWithExecutor:executor withBlock:^id(OSSTask *task) {
  228. return block();
  229. }];
  230. }
  231. #pragma mark - Custom Setters/Getters
  232. - (nullable id)result {
  233. @synchronized(self.lock) {
  234. return _result;
  235. }
  236. }
  237. - (BOOL)trySetResult:(nullable id)result {
  238. @synchronized(self.lock) {
  239. if (self.completed) {
  240. return NO;
  241. }
  242. self.completed = YES;
  243. _result = result;
  244. [self runContinuations];
  245. return YES;
  246. }
  247. }
  248. - (nullable NSError *)error {
  249. @synchronized(self.lock) {
  250. return _error;
  251. }
  252. }
  253. - (BOOL)trySetError:(NSError *)error {
  254. @synchronized(self.lock) {
  255. if (self.completed) {
  256. return NO;
  257. }
  258. self.completed = YES;
  259. self.faulted = YES;
  260. _error = error;
  261. [self runContinuations];
  262. return YES;
  263. }
  264. }
  265. - (nullable NSException *)exception {
  266. @synchronized(self.lock) {
  267. return _exception;
  268. }
  269. }
  270. - (BOOL)trySetException:(NSException *)exception {
  271. @synchronized(self.lock) {
  272. if (self.completed) {
  273. return NO;
  274. }
  275. self.completed = YES;
  276. self.faulted = YES;
  277. _exception = exception;
  278. [self runContinuations];
  279. return YES;
  280. }
  281. }
  282. - (BOOL)isCancelled {
  283. @synchronized(self.lock) {
  284. return _cancelled;
  285. }
  286. }
  287. - (BOOL)isFaulted {
  288. @synchronized(self.lock) {
  289. return _faulted;
  290. }
  291. }
  292. - (BOOL)trySetCancelled {
  293. @synchronized(self.lock) {
  294. if (self.completed) {
  295. return NO;
  296. }
  297. self.completed = YES;
  298. self.cancelled = YES;
  299. [self runContinuations];
  300. return YES;
  301. }
  302. }
  303. - (BOOL)isCompleted {
  304. @synchronized(self.lock) {
  305. return _completed;
  306. }
  307. }
  308. - (void)runContinuations {
  309. @synchronized(self.lock) {
  310. [self.condition lock];
  311. [self.condition broadcast];
  312. [self.condition unlock];
  313. for (void (^callback)(void) in self.callbacks) {
  314. callback();
  315. }
  316. [self.callbacks removeAllObjects];
  317. }
  318. }
  319. #pragma mark - Chaining methods
  320. - (OSSTask *)continueWithExecutor:(OSSExecutor *)executor withBlock:(OSSContinuationBlock)block {
  321. return [self continueWithExecutor:executor block:block cancellationToken:nil];
  322. }
  323. - (OSSTask *)continueWithExecutor:(OSSExecutor *)executor
  324. block:(OSSContinuationBlock)block
  325. cancellationToken:(nullable OSSCancellationToken *)cancellationToken {
  326. OSSTaskCompletionSource *tcs = [OSSTaskCompletionSource taskCompletionSource];
  327. // Capture all of the state that needs to used when the continuation is complete.
  328. dispatch_block_t executionBlock = ^{
  329. if (cancellationToken.cancellationRequested) {
  330. [tcs cancel];
  331. return;
  332. }
  333. id result = nil;
  334. @try {
  335. result = block(self);
  336. } @catch (NSException *exception) {
  337. tcs.exception = exception;
  338. OSSLogError(@"exception name: %@",[exception name]);
  339. OSSLogError(@"exception reason: %@",[exception reason]);
  340. return;
  341. }
  342. if ([result isKindOfClass:[OSSTask class]]) {
  343. id (^setupWithTask) (OSSTask *) = ^id(OSSTask *task) {
  344. if (cancellationToken.cancellationRequested || task.cancelled) {
  345. [tcs cancel];
  346. } else if (task.exception) {
  347. tcs.exception = task.exception;
  348. } else if (task.error) {
  349. tcs.error = task.error;
  350. } else {
  351. tcs.result = task.result;
  352. }
  353. return nil;
  354. };
  355. OSSTask *resultTask = (OSSTask *)result;
  356. if (resultTask.completed) {
  357. setupWithTask(resultTask);
  358. } else {
  359. [resultTask continueWithBlock:setupWithTask];
  360. }
  361. } else {
  362. tcs.result = result;
  363. }
  364. };
  365. BOOL completed;
  366. @synchronized(self.lock) {
  367. completed = self.completed;
  368. if (!completed) {
  369. [self.callbacks addObject:[^{
  370. [executor execute:executionBlock];
  371. } copy]];
  372. }
  373. }
  374. if (completed) {
  375. [executor execute:executionBlock];
  376. }
  377. return tcs.task;
  378. }
  379. - (OSSTask *)continueWithBlock:(OSSContinuationBlock)block {
  380. return [self continueWithExecutor:[OSSExecutor defaultExecutor] block:block cancellationToken:nil];
  381. }
  382. - (OSSTask *)continueWithBlock:(OSSContinuationBlock)block cancellationToken:(nullable OSSCancellationToken *)cancellationToken {
  383. return [self continueWithExecutor:[OSSExecutor defaultExecutor] block:block cancellationToken:cancellationToken];
  384. }
  385. - (OSSTask *)continueWithExecutor:(OSSExecutor *)executor
  386. withSuccessBlock:(OSSContinuationBlock)block {
  387. return [self continueWithExecutor:executor successBlock:block cancellationToken:nil];
  388. }
  389. - (OSSTask *)continueWithExecutor:(OSSExecutor *)executor
  390. successBlock:(OSSContinuationBlock)block
  391. cancellationToken:(nullable OSSCancellationToken *)cancellationToken {
  392. if (cancellationToken.cancellationRequested) {
  393. return [OSSTask cancelledTask];
  394. }
  395. return [self continueWithExecutor:executor block:^id(OSSTask *task) {
  396. if (task.faulted || task.cancelled) {
  397. return task;
  398. } else {
  399. return block(task);
  400. }
  401. } cancellationToken:cancellationToken];
  402. }
  403. - (OSSTask *)continueWithSuccessBlock:(OSSContinuationBlock)block {
  404. return [self continueWithExecutor:[OSSExecutor defaultExecutor] successBlock:block cancellationToken:nil];
  405. }
  406. - (OSSTask *)continueWithSuccessBlock:(OSSContinuationBlock)block cancellationToken:(nullable OSSCancellationToken *)cancellationToken {
  407. return [self continueWithExecutor:[OSSExecutor defaultExecutor] successBlock:block cancellationToken:cancellationToken];
  408. }
  409. #pragma mark - Syncing Task (Avoid it)
  410. - (void)warnOperationOnMainThread {
  411. ossWarnBlockingOperationOnMainThread();
  412. }
  413. - (void)waitUntilFinished {
  414. if ([NSThread isMainThread]) {
  415. [self warnOperationOnMainThread];
  416. }
  417. @synchronized(self.lock) {
  418. if (self.completed) {
  419. return;
  420. }
  421. [self.condition lock];
  422. }
  423. while (!self.completed) {
  424. [self.condition wait];
  425. }
  426. [self.condition unlock];
  427. }
  428. #pragma mark - NSObject
  429. - (NSString *)description {
  430. // Acquire the data from the locked properties
  431. BOOL completed;
  432. BOOL cancelled;
  433. BOOL faulted;
  434. NSString *resultDescription = nil;
  435. @synchronized(self.lock) {
  436. completed = self.completed;
  437. cancelled = self.cancelled;
  438. faulted = self.faulted;
  439. resultDescription = completed ? [NSString stringWithFormat:@" result = %@", self.result] : @"";
  440. }
  441. // Description string includes status information and, if available, the
  442. // result since in some ways this is what a promise actually "is".
  443. return [NSString stringWithFormat:@"<%@: %p; completed = %@; cancelled = %@; faulted = %@;%@>",
  444. NSStringFromClass([self class]),
  445. self,
  446. completed ? @"YES" : @"NO",
  447. cancelled ? @"YES" : @"NO",
  448. faulted ? @"YES" : @"NO",
  449. resultDescription];
  450. }
  451. @end
  452. NS_ASSUME_NONNULL_END