Stanislav Pankevich
Stanislav Pankevich

Reputation: 11368

"Block" main thread (dispatch_get_main_queue()) and (or not) run currentRunLoop periodically - what is the difference?

I have the following code:

- (void)test_with_running_runLoop {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    NSTimeInterval checkEveryInterval = 0.05;

    NSLog(@"Is main queue? : %d", dispatch_get_current_queue() == dispatch_get_main_queue());

    dispatch_async(dispatch_get_main_queue(), ^{
        sleep(1);
        NSLog(@"I will reach here, because currentRunLoop is run");
        dispatch_semaphore_signal(semaphore);
    });

    while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:checkEveryInterval]];

    NSLog(@"I will see this, after dispatch_semaphore_signal is called");
}

- (void)test_without_running_runLoop {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    NSLog(@"Is main queue? : %d", dispatch_get_current_queue() == dispatch_get_main_queue());

    dispatch_async(dispatch_get_main_queue(), ^{
        sleep(1);
        NSLog(@"I will not reach here, because currentRunLoop is not run");
        dispatch_semaphore_signal(semaphore);
    });

    NSLog(@"I will just hang here...");
    while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW));

    NSLog(@"I will see this, after dispatch_semaphore_signal is called");
}

producing the following:

Starting CurrentTests/test_with_running_runLoop
2012-11-29 08:14:29.781 Tests[31139:1a603] Is main queue? : 1
2012-11-29 08:14:30.784 Tests[31139:1a603] I will reach here, because currentRunLoop is run
2012-11-29 08:14:30.791 Tests[31139:1a603] I will see this, after dispatch_semaphore_signal is called
OK (1.011s)

Starting CurrentTests/test_without_running_runLoop
2012-11-29 08:14:30.792 Tests[31139:1a603] Is main queue? : 1
2012-11-29 08:14:30.797 Tests[31139:1a603] I will just hang here...

My questions are related to each other:

  1. If I understand it right, main queue (dispatch_get_main_queue()) is a serial queue. I block the main queue/main thread with dispatch_semaphore_wait, so why do I see the "I will reach here, because currentRunLoop is run" in the first test case (I'm OK with the second case - in my understanding, it does, what it should)?

  2. How a serial queue, having current executing task blocked, can have a next task (oh, this mysterious runLoop:beforeDate:) dispatched before the current one is unlocked?

I want to hear detailed and comprehensive answer on this, because very, very much things (here around on SO too) depend on this issue!

UPDATE: Besides the accepted answer, this SO topic has good answer to this question: Pattern for unit testing async queue that calls main queue on completion

Upvotes: 3

Views: 4322

Answers (1)

Wade Tregaskis
Wade Tregaskis

Reputation: 2018

Because the default runloop on the main thread has the special behaviour that, when run, it also processes for the main dispatch queue. You're not actually blocking in this case because you're telling dispatch_semaphore_wait to timeout immediately, which it's doing (returning non-zero, which evaluates in your if to true) - so you run through your while loop, where you drive the current run loop, and thus your enqueued block gets executed.

But my answer is broad because I'm not sure what part you're surprised by.

Upvotes: 6

Related Questions