Suspending GCD query problem

Kostas.N picture Kostas.N · Jul 18, 2011 · Viewed 32.7k times · Source

i have trouble suspending a gcd query. Here is some code that demonstrates the problem:

static dispatch_queue_t q=nil;

static void test(int a){
    if(q){
        dispatch_suspend(q);
        dispatch_release(q);
        q=nil;
    }
    q=dispatch_get_global_queue(0,0);
    dispatch_async(q,^ {
        while(1){NSLog(@"query %d",a);sleep(2);}
    });

}

int main(int argc, const char* argv[]){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    test(1);

    //blah blah blah

    test(2);

    while(1){}
    [pool release];
    return 0;
}

What I'm trying to do is suspend,release and reinitialise query q when function test is called the second time, but apparenty my code is wrong and both instances of query q continue to run.

Your help is much appreciated, thank you.

Answer

Ryan picture Ryan · Jul 18, 2011

Any blocks that have been dispatched to your queue asynchronously before you actually call dispatch_suspend() will get run before the suspend takes effect. In your code you are firing off a bunch of blocks asynchronously, so some are probably still in the queue when you call test(2), and those blocks will be executed.

If you want to be able to cancel your running jobs, you'll need to do so in your own logic. GCD purposefully doesn't expose a true cancellation API. You could do something like this:

@interface Canceller
{
    BOOL _shouldCancel;
}
- (void)setShouldCancel:(BOOL)shouldCancel;
- (BOOL)shouldCancel;
@end

@implementation Canceller
- (void)setShouldCancel:(BOOL)shouldCancel {
    _shouldCancel = shouldCancel;
}
- (BOOL)shouldCancel {
    return _shouldCancel;
}
@end

static void test(int a){
    static Canceller * canceller = nil;

    if(q){
        [canceller setShouldCancel:YES];
        [canceller release];
        dispatch_suspend(q);
        dispatch_release(q);
        q=nil;
    }
    canceller = [[Canceller alloc] init];
    q=dispatch_get_global_queue(0,0);
    dispatch_async(q,^ {
        while(![canceller shouldCancel]){NSLog(@"query %d",a);sleep(2);}
    });

}

In this way, each block will keep a reference to an object that knows if it should stop doing work.