Productive Waste of Time: Figuring out the main thread

In keeping with my tradition of random experiments of no practical use, I present another edition of “Productive Waste of Time”.

In a Cocoa app, there’s a notion of the main thread. It’s the thread where all events are dispatched. If you don’t create any threads yourself, all your code is going to run in this thread.

The issue of getting a hold of the main thread came up in #macsb. While the Cocoa docs talk a bit about the main thread and what should and should not happen there, they do not give you a way to actually get a handle on it. Daniel Jalkut, whose fault it is for bringing this all up, suggested using pthread_main_np(). While it may work, I looked for a Cocoa-only solution that didn’t assume that pthreads’ notion of a main thread would always align with Cocoa’s.

So, I whipped up this NSThread category to do just that. Just drop it in. No special hooks or hook up needed. It basically just uses -performSelectorOnMainThread:… to set a static var. I didn’t use any locks as I felt the worse that would happen is a bunch of threads set the variable multiple times with the same value. If there’s a subtlety in the memory model that I’m missing here, let me know. Also, if the main thread is tied up, you could potentially tie up other threads as well. Also also, if the main thread exits, I’m guessing bad stuff will happen though it will happen regardless of the code here.

@interface NSThread (MainThreadAdditions)

+ (NSThread *)mainThread;
- (BOOL)isMainThread;

@end

static NSThread		*_mainThread;

@implementation NSThread (MainThreadAdditions)

// Only call this from the main thread.
// Actually, do not call this at all. It is done for you.
+ (void)_setMainThread
{
    _mainThread = [NSThread currentThread];
}

+ (NSThread *)mainThread
{
    if (_mainThread == nil)
    {
        [self performSelectorOnMainThread:@selector(_setMainThread) withObject:nil waitUntilDone:YES];
    }
    return _mainThread;
}


- (BOOL)isMainThread
{
    return [[NSThread currentThread] isEqual:[NSThread mainThread]];
}

@end

Now, why would you need this? Beats me. It came up in discussion and I decided to roll with it. I provide the code here for your use. It is released under the ‘Splain license. This license says you can do whatever you want with this code under the condition that you ‘splain why you need it. Please post here.

Use this code at your own risk. I am not liable for any bad stuff that may happen as the result, directly or indirectly, of using this code. Side-effects may include nausea, dry mouth and brain rash. This is not a suppository.

Category: Cocoa, OS X, Programming 9 comments »

9 Responses to “Productive Waste of Time: Figuring out the main thread”

  1. Chris Ryland

    Luckily, I believe this code, along with many other good thread-related activities, will no longer be an issue in a few momths. Ahem. 😉

  2. mr_noodle

    Yes, I know of what you speak, which is, of course, of NOTHING, because there is NOTHING being divulged about future OS releases that may or may not exist. Nothing to see here. La-di-da. Oh, look over there!

  3. Jesper

    Convincing.

  4. Martin Wennerberg

    Method names starting with _ are reserved for Apple private use so I would suggest renaming the _setMainThread method to make sure that you aren’t overriding something in Apple’s implementation on NSThread. (http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_950_section_11.html)

    Also, as Apple might implement methods called -isMainThread and +mainThread in future versions of Mac OS X I would call them something that Apple wouldn’t.

  5. Ofri Wolfus

    Just for fun, here is another approach which doesn’t block the caller thread (yes, I know I’m leaking the main thread instance but it shouldn’t go away anyhow)
    static NSThread *_mainThread = nil;

    static __attribute__((constructor)) void _DPInitMainThread(void) {
    _mainThread = [[NSThread currentThread] retain];
    }

    + (NSThread *)mainThread {
    return _mainThread;
    }

  6. Adam Nohejl

    Suppose that the main thread is busy (it doesn’t cycle through its run loop for the moment) – then the +mainThread method gets blocked waiting. Suppose that, for some reason, the main thread will want to stay like this until some work is done in the detached thread (there may be for instance a lock it wants to acquire), but the detached thread is now blocked waiting for a selector to be executed on the main thread…

    Just a hypothetical scenario, but anyway, I don’t think this way of determining the main thread is a good idea. I would either use a different method or set the main thread static var beforehand (not very elegant).

  7. JustBill

    none of what you are suggesting is necessary at all. i’m relatively new to the Mac platform, but at least from what i can tell you have everything you need in the leopard release (not sure if it was there before).

    the major reason to run performSelectorOnMainThread is to ensure secondary threads don’t update the UI since thread safe access is accomplish through thread affinity using NSRunLoop.

    you can call performSelectorOnMainThread on any object or class, and providing the selector is implemented via a matching method, Cocoa will execute that method on the “main” thread.

    whomever is writing the code should be aware what their doing;nonetheless, you can ensure it by doing something similar to the following:

    -(void)doSomething: (NSNumber*)someValue
    {
    if (![NSThread isMainThread])
    [self performSelectorOnMainThread:@selector(doSomething:)
    withObject:somevalue
    waitUntilDone:NO];
    else
    [_txtField1 setIntValue: [someValue intValue]];
    }

  8. Adrian Bool

    If you needed this information in your application, would you not just set a global pointer to it as the app starts up – and you know you are in the main thread – for later use..? Gets rid of any possible deadlocks…

  9. bob

    Your -isMainThread method is an instance method, which suggests that it tells you if the receiver is the main thread. But it does not — it returns whether the current thread is the main thread, regardless of what the receiver is.

    Since you do not use the receiver at all, you should change it to a class method: +isMainThread, to aleviate the confusion.

    To correctly implement a -isMainThread method that tests if the receiver is the main thread, simply change [NSThread mainThread] to self:
    – (BOOL)isMainThread
    {
    return [self isEqual:[NSThread mainThread]];
    }


Leave a Reply



Back to top