3

I'm trying to dispatch some code to the main queue via GCD on iOS but even the most simple tests always fail. In the end It boiled down to this:

static const int TICK_INTERVAL = 1;

#pragma UIApplicationDelegate implementation

- (void) doTick
{
    if (![NSThread isMainThread])
    {
        NSLog(@"Trying to dispatch . . .");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"test . . .");
        });
    }
}

- (void) startUpdate
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        timer_ = [NSTimer
                  scheduledTimerWithTimeInterval:TICK_INTERVAL
                  target:self
                  selector:@selector(backgroundUpdate)
                  userInfo:nil
                  repeats:NO
                  ];

        [[NSRunLoop currentRunLoop]
         addTimer:timer_
         forMode:NSRunLoopCommonModes
         ];

        [[NSRunLoop currentRunLoop] run];
    });

    UIBackgroundTaskIdentifier back =
    [[UIApplication sharedApplication]
     beginBackgroundTaskWithExpirationHandler:^{
         [self doTick];
         [[UIApplication sharedApplication]
          endBackgroundTask:back
          ];
     }];
}

-(void)backgroundUpdate
{
    [self doTick];

    UIBackgroundTaskIdentifier back =
    [[UIApplication sharedApplication]
     beginBackgroundTaskWithExpirationHandler:^{
         [self doTick];
         [[UIApplication sharedApplication]
          endBackgroundTask:back
          ];
     }];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            timer_ = [NSTimer
                     scheduledTimerWithTimeInterval:TICK_INTERVAL
                     target:self
                     selector:@selector(backgroundUpdate)
                     userInfo:nil
                     repeats:NO
                     ];

            [[NSRunLoop currentRunLoop]
             addTimer:timer_
             forMode:NSRunLoopCommonModes
             ];

            [[NSRunLoop currentRunLoop] run];
        });
}

- (id) init
{   
    self = [super init];
    [self startUpdate];
    return self;
}

That's the my AppDelegate. I would expect the NSLog to be executed in the main thread to log the test text above, but nothing happens. dispatch_sync code just waits forever and the breakpoint I placed inside the block is never reached.

I made sure that the code isn't executed in the main thread. Before testing with dispatch_sync, I experimented with dispatch_async in my app, the results being, of course, basically the same: nothing happens (without the blocking).

Interestingly enough, it just doesn't seem to work with the main queue, other queues (current queue, global queue) appear to work just fine.

I'm using Phonegap (Cordova) in my app, if that's of any significance.

Any ideas?

Thanks a lot!

1 Answers1

4

You should never dispatch_sync from a task executing on a queue to the same queue. This is guaranteed to deadlock on any serial queue like the main queue and is bad idea on concurrent queues. See Apple's Concurrency Programming Guide for more details. Since the application delegate's init method is run on the main thread, dispatch_sync to the main queue causes a deadlock.

dispatch_async to the main queue will work as long as you are running a normal NSRunLoop on the main thread. Of course your NSLog(@"test . . .") may be executed substantially later than the completion of your application delegate's init method.

torrey.lyons
  • 5,519
  • 1
  • 23
  • 30
  • Absolutely right, although I don't know why dispatch_async would fail. You sure -init is getting called? – EricS Jun 10 '12 at 01:30
  • Technically, you shouldn't `dispatch_sync` from a block that was sent to the same queue via `dispatch_sync`. I imagine a `dispatch_sync` inside of a `dispatch_async` would be fine even if it is the same queue. – borrrden Jun 10 '12 at 07:06
  • Thanks for the hint, I obviously failed at boiling it down. However, I am sure that the problem is not that my code gets executed in the main queue. Please see the updated code for explanation. – user1446796 Jun 10 '12 at 16:57
  • The revised code sample is pretty atypical. However, when I cut and paste it into a template iOS empty application it does work and repeatedly display both the "Trying to dispatch" and "test" messages. So whatever is wrong is outside the sample code and is in whatever is happening on the main thread after the -init. – torrey.lyons Jun 12 '12 at 04:28