Re: Can I create a thread with a runloop and a dispatch queue?
Re: Can I create a thread with a runloop and a dispatch queue?
- Subject: Re: Can I create a thread with a runloop and a dispatch queue?
- From: Andreas Grosam <email@hidden>
- Date: Fri, 04 Oct 2013 13:08:34 +0200
On 03.10.2013, at 22:00, Jens Alfke <email@hidden> wrote:
>
> On Oct 3, 2013, at 12:17 PM, Kyle Sluder <email@hidden> wrote:
>
>> What is adopting GCD going to get you if you aren't going to use GCD the
>> way it was intended?
>
…
> Also, my project is a library, and I don’t want to put too many restrictions on how it’s used by the caller. For example, here’s one method that runs an async query:
> - (void) runAsync: (void (^)(CBLQueryEnumerator*))onComplete;
> When the query results are available, the onComplete block is called. The problem is, in what context should the block be called?
Ideally, the execution context is "implementation defined". It is actually a dedicated private thread or private dispatch queue.
> Currently it’s scheduled on the same runloop as the original call.
Scheduling or dispatching a callback or block onto a "known" thread/queue will increase the risk for a dead lock. Thus, the execution context of the callback shall be created/selected by the "asynchronous result provider" and should not be accessible by the call-site.
If the call-site wishes to handle the callback code on a particular execution context it can easily do that through dispatching the code on that queue/thread.
> That works great for runloop-based clients.
If the client knowns about the risks (namely dead-locks), then perhaps yes. ;)
> But if this method is called from a dispatch queue (except the main one) it won’t work.
Possibly, a different design will solve potentially problems: instead "exposing" the execution context, just make it a private property of the "asynchronous result provider".
Suppose you have some asynchronous task (a class, not unlike an NSOperation), whose responsibility is to take an NSInputStream and copy it into an NSOutputStream. The principal API may look as follows:
@interface StreamToStreamCopier : NSObject <AsynchronousOperation>
- (void) initWithSourceStream:(NSInputStream*)sourceStream
destinationStream:(NSOutputStream*)destinationStream
completion:(completion_block_t)completionHandler;
- (void) start; // AsynchronousOperation
- (void) cancel; // AsynchronousOperation
@property (nonatomic, copy) completion_block_t completionHandler; // AsynchronousOperation
@end
// For StreamToStreamCopier the parameter result is the destinationStream
typedef void(^ completion_block_t)(id)result, NSError*error);
Here, *where* the completion handler will be actually executed is completely irrelevant!
Even when it sometimes gets possible to dispatch NSStream on dispatch queues, the API won't change.
Furthermore, it seems reasonable that, internally, the StreamToStreamCopier uses NSStream delegates, and hence run loops. Just, there's seems no way to define the run loop or "worker thread". But with an "extra" API, like:
@propery (nonatomic) workerThread; // NOT part of AsynchronousOperation!
or a suitable init method the run loop can be specified easily.
> Do I have to have a second variant, like runAsync:onQueue:, that does the same thing except it invokes the block on a queue?
What actually is "onQueue"? The execution context where the actual code of the task shall run, or is it the execution context where the completion handler shall be executed?
IMHO, a robust design should *not* require the call-site to specify the execution context of internal and private functions. Since internal functions are private the execution context shall, too! There may be valid but rare exceptions, though.
The latter - that is the call-size specifies the execution context of the callback - is OK, though unnecessary.
> That’s messy, and if runloops had queues it would be unnecessary.
> Does anyone have any comments beyond “this isn’t supported, it's bad and you are clueless”?*
I admit, it's a certain effort which is required when implementing a run loop based approach or when implementing a dispatch approach. A proper API then hides these implementation details.
So, I think there is a solution for the client side, as shown above.
You can go even further with an additional layer of abstraction, which - for the call-site - hides the implementation of, or "kind of" the "asynchronous result provider", which can be a NSOperation or other class with delegates or completion handler, with either a run loop based approach or a dispatch approach, an asynchronous function, an asynchronous method, or whatever is suitable to asynchronously deliver the eventual result:
The client side only deals with the "eventual result" represented in a "Promise". e.g.
Promise* promise = doSomethingAsyncWithParameter:(id)param;
Here, the execution context where the task executes it's functions is not known to the call-site. (Think of a NSURLConnection). We didn't specify a completion handler, since this will be handled through the "Promise" in a much more elegant and more powerful way.
Such a "Promise" is an Objective-C class, which encapsulates the eventual result (or error) of an asynchronous method, task or operation. And a client can "register" callbacks (blocks), *anywhere* and *anytime* (even when the operation is finished and deallocated), which get called *when* the eventual result is finally available, e.g.:
promise.then(^id(id result) {
NSLog(@"The result is finally: %@", result);
return result; // unused return value
}, nil /*error handler not needed */);
That way, the client side never sees NSOperations, NSRunloops, and does not have to create worker threads which require a run loop. The client side merely sees convenient "helper" methods like "doSomethingAsyncWithParameter:".
This effectively means, that the API is always the same, regardless how the underlaying code is implemented.
Andreas
> —Jens (in an increasingly grumpy mood today)
>
> * http://www.offworlddesigns.com/rules-lawyer-t-shirt/
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden