Carbon/Cocoa Inter Thread Communication

Carbon/Cocoa Inter Thread Communication

Apple has a nice overview of thread communication means in their multithreading documentation (for both, Carbon and Carbon). However, I think the documentation is missing another technique that is very simple.

Let me get to the beginning of my problem: I have a two threads, one main thread and a secondary thread. The main thread can run a Carbon or Cocoa eventloop. From my secondary thread I want to be able to perfom certain actions in the main thread.

So what I need is to wake up the event loop in the main thread and let a callback function be called in the main thread. The techniques in the above documentation (port-based communication, e.g.) would work for me, but it seems overcomplicated. Especially since I need a solution that works with both, Carbon and Cocoa, I prefer a solution that works directly on the core foundation run loop (the Carbon and Cocoa event loops are based on it).

But using the CFMessagePort APIs was not that enjoyable and since I only need to wake up the thread without passing any information, it is really too much. So I was looking for another solution and it is actually quite easy: just create your own CFRunLoopSource, add it to the main thread’s run loop and in the secondary thread you can simply signal the run loop source and the callback is called in the main thread.

Here is some code — create the event loop source in the main thread (invocationHandler is the callback that should be called in the main thread):

CFRunLoopSourceContext context = {
    0,                 // version
    0,                 // info
    0,                 // retain
    0,                 // release
    0,                 // copyDescription
    0,                 // equal
    0,                 // hash
    0,                 // schedule
    0,                 // cancel
    &invocationHandler // perform
};
runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
if (runLoopSource)
    CFRunLoopAddSource(mainThreadsRunLoop, runLoopSource, kCFRunLoopCommonModes);

In the secondary thread, I trigger the invocationHandler to be called in the main thread by simply:

FRunLoopSourceSignal(runLoopSource);
CFRunLoopWakeUp(mainThreadsRunLoop);

And that’s all. Compared to a solution that uses CFMessagePort, it is really way easier (both in lines of code and to understand the code).