Oh, here it came to my mind why you get what you get. Notes in line:
dispatch_sync(queue1, ^{
When you get to this point, the "current queue" queue1
if(dispatch_get_specific(kQueue1Key))
You request the current queue for the value that it has for kQueue1Key , you set it earlier so that it returns to you.
{ NSLog(@"I'm expecting this line to run (A)"); dispatch_sync(queue2, ^{
When you get to this point, now the "current queue" queue2
NSLog(@"I'm expecting this line to run (B)"); if(dispatch_get_specific(kQueue2Key))
You request the current queue for the value that it has for kQueue2Key , you set it earlier so that it returns you.
{ if(dispatch_get_specific(kQueue1Key))
Now you are requesting the current queue for the value that it has for kQueue1Key . Since the current queue is queue2 and you never set the value from kQueue1Key to queue2 , you return NULL .
{ NSLog(@"I'm expecting this line to run (C)"); } else { [NSException raise:NSInternalInconsistencyException format:@"Should not end up here (C)"]; }
The misunderstanding here is that dispatch_get_specific does not cross the stack of nested queues, it crosses the line of targeting the queue. For example, if you did this,
static void * kQueue1Key = (void*)"key1"; static void * kQueue2Key = (void*)"key2"; dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL); dispatch_queue_set_specific(queue1, kQueue1Key, (void *)kQueue1Key, NULL); dispatch_queue_set_specific(queue2, kQueue2Key, (void *)kQueue2Key, NULL);
... a targeting relationship is one that passes, not a stack relationship. It would be nice if there was something that crossed the stack relationship, but I don't know anything (which you would not need to implement yourself).