GCD with dispatch_suspend() and dispatch_release()

1,425 views
Skip to first unread message

Hwee-Boon Yar

unread,
Apr 11, 2011, 4:41:54 AM4/11/11
to cocoa-unbound
I'm using GCD, creating a dispatch queue and want to be able to stop
the tasks (blocks) from running if the user cancels. So I have all
this in a class:
- (id)init {
//stuff...
queue = 0;
}

- (void)startRunning {
queue = dispatch_queue_create(0, 0);
for (some loop) {
dispatch_async(queue, ^{
//do something with current element
});
}
}

- (void)userCancels {
if (queue) {
dispatch_suspend(queue);
}
}


- (void)dealloc {
if (queue) dispatch_release(queue);

[super dealloc];
}

This object is a UIView subclass, but I have tried to make it generic.
I'm having a few issues:

1. Is there better way to manage the dispatch queue? The only reason
why I created one is to be able to suspend (actually cancel) tasks.
2. I'm not sure how to use dispatch_suspend() and dispatch_release()
correctly. Obviously, if user allows the tasks to run to completion,
dispatch_release() runs and everything is fine. If the user cancels
though, dispatch_suspend() suspends the dispatch queue nicely. But how
do I then release the queue? It looks like the docs is saying that
dispatch_suspend() has to be balanced with dispatch_resume() before
calling dispatch_release(), but that would mean that the blocks starts
to run again.
3. Should I go back to NSOperation and folks instead?

Anyone can help? Thanks.

--
Hwee-Boon

Alex Blewitt

unread,
Apr 11, 2011, 7:14:20 AM4/11/11
to cocoa-...@googlegroups.com, cocoa-unbound
On 11 Apr 2011, at 09:41, Hwee-Boon Yar <hwee...@gmail.com> wrote:

> I'm using GCD, creating a dispatch queue and want to be able to stop
> the tasks (blocks) from running if the user cancels.

The problem with using a dispatch queue is any tasks already enqueued may have locks etc which then never get released. You really should put a short circuit in you block to do the equivalent of 'if(cancelled) return' so they all turn into no-ops, complete, and then release any holds they have.

Suspending is akin to pausing rather than cancelling; there's an assumption you will resume (shortly) thereafter.

NSOperationQueue allows for this and delegates to GCD when available so sounds like that may be a better fit from what you're describing.

Alex
@alblue

William Garrison

unread,
Apr 11, 2011, 11:27:16 PM4/11/11
to cocoa-...@googlegroups.com
+1 on using NSOperation for this. The ability to easily cancel out of
an NSOperation is its primary advantage over drinking GCD straight
from the bottle.

If you use the new NSBlockOperation, you can add as many blocks as you
want to the given operation, and still cancel them out with a -cancel.

Bill

Dave Zarzycki

unread,
Apr 12, 2011, 12:22:21 AM4/12/11
to cocoa-...@googlegroups.com
Ahem. As superficially convenient it is to cancel a NSOperation, doing so doesn't magically make life okay. One must be extremely careful to relinquish any resources owned by the cancelled operation. Otherwise, your code will at best leak and at worst hang. The GCD model may not be what you want, but it does force the discipline that your code needs in the long term.

Good luck,

davez

William Garrison

unread,
Apr 12, 2011, 5:56:14 PM4/12/11
to cocoa-...@googlegroups.com
PSA: It's a good idea to override -(void) cancel in your NSOperation
subclass. Just wily-nily invoking -cancel on an operation might leak
some memory.

Paul Marks

unread,
Apr 13, 2011, 2:17:27 AM4/13/11
to cocoa-...@googlegroups.com

On Apr 12, 2011, at 2:56 PM, William Garrison wrote:

> PSA: It's a good idea to override -(void) cancel in your NSOperation
> subclass. Just wily-nily invoking -cancel on an operation might leak
> some memory.

Not if you design your NSOperation subclass properly, it won't. Anything your operation needs should be retained in -init (or your designated initializer), and released in -dealloc and/or -finalize. That way it doesn't matter if your -main implementation even gets called (or cancelled); you'll still relinquish resources when the operation is deallocated.

The presence of -[NSOperation cancel] isn't a silver bullet; it will not stop a running -main in its tracks; you still need to insert cancellation points by checking [self isCancelled] and taking proper action. So, unless you resort to checking a flag (in this case, one NSOperation defines for you), the entirety of -main will run to completion just like a block submitted to a dispatch queue.

The only time I'd expect overriding -cancel to be necessary is if your -isConcurrent returns YES. If you're managing your own background processing, -cancel might want to do something more interesting than just setting a flag. (However, you should definitely call [super cancel] if you do override it.)

- Paul

Reply all
Reply to author
Forward
0 new messages