Last Updated: February 25, 2016
·
5.64K
· dagams

How to wait for GCD queues to suspend before performing a task

I've recently been faced with the following problem: we control access to some resources (ie database/file system) by funnelling all access to these resources through a serial queue. In that way, we can ensure that no two threads access the DB concurrently by accident. However, we now needed a way to pause all access to the database while we swap the database file underneath. How to make sure that no task is accessing the db at the moment before commencing with the file swap, and ensure that nothing happens during the operation?

The idea we had was to dispatch_suspend the queue from within a task submitted to that same queue, and resume the queue only after the file swapping is finished. You can combine this with dispatch_group to wait for the task to finish before continuing:

dispatch_queue_t queue1 = dispatch_queue_create("com.mekentosj.queue1", 0);
dispatch_queue_t queue2 = dispatch_queue_create("com.mekentosj.queue2", 0);
dispatch_group_t group = dispatch_group_create();

dispatch_async(queue1, ^{
    NSLog(@"Job 1 on queue 1...");
    sleep(1);
    NSLog(@"Done 1");
});

dispatch_async(queue2, ^{
    NSLog(@"Job 1 on queue 2...");
    sleep(1);
    NSLog(@"Done 2");
});

dispatch_group_async(group, queue1, ^{
    NSLog(@"Suspending queue 1");
    dispatch_suspend(queue1);
});
dispatch_group_async(group, queue2, ^{
    NSLog(@"Suspending queue 2");
    dispatch_suspend(queue2);
});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"Finished waiting for queues to stop, doing something...");
dispatch_async(queue1, ^{
    NSLog(@"Job 2 queue 1");
});
dispatch_async(queue2, ^{
    NSLog(@"Job 2 queue 2");
});
NSLog(@"This will happen before the two logs above");

dispatch_resume(queue1);
dispatch_resume(queue2);

sleep(1); // just let the background jobs finish