Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 553
user1446796 Avatar asked Apr 10 '26 02:04

user1446796


1 Answers

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.

like image 184
torrey.lyons Avatar answered Apr 11 '26 17:04

torrey.lyons



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!