objc2/main_thread_marker.rs
1//! This belongs in `std` IMO, but wasn't accepted there:
2//! <https://github.com/rust-lang/rust/pull/136616>
3use core::fmt;
4use core::marker::PhantomData;
5
6use crate::rc::Allocated;
7use crate::{ClassType, MainThreadOnly};
8
9/// Whether the current thread is the main thread.
10#[inline]
11fn is_main_thread() -> bool {
12 #[cfg(target_vendor = "apple")]
13 {
14 // Normally you would use `+[NSThread isMainThread]`, but benchmarks
15 // have shown that calling the underlying `pthread_main_np` directly
16 // is up to four times faster, so we use that instead.
17
18 // SAFETY: The signature in here is the exact same as in `libc`.
19 //
20 // `pthread_main_np` is included via `libSystem` when `libstd` is
21 // linked. All of this is done to avoid a dependency on the `libc`
22 // crate.
23 //
24 // `extern "C"` is safe because this will never unwind.
25 #[cfg_attr(not(feature = "std"), link(name = "c", kind = "dylib"))]
26 extern "C" {
27 fn pthread_main_np() -> core::ffi::c_int;
28 }
29
30 // SAFETY: Can be called from any thread.
31 //
32 // Apple's man page says:
33 // > The pthread_main_np() function returns 1 if the calling thread is the initial thread, 0 if
34 // > the calling thread is not the initial thread, and -1 if the thread's initialization has not
35 // > yet completed.
36 //
37 // However, Apple's header says:
38 // > Returns non-zero if the current thread is the main thread.
39 //
40 // So unclear if we should be doing a comparison against 1, or a negative comparison against 0?
41 // To be safe, we compare against 1, though in reality, the current implementation can only ever
42 // return 0 or 1:
43 // https://github.com/apple-oss-distributions/libpthread/blob/libpthread-535/src/pthread.c#L1084-L1089
44 unsafe { pthread_main_np() == 1 }
45 }
46
47 #[cfg(not(target_vendor = "apple"))]
48 {
49 // Fall back to isMainThread on non-Apple platforms, as
50 // `pthread_main_np` is not always available there.
51 unsafe { crate::msg_send![crate::class!(NSThread), isMainThread] }
52 }
53}
54
55/// A marker type for functionality only available on the main thread.
56///
57/// The main thread is a system-level property on Apple/Darwin platforms, and
58/// has extra capabilities not available on other threads. This is usually
59/// relevant when using native GUI frameworks, where most operations must be
60/// done on the main thread.
61///
62/// This type enables you to manage that capability. By design, it is neither
63/// [`Send`] nor [`Sync`], and can only be created on the main thread, meaning
64/// that if you have an instance of this, you are guaranteed to be on the main
65/// thread / have the "main-thread capability".
66///
67/// [The `main` function][main-functions] will run on the main thread. This
68/// type can also be used with `#![no_main]` or other such cases where Rust is
69/// not defining the binary entry point.
70///
71/// See the following links for more information on main-thread-only APIs:
72/// - [Are the Cocoa Frameworks Thread Safe?](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47)
73/// - [About Threaded Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html)
74/// - [Thread Safety Summary](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-SW1)
75/// - [Technical Note TN2028 - Threading Architectures](https://developer.apple.com/library/archive/technotes/tn/tn2028.html#//apple_ref/doc/uid/DTS10003065)
76/// - [Thread Management](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html)
77/// - [Swift's `@MainActor`](https://developer.apple.com/documentation/swift/mainactor)
78/// - [Main Thread Only APIs on OS X](https://www.dribin.org/dave/blog/archives/2009/02/01/main_thread_apis/)
79/// - [Mike Ash' article on thread safety](https://www.mikeash.com/pyblog/friday-qa-2009-01-09.html)
80///
81/// [main-functions]: https://doc.rust-lang.org/reference/crates-and-source-files.html#main-functions
82///
83///
84/// # Main Thread Checker
85///
86/// Xcode provides a tool called the ["Main Thread Checker"][mtc] which
87/// verifies that UI APIs are being used from the correct thread. This is not
88/// as principled as `MainThreadMarker`, but is helpful for catching mistakes.
89///
90/// You can use this tool on macOS by loading `libMainThreadChecker.dylib`
91/// into your process using `DYLD_INSERT_LIBRARIES`:
92///
93/// ```console
94/// DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib MTC_RESET_INSERT_LIBRARIES=0 cargo run
95/// ```
96///
97/// If you're not running your binary through Cargo, you can omit
98/// [`MTC_RESET_INSERT_LIBRARIES`][mtc-reset].
99///
100/// ```console
101/// DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib target/debug/myapp
102/// ```
103///
104/// If you're developing for iOS, you probably better off enabling the tool in
105/// Xcode's own UI.
106///
107/// See [this excellent blog post][mtc-cfg] for details on further
108/// configuration options.
109///
110/// [mtc]: https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early#Detect-improper-UI-updates-on-background-threads
111/// [mtc-reset]: https://bryce.co/main-thread-checker-configuration/#mtc_reset_insert_libraries
112/// [mtc-cfg]: https://bryce.co/main-thread-checker-configuration/
113///
114///
115/// # Examples
116///
117/// Retrieve the main thread marker in different situations.
118///
119/// ```
120/// use objc2::MainThreadMarker;
121///
122/// # // explicitly uses `fn main`
123/// fn main() {
124/// // The thread that `fn main` runs on is the main thread.
125/// assert!(MainThreadMarker::new().is_some());
126///
127/// // Subsequently spawned threads are not the main thread.
128/// std::thread::spawn(|| {
129/// assert!(MainThreadMarker::new().is_none());
130/// }).join().unwrap();
131/// }
132/// ```
133///
134/// Use when accessing APIs that are only safe to use on the main thread.
135///
136/// ```no_run
137/// use objc2::MainThreadMarker;
138/// # #[cfg(needs_app_kit)]
139/// use objc2_app_kit::NSApplication;
140/// #
141/// # use objc2::runtime::NSObject as NSApplication;
142/// # trait Foo {
143/// # fn sharedApplication(_mtm: MainThreadMarker) {}
144/// # }
145/// # impl Foo for NSApplication {}
146///
147/// # // explicitly uses `fn main`
148/// fn main() {
149/// // Create a new MainThreadMarker.
150/// let mtm = MainThreadMarker::new().expect("must be on the main thread");
151///
152/// // NSApplication is only usable on the main thread,
153/// // so we need to pass the marker as an argument.
154/// let app = NSApplication::sharedApplication(mtm);
155///
156/// // Do something with the application
157/// // app.run();
158/// }
159/// ```
160///
161/// Create a static that is only usable on the main thread. This is similar to
162/// a thread-local, but can be more efficient because it doesn't handle
163/// multiple threads.
164///
165/// See also `dispatch2::MainThreadBound`.
166///
167/// ```
168/// use objc2::MainThreadMarker;
169/// use std::cell::UnsafeCell;
170///
171/// struct SyncUnsafeCell<T>(UnsafeCell<T>);
172///
173/// unsafe impl<T> Sync for SyncUnsafeCell<T> {}
174///
175/// static MAIN_THREAD_ONLY_VALUE: SyncUnsafeCell<i32> = SyncUnsafeCell(UnsafeCell::new(0));
176///
177/// fn set(value: i32, _mtm: MainThreadMarker) {
178/// // SAFETY: We have an instance of `MainThreadMarker`, so we know that
179/// // we're running on the main thread (and thus do not need any
180/// // synchronization, since the only accesses to this value is from the
181/// // main thread).
182/// unsafe { *MAIN_THREAD_ONLY_VALUE.0.get() = value };
183/// }
184///
185/// fn get(_mtm: MainThreadMarker) -> i32 {
186/// // SAFETY: Same as above.
187/// unsafe { *MAIN_THREAD_ONLY_VALUE.0.get() }
188/// }
189///
190/// # // explicitly uses `fn main`
191/// fn main() {
192/// let mtm = MainThreadMarker::new().expect("must be on the main thread");
193/// set(42, mtm);
194/// assert_eq!(get(mtm), 42);
195/// }
196/// ```
197#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
198// ^^^^ this is valid because it's still `!Send` and `!Sync`.
199pub struct MainThreadMarker {
200 // No lifetime information needed; the main thread is static and available
201 // throughout the entire program!
202
203 // Ensure `!Send` and `!Sync`.
204 _priv: PhantomData<*mut ()>,
205}
206
207impl MainThreadMarker {
208 /// Construct a new `MainThreadMarker`.
209 ///
210 /// Returns [`None`] if the current thread was not the main thread.
211 ///
212 ///
213 /// # Example
214 ///
215 /// Check whether the current thread is the main thread.
216 ///
217 /// ```
218 /// use objc2::MainThreadMarker;
219 ///
220 /// if MainThreadMarker::new().is_some() {
221 /// // Is the main thread
222 /// } else {
223 /// // Not the main thread
224 /// }
225 /// ```
226 #[inline]
227 #[doc(alias = "is_main_thread")]
228 #[doc(alias = "pthread_main_np")]
229 #[doc(alias = "isMainThread")]
230 pub fn new() -> Option<Self> {
231 if is_main_thread() {
232 // SAFETY: We just checked that we are running on the main thread.
233 Some(unsafe { Self::new_unchecked() })
234 } else {
235 None
236 }
237 }
238
239 /// Construct a new `MainThreadMarker` without first checking whether the
240 /// current thread is the main one.
241 ///
242 ///
243 /// # Safety
244 ///
245 /// The current thread must be the main thread.
246 ///
247 /// Alternatively, you may create this briefly if you know that a an API
248 /// is safe in a specific case, but is not marked so. If you do that, you
249 /// must ensure that any use of the marker is actually safe to do from
250 /// another thread than the main one.
251 #[inline]
252 pub const unsafe fn new_unchecked() -> Self {
253 // SAFETY: Upheld by caller.
254 //
255 // We can't debug_assert that this actually is the main thread, both
256 // because this is `const` (to allow usage in `static`s), and because
257 // users may sometimes want to create this briefly, e.g. to access an
258 // API that in most cases requires the marker, but is safe to use
259 // without in specific cases.
260 Self { _priv: PhantomData }
261 }
262
263 /// Allocate a new instance of the specified class on the main thread.
264 ///
265 /// This can be useful in certain situations, such as generic contexts
266 /// where you don't know whether the class is main thread or not, but
267 /// usually you should prefer [`MainThreadOnly::alloc`].
268 #[inline]
269 pub fn alloc<T: ClassType>(self) -> Allocated<T> {
270 // SAFETY: We hold `MainThreadMarker`, and classes are either only
271 // safe to allocate on the main thread, or safe to allocate
272 // everywhere.
273 unsafe { Allocated::alloc(T::class()) }
274 }
275}
276
277/// Get a [`MainThreadMarker`] from a main-thread-only object.
278///
279/// This is a shorthand for [`MainThreadOnly::mtm`].
280impl<T: ?Sized + MainThreadOnly> From<&T> for MainThreadMarker {
281 #[inline]
282 fn from(obj: &T) -> Self {
283 obj.mtm()
284 }
285}
286
287impl fmt::Debug for MainThreadMarker {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 f.debug_tuple("MainThreadMarker").finish()
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use std::panic::{RefUnwindSafe, UnwindSafe};
297
298 static_assertions::assert_impl_all!(MainThreadMarker: Unpin, UnwindSafe, RefUnwindSafe, Sized);
299 static_assertions::assert_not_impl_any!(MainThreadMarker: Send, Sync);
300
301 #[test]
302 fn debug() {
303 // SAFETY: We don't use the marker for anything other than its Debug
304 // impl, so this test doesn't actually need to run on the main thread!
305 let marker = unsafe { MainThreadMarker::new_unchecked() };
306 assert_eq!(std::format!("{marker:?}"), "MainThreadMarker");
307 }
308
309 #[test]
310 fn test_not_main_thread() {
311 let res = std::thread::spawn(|| MainThreadMarker::new().is_none())
312 .join()
313 .unwrap();
314 assert!(res);
315 }
316}