Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  main_thread_marker.rs

  Sprache: Rust
 

//! This belongs in `std` IMO, but wasn't accepted there:
//! <https://github.com/rust-lang/rust/pull/136616>
use core::fmt;
use core::marker::PhantomData;

use crate::rc::Allocated;
use crate::{ClassType, MainThreadOnly};

/// Whether the current thread is the main thread.
#[inline]
fn is_main_thread() -> bool {
    #[cfg(target_vendor = "apple")]
    {
        // Normally you would use `+[NSThread isMainThread]`, but benchmarks
        // have shown that calling the underlying `pthread_main_np` directly
        // is up to four times faster, so we use that instead.

        // SAFETY: The signature in here is the exact same as in `libc`.
        //
        // `pthread_main_np` is included via `libSystem` when `libstd` is
        // linked. All of this is done to avoid a dependency on the `libc`
        // crate.
        //
        // `extern "C"` is safe because this will never unwind.
        #[cfg_attr(not(feature = "std"), link(name = "c", kind = "dylib"))]
        extern "C" {
            fn pthread_main_np() -> core::ffi::c_int;
        }

        // SAFETY: Can be called from any thread.
        //
        // Apple's man page says:
        // > The pthread_main_np() function returns 1 if the calling thread is the initial thread, 0 if
        // > the calling thread is not the initial thread, and -1 if the thread's initialization has not
        // > yet completed.
        //
        // However, Apple's header says:
        // > Returns non-zero if the current thread is the main thread.
        //
        // So unclear if we should be doing a comparison against 1, or a negative comparison against 0?
        // To be safe, we compare against 1, though in reality, the current implementation can only ever
        // return 0 or 1:
        // https://github.com/apple-oss-distributions/libpthread/blob/libpthread-535/src/pthread.c#L1084-L1089
        unsafe { pthread_main_np() == 1 }
    }

    #[cfg(not(target_vendor = "apple"))]
    {
        // Fall back to isMainThread on non-Apple platforms, as
        // `pthread_main_np` is not always available there.
        unsafe { crate::msg_send![crate::class!(NSThread), isMainThread] }
    }
}

/// 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][main-functions] 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?](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47)
/// - [About Threaded Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html)
/// - [Thread Safety Summary](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-SW1)
/// - [Technical Note TN2028 - Threading Architectures](https://developer.apple.com/library/archive/technotes/tn/tn2028.html#//apple_ref/doc/uid/DTS10003065)
/// - [Thread Management](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html)
/// - [Swift's `@MainActor`](https://developer.apple.com/documentation/swift/mainactor)
/// - [Main Thread Only APIs on OS X](https://www.dribin.org/dave/blog/archives/2009/02/01/main_thread_apis/)
/// - [Mike Ash' article on thread safety](https://www.mikeash.com/pyblog/friday-qa-2009-01-09.html)
///
/// [main-functions]: https://doc.rust-lang.org/reference/crates-and-source-files.html#main-functions
///
///
/// # Main Thread Checker
///
/// Xcode provides a tool called the ["Main Thread Checker"][mtc] 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`:
///
/// ```console
/// 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`][mtc-reset].
///
/// ```console
/// 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][mtc-cfg] for details on further
/// configuration options.
///
/// [mtc]: https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early#Detect-improper-UI-updates-on-background-threads
/// [mtc-reset]: https://bryce.co/main-thread-checker-configuration/#mtc_reset_insert_libraries
/// [mtc-cfg]: https://bryce.co/main-thread-checker-configuration/
///
///
/// # Examples
///
/// Retrieve the main thread marker in different situations.
///
/// ```
/// use objc2::MainThreadMarker;
///
/// # // explicitly uses `fn main`
/// 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.
///
/// ```no_run
/// use objc2::MainThreadMarker;
/// # #[cfg(needs_app_kit)]
/// use objc2_app_kit::NSApplication;
/// #
/// # use objc2::runtime::NSObject as NSApplication;
/// # trait Foo {
/// #     fn sharedApplication(_mtm: MainThreadMarker) {}
/// # }
/// # impl Foo for NSApplication {}
///
/// # // explicitly uses `fn main`
/// 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() }
/// }
///
/// # // explicitly uses `fn main`
/// fn main() {
///     let mtm = MainThreadMarker::new().expect("must be on the main thread");
///     set(42, mtm);
///     assert_eq!(get(mtm), 42);
/// }
/// ```
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
//              ^^^^ this is valid because it's still `!Send` and `!Sync`.
pub struct MainThreadMarker {
    // No lifetime information needed; the main thread is static and available
    // throughout the entire program!

    // Ensure `!Send` and `!Sync`.
    _priv: PhantomData<*mut ()>,
}

impl MainThreadMarker {
    /// Construct a new `MainThreadMarker`.
    ///
    /// Returns [`None`] if the current thread was not the main thread.
    ///
    ///
    /// # Example
    ///
    /// Check whether the current thread is the main thread.
    ///
    /// ```
    /// use objc2::MainThreadMarker;
    ///
    /// if MainThreadMarker::new().is_some() {
    ///     // Is the main thread
    /// } else {
    ///     // Not the main thread
    /// }
    /// ```
    #[inline]
    #[doc(alias = "is_main_thread")]
    #[doc(alias = "pthread_main_np")]
    #[doc(alias = "isMainThread")]
    pub fn new() -> Option<Self> {
        if is_main_thread() {
            // SAFETY: We just checked that we are running on the main thread.
            Some(unsafe { Self::new_unchecked() })
        } else {
            None
        }
    }

    /// 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.
    #[inline]
    pub const unsafe fn new_unchecked() -> Self {
        // SAFETY: Upheld by caller.
        //
        // We can't debug_assert that this actually is the main thread, both
        // because this is `const` (to allow usage in `static`s), and because
        // users may sometimes want to create this briefly, e.g. to access an
        // API that in most cases requires the marker, but is safe to use
        // without in specific cases.
        Self { _priv: PhantomData }
    }

    /// 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`].
    #[inline]
    pub fn alloc<T: ClassType>(self) -> Allocated<T> {
        // SAFETY: We hold `MainThreadMarker`, and classes are either only
        // safe to allocate on the main thread, or safe to allocate
        // everywhere.
        unsafe { Allocated::alloc(T::class()) }
    }
}

/// Get a [`MainThreadMarker`] from a main-thread-only object.
///
/// This is a shorthand for [`MainThreadOnly::mtm`].
impl<T: ?Sized + MainThreadOnly> From<&T> for MainThreadMarker {
    #[inline]
    fn from(obj: &T) -> Self {
        obj.mtm()
    }
}

impl fmt::Debug for MainThreadMarker {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("MainThreadMarker").finish()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::panic::{RefUnwindSafe, UnwindSafe};

    static_assertions::assert_impl_all!(MainThreadMarker: Unpin, UnwindSafe, RefUnwindSafe, Sized);
    static_assertions::assert_not_impl_any!(MainThreadMarker: Send, Sync);

    #[test]
    fn debug() {
        // SAFETY: We don't use the marker for anything other than its Debug
        // impl, so this test doesn't actually need to run on the main thread!
        let marker = unsafe { MainThreadMarker::new_unchecked() };
        assert_eq!(std::format!("{marker:?}"), "MainThreadMarker");
    }

    #[test]
    fn test_not_main_thread() {
        let res = std::thread::spawn(|| MainThreadMarker::new().is_none())
            .join()
            .unwrap();
        assert!(res);
    }
}

Messung V0.5 in Prozent
C=88 H=94 G=90

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002