Check if current thread is the main thread


#1

Is there a way to check if the current thread is the main thread? I saw some Python StackOverflow answers suggesting to check the name of the current thread. Is something like the following future-proof?

use std::thread;

fn main() {
    if let Some("main") = thread::current().name() {
        println!("On the main thread");
    }
    println!("{:?}", thread::current().name()); // prints Some("main")
}

#2

If there is no built-in way, you could use a thread-local boolean which is set to false by default, but set to true by the main thread on startup.


#3

Thanks for your response!

This code is part of a library. Is there a way for me to set the thread local value? How do I know I’m in the main thread in order to set the thread local value to the correct value?


#4

To answer this question, I guess I would need to know more about why you need the notion of a main thread in a library.


#5

One possibly feasible library approach is to have a “root” object which would be Sync, but not Send, and which would have a method to tell the caller from any thread whether it is in the same thread. Every instance of state in your library would then be created out of that root object and encapsulate an Arc to something inside it that is a clone of the Arc owned by the root object. That’s just off the top of my head, I didn’t verify if it works.


#6

My highly unscientific experimenting shows that Thread::name() returns Some("main") for the main thread on Linux. From memory, the main thread is also called "main" on Windows as (it’s printed whenever you panic), so you might get lucky and find this is common behaviour across all platforms.


#7

It’s explicitly set to “main” here:

But that’s only if the process is a Rust executable that went through rt::lang_start. Threads started in other languages (main or otherwise) will always return None AFAIK.

There’s also nothing stopping someone from calling any thread “main”:
Builder::new().name("main".to_owned()).spawn(|| ...)


#8

The library creates a window, windows can only be created on the main thread on MacOS. The winit crate (which I’m using through piston_window) panics if you try to create a window off of the main thread. This check would allow me to check that condition in advance of creating the window. winit uses some OS-specific/GUI-specific API as far as I can tell. I’m looking for something I can use without all of that.

So far name() seems to be the most reliable method. Though as people have pointed out, it has many drawbacks.


#9

I’m okay with this check being limited to Rust processes, though that is very good to know. Thanks! And yes someone could create a thread named main. That is unfortunate, but not a deal breaker in my situation.


#10

On macOS maybe use dispatch_[a]sync(dispatch_get_main_queue(), …) to always get it run on the main thread, instead of checking which thread it is?


#11

It’s nice to be able to avoid the overhead in the same-thread case with a statically type checked API, which Rust allows you to do. That’s why there are different types for thread-local and remote event loop handles in tokio_core.


#12

Then instead of checks at run time, you can have a non-Send context object type that cannot be safely passed to other threads. The window-creating method on the type would have to use one such object as &mut self, presumably the one that lives on the main thread and has ensured the necessary initialization, checks etc. in its construction method.


#13

Sorry for the necropost, but use the objc crate. After all, what matters is what Cocoa thinks.

#[cfg(target_os = "macos")]
fn gui_thread() -> bool {
    // Check if Cocoa thinks this is the 'main' thread.
    let nsthread = Class::get("NSObject").unwrap();
    objc![nsthread, isMainThread] != NO
}

#[cfg(not(target_os = "macos"))]
fn gui_thread() -> bool {
    true // Any thread can be the GUI thread!
}

#14

Sure, that would work if it was an entirely new GUI library being built from scratch, or if it was literally anything other than Cocoa (even Carbon), but for some reason, Apple just really wants the GUI thread to be the first thread.