Objective C: Need some clarifications about dispatch_queue_create and RunLoop. Sharing RunLoop between them.

I'm developing iOS framework with Objective C.

I create a dispatch_queue_t by using dispatch_queue_create. And call CFRunLoopRun() for run the Runloop in the queue.

But, It looks like the dispatch_queue_t has share the RunLoop. Some classes has add an invalid timer, and when I call the CFRunLoopRun(), It crashed on my side.

Sample code:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.queue1 = dispatch_queue_create("com.queue1", DISPATCH_QUEUE_CONCURRENT);
    self.queue2 = dispatch_queue_create("org.queue2", DISPATCH_QUEUE_CONCURRENT);
}

- (IBAction)btnButtonAction:(id)sender {
    
    dispatch_async(self.queue1, ^{
        
        NSString *runloop = [NSString stringWithFormat:@"%@", CFRunLoopGetCurrent()];
        runloop = [runloop substringWithRange:NSMakeRange(0, 22)];
        NSLog(@"Queue1 %p run: %@", self.queue1, runloop);
        
        //NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1 target:self selector:@selector(wrongSeletor:) userInfo:nil repeats:NO];
        //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    });
    
    dispatch_async(self.queue2, ^{
        NSString *runloop = [NSString stringWithFormat:@"%@", CFRunLoopGetCurrent()];
        runloop = [runloop substringWithRange:NSMakeRange(0, 22)];
        NSLog(@"Queue2 %p run: %@", self.queue2, runloop);
        
        CFRunLoopRun();
    });
}

Some time they take same RunLoop:

https://i.stack.imgur.com/wGcv3.png

=====

You can see the crash by uncomment the code of NSTimer. The NSTimer has been added in queue1, but it still running when call CFRunLoopRun() in queue2.

I have read some description like: https://stackoverflow.com/questions/38000727/need-some-clarifications-about-dispatch-queue-thread-and-nsrunloop

They told that: "system creates a run loop for the thread". But, in my check, they are sharing the RunLoop.

This is sad for me, because I facing that crashes happen when calling CFRunLoopRun() on production.

Can someone take a look at this.

Replies

Some time they take same RunLoop:

Right. Because run loops are tied to threads, and Dispatch has a worker pool of threads that it uses to run work items on queues. So, there are two cases:

  • If Dispatch uses two different worker threads to run the work items on queue1 and queue2, you’ll see different run loops.

  • If it uses the same worker thread for both work items, you’ll see the same run loop.

Mixing run loops and Dispatch is tricky, and it’s something I strongly recommend that you avoid. If you have an API that only works with run loops, then there are two good ways forward:

  • Do all of this work on the main thread, taking advantage of the main thread’s run loop.

  • Create your own thread and do all of this work on that thread.

Don’t use Dispatch as a ‘cheap’ way to create a thread. Doing that permanently ‘steals’ that thread from Dispatch’s pool of threads. Instead, use NSThread to create your own thread.

IMPORTANT Always add and remove run loop sources from the context of the thread itself. For an NSThread this is easy: Do the work using -performSelector:onThread:…. Cross-thread run loop source scheduling is another tricky proposition.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@ninh.asus Just to add couple points to @eskimo's excellent answer.

I'd suggest first look into why you need a CFRunLoop or NSRunLoop in the first place. I bet that many (if not all) regular use cases (timers, socket read/write notifications) have better equivalents in DispatchSource.

I just went through the experience myself by updating couple regular Objective-C libraries from CFRunLoop to DispatchSource to take them off the main queue and all the building blocks are there.