diff --git a/PINFuture/Classes/PINFuture.h b/PINFuture/Classes/PINFuture.h index 30ac42c..e8f1d39 100644 --- a/PINFuture/Classes/PINFuture.h +++ b/PINFuture/Classes/PINFuture.h @@ -55,6 +55,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)executor:(id)executor success:(nullable void(^)(ObjectType value))success failure:(nullable void(^)(NSError *error))failure; +/** + * Enter the given dispatch group now, and + * leave when the future completes. If the future + * is already completed, this method has no effect. + */ +- (void)addToDispatchGroup:(dispatch_group_t)dispatchGroup; + @end NS_ASSUME_NONNULL_END diff --git a/PINFuture/Classes/PINFuture.m b/PINFuture/Classes/PINFuture.m index 49f82eb..bd392e0 100644 --- a/PINFuture/Classes/PINFuture.m +++ b/PINFuture/Classes/PINFuture.m @@ -32,7 +32,7 @@ @interface PINFuture () @property (nonatomic) NSLock *propertyLock; // TODO(chris): Use PINResult here. -@property (nonatomic) enum PINFutureState state; +@property (nonatomic) PINFutureState state; @property (nonatomic, nullable) id value; @property (nonatomic, nullable) NSError *error; @property (nonatomic, nullable) NSMutableArray *callbacks; // If nil, there are no callbacks @@ -102,17 +102,35 @@ - (void)executor:(id)executor success:(nullable void(^)(id value))s callback.success = success; callback.failure = failure; [self.propertyLock lock]; - // Lazily instantiate self.callbacks. Lots of futures will never have any callbacks. - if (self.callbacks == nil) { - self.callbacks = [NSMutableArray new]; - } - - [self.callbacks addObject:callback]; + [self locked_attachCallback:callback]; [self.propertyLock unlock]; [self tryFlushCallbacks]; } +- (void)addToDispatchGroup:(dispatch_group_t)dispatchGroup +{ + [self.propertyLock lock]; + if (self.state == PINFutureStatePending) { + // We're pending. Enter the group now, + // and attach a callback to leave the group + // on completion. + dispatch_group_enter(dispatchGroup); + PINFutureCallback *callback = [[PINFutureCallback alloc] init]; + callback.executor = PINExecutor.immediate; + callback.success = ^(__unused id value) { + dispatch_group_leave(dispatchGroup); + }; + callback.failure = ^(__unused NSError * error) { + dispatch_group_leave(dispatchGroup); + }; + [self locked_attachCallback:callback]; + } else { + // nop, we're already finished. + } + [self.propertyLock unlock]; +} + #pragma mark - internal - (void)transitionToState:(PINFutureState)state value:(nullable NSObject *)value error:(nullable NSError *)error @@ -130,6 +148,16 @@ - (void)transitionToState:(PINFutureState)state value:(nullable NSObject *)value [self tryFlushCallbacks]; } +- (void)locked_attachCallback:(PINFutureCallback *)callback +{ + // Lazily instantiate self.callbacks. Lots of futures will never have any callbacks. + if (self.callbacks == nil) { + self.callbacks = [NSMutableArray new]; + } + + [self.callbacks addObject:callback]; +} + - (void)tryFlushCallbacks { NSArray *callbacks;