user1446796
user1446796

Reputation: 31

dispatching to main queue always fails

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!

Upvotes: 3

Views: 962

Answers (1)

torrey.lyons
torrey.lyons

Reputation: 5589

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.

Upvotes: 4

Related Questions