pub struct MainThreadMarker { /* private fields */ }
Expand description
A marker type for functionality only available on the main thread.
The main thread is a system-level property on Apple/Darwin platforms, and has extra capabilities not available on other threads. This is usually relevant when using native GUI frameworks, where most operations must be done on the main thread.
This type enables you to manage that capability. By design, it is neither
Send
nor Sync
, and can only be created on the main thread, meaning
that if you have an instance of this, you are guaranteed to be on the main
thread / have the “main-thread capability”.
The main
function will run on the main thread. This
type can also be used with #![no_main]
or other such cases where Rust is
not defining the binary entry point.
See the following links for more information on main-thread-only APIs:
- Are the Cocoa Frameworks Thread Safe?
- About Threaded Programming
- Thread Safety Summary
- Technical Note TN2028 - Threading Architectures
- Thread Management
- Swift’s
@MainActor
- Main Thread Only APIs on OS X
- Mike Ash’ article on thread safety
§Main Thread Checker
Xcode provides a tool called the “Main Thread Checker” which
verifies that UI APIs are being used from the correct thread. This is not
as principled as MainThreadMarker
, but is helpful for catching mistakes.
You can use this tool on macOS by loading libMainThreadChecker.dylib
into your process using DYLD_INSERT_LIBRARIES
:
DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib MTC_RESET_INSERT_LIBRARIES=0 cargo run
If you’re not running your binary through Cargo, you can omit
MTC_RESET_INSERT_LIBRARIES
.
DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib target/debug/myapp
If you’re developing for iOS, you probably better off enabling the tool in Xcode’s own UI.
See this excellent blog post for details on further configuration options.
§Examples
Retrieve the main thread marker in different situations.
use objc2::MainThreadMarker;
fn main() {
// The thread that `fn main` runs on is the main thread.
assert!(MainThreadMarker::new().is_some());
// Subsequently spawned threads are not the main thread.
std::thread::spawn(|| {
assert!(MainThreadMarker::new().is_none());
}).join().unwrap();
}
Use when accessing APIs that are only safe to use on the main thread.
use objc2::MainThreadMarker;
use objc2_app_kit::NSApplication;
fn main() {
// Create a new MainThreadMarker.
let mtm = MainThreadMarker::new().expect("must be on the main thread");
// NSApplication is only usable on the main thread,
// so we need to pass the marker as an argument.
let app = NSApplication::sharedApplication(mtm);
// Do something with the application
// app.run();
}
Create a static that is only usable on the main thread. This is similar to a thread-local, but can be more efficient because it doesn’t handle multiple threads.
See also dispatch2::MainThreadBound
.
use objc2::MainThreadMarker;
use std::cell::UnsafeCell;
struct SyncUnsafeCell<T>(UnsafeCell<T>);
unsafe impl<T> Sync for SyncUnsafeCell<T> {}
static MAIN_THREAD_ONLY_VALUE: SyncUnsafeCell<i32> = SyncUnsafeCell(UnsafeCell::new(0));
fn set(value: i32, _mtm: MainThreadMarker) {
// SAFETY: We have an instance of `MainThreadMarker`, so we know that
// we're running on the main thread (and thus do not need any
// synchronization, since the only accesses to this value is from the
// main thread).
unsafe { *MAIN_THREAD_ONLY_VALUE.0.get() = value };
}
fn get(_mtm: MainThreadMarker) -> i32 {
// SAFETY: Same as above.
unsafe { *MAIN_THREAD_ONLY_VALUE.0.get() }
}
fn main() {
let mtm = MainThreadMarker::new().expect("must be on the main thread");
set(42, mtm);
assert_eq!(get(mtm), 42);
}
Implementations§
Source§impl MainThreadMarker
impl MainThreadMarker
Sourcepub fn new() -> Option<MainThreadMarker>
pub fn new() -> Option<MainThreadMarker>
Sourcepub const unsafe fn new_unchecked() -> MainThreadMarker
pub const unsafe fn new_unchecked() -> MainThreadMarker
Construct a new MainThreadMarker
without first checking whether the
current thread is the main one.
§Safety
The current thread must be the main thread.
Alternatively, you may create this briefly if you know that a an API is safe in a specific case, but is not marked so. If you do that, you must ensure that any use of the marker is actually safe to do from another thread than the main one.
Sourcepub fn alloc<T>(self) -> Allocated<T>where
T: ClassType,
pub fn alloc<T>(self) -> Allocated<T>where
T: ClassType,
Allocate a new instance of the specified class on the main thread.
This can be useful in certain situations, such as generic contexts
where you don’t know whether the class is main thread or not, but
usually you should prefer MainThreadOnly::alloc
.
Trait Implementations§
Source§impl Clone for MainThreadMarker
impl Clone for MainThreadMarker
Source§fn clone(&self) -> MainThreadMarker
fn clone(&self) -> MainThreadMarker
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreSource§impl Debug for MainThreadMarker
impl Debug for MainThreadMarker
Source§impl<T> From<&T> for MainThreadMarkerwhere
T: MainThreadOnly + ?Sized,
Get a MainThreadMarker
from a main-thread-only object.
impl<T> From<&T> for MainThreadMarkerwhere
T: MainThreadOnly + ?Sized,
Get a MainThreadMarker
from a main-thread-only object.
This is a shorthand for MainThreadOnly::mtm
.