How to use dispatch_queue_set_specific() and dispatch_get_specific()

grand-central-dispatch, objective-c-blocks

Solution

Oh here, it popped into my head why you're getting what you're getting. Notes in line:

dispatch_sync(queue1, ^{

When you get to this point, the "current queue" is `queue1`

    if(dispatch_get_specific(kQueue1Key))

You're asking the current queue for the value it has for `kQueue1Key`, you set that earlier, so it gives it back to you.

    {
        NSLog(@"I'm expecting this line to run (A)");

        dispatch_sync(queue2, ^{

When you get to this point, the "current queue" is now `queue2`

            NSLog(@"I'm expecting this line to run (B)");

            if(dispatch_get_specific(kQueue2Key))

You're asking the current queue for the value it has for `kQueue2Key`, you set that earlier, so it gives it back to you.

            {

                if(dispatch_get_specific(kQueue1Key))

You're now asking the current queue for the value it has for `kQueue1Key`. Since the current queue is `queue2` and you never set a value with `kQueue1Key` on `queue2` you get back `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` doesn't traverse the stack of nested queues, it traverses the queue targeting lineage. For instance, if you did this instead,

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);

// Set Queue2 to target Queue1
dispatch_set_target_queue(queue2, queue1);

dispatch_sync(queue2, ^{

    if(dispatch_get_specific(kQueue1Key))
    {
        NSLog(@"I'm expecting this line to run (A)");
    }
    else
    {
        [NSException raise:NSInternalInconsistencyException format:@"Should not end up here (C)"];
    }

    if(dispatch_get_specific(kQueue2Key))
    {
        NSLog(@"I'm expecting this line to run (B)");
    }
    else
    {
        [NSException raise:NSInternalInconsistencyException format:@"Should not end up here (C)"];
    }
});

...the targeting relationship is the one that gets traversed, not the stack relationship. It would be nice if there were something that traversed the stack relationship, but I'm not aware of anything (that you wouldn't have to implement yourself).

Problem

I'm having a hard time finding good examples on how to use these functions. ``` static void * kQueue1Key = "key1"; static void * kQueue2Key = "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); dispatch_sync(queue1, ^{ if(dispatch_get_specific(kQueue1Key)) { NSLog(@"I'm expecting this line to run (A)"); dispatch_sync(queue2, ^{ NSLog(@"I'm expecting this line to run (B)"); if(dispatch_get_specific(kQueue2Key)) { if(dispatch_get_specific(kQueue1Key)) { NSLog(@"I'm expecting this line to run (C)"); } else { [NSException raise:NSInternalInconsistencyException format:@"Should not end up here (C)"]; } } else { [NSException raise:NSInternalInconsistencyException format:@"Should not end up here (B)"]; } }); } else { [NSException raise:NSInternalInconsistencyException format:@"Should not end up here (A)"]; } }); ``` Result ``` I'm expecting this line to run (A) I'm expecting this line to run (B) *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Should not end up here (C)' ``` Is it expected behavior? If I were to dispatch_sync to queue1 since I'm not on the queue I would deadlock. What am I missing?

Original source

Related problems