Skip to content

Instantly share code, notes, and snippets.

@jspahrsummers
Last active January 20, 2021 11:55
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jspahrsummers/dbd861d425d783bd2e5a to your computer and use it in GitHub Desktop.
Save jspahrsummers/dbd861d425d783bd2e5a to your computer and use it in GitHub Desktop.
Synchronizing with multiple GCD queues
//
// DON'T do this, or else you risk a deadlock (e.g., by accidentally performing it in a different order somewhere)
//
dispatch_async(firstQueue, ^{
dispatch_sync(secondQueue, ^{
// code requiring both queues
});
});
//
// DO this instead
//
dispatch_queue_t concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(firstQueue, concurrentQueue);
dispatch_set_target_queue(secondQueue, concurrentQueue);
dispatch_barrier_async(concurrentQueue, ^{
// code requiring both queues
});
@qnoid
Copy link

qnoid commented Mar 2, 2016

@jspahrsummers thanks!

@mlaffitteIntego
Copy link

mlaffitteIntego commented Sep 27, 2017

@jspahrsummers
Hi, I have concerns about blocks already/still running for instance on firstQueue when dispatch_set_target_queue(firstQueue, concurrentQueue) is called. I suppose that after the call to dispatch_set_target_queue() they continue running on the original target queue. Hence, I fear that a dispatch_barrier on concurrentQueue used as the new target queue will not wait for those blocks running on their original target queues to complete before starting the barrier block.
What do you think about this ?
Marc

@mlaffitteIntego
Copy link

mlaffitteIntego commented Sep 28, 2017

I have run tests and my fear has been confirmed: Execution of the block added via dispatch_barrier may start before blocks added to both queues complete if those blocks have been added before the call to dispatch_set_target.
A solution ensuring that nothing runs anymore on both queues when the barrier block starts is to use nested barriers:

dispatch_barrier_async(firstQueue, ^{
	dispatch_barrier_sync(secondQueue, ^{
		// barrier block
		// when it is called, we are ensured that nothing is running anymore on both queues
	});
});

Note that even if the outer barrier can be asynch, the inner barriers should be synch.
This solution has also the advantage not to mess with the original queue targets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment