Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions PINFuture/Classes/PINFuture.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)executor:(id<PINExecutor>)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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is the best way to expose the composite future feature you're suggesting?

Separately, I think it would be more PINFuture-y to have this in a category and import it below?


@end

NS_ASSUME_NONNULL_END
Expand Down
42 changes: 35 additions & 7 deletions PINFuture/Classes/PINFuture.m
Original file line number Diff line number Diff line change
Expand Up @@ -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<PINFutureCallback *> *callbacks; // If nil, there are no callbacks
Expand Down Expand Up @@ -102,17 +102,35 @@ - (void)executor:(id<PINExecutor>)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
Expand All @@ -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<PINFutureCallback *> *callbacks;
Expand Down