1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use static_assertions::assert_not_impl_any;

/// A token that (almost) proves we are on the main thread.
///
/// Certain operations are only allowed on the main thread.
/// These operations should require this token.
/// For instance, any function using file dialogs (e.g. using [`rfd`](https://docs.rs/rfd/latest/rfd/)) should require this token.
///
/// The token should only be constructed in `fn main`, using [`MainThreadToken::i_promise_i_am_on_the_main_thread`],
/// and then be passed down the call tree to where it is needed.
/// [`MainThreadToken`] is neither `Send` nor `Sync`,
/// thus guaranteeing that it cannot be found in other threads.
///
/// Of course, there is nothing stopping you from calling [`MainThreadToken::i_promise_i_am_on_the_main_thread`] from a background thread,
/// but PLEASE DON'T DO THAT.
/// In other words, don't use this as a guarantee for unsafe code.
///
/// There is also [`MainThreadToken::from_egui_ui`] which uses the implicit guarantee of egui
/// (which _usually_ is run on the main thread) to construct a [`MainThreadToken`].
/// Use this only in a code base where you are sure that egui is running only on the main thread.
#[derive(Clone, Copy)]
pub struct MainThreadToken {
    /// Prevent from being sent between threads.
    ///
    /// Workaround until `impl !Send for X {}` is stable.
    _dont_send_me: std::marker::PhantomData<*const ()>,
}

impl MainThreadToken {
    /// Only call this from `fn main`, or you may get weird runtime errors!
    pub fn i_promise_i_am_on_the_main_thread() -> Self {
        // On web there is no thread name.
        // On native the thread-name is always "main" in Rust,
        // but there is nothing preventing a user from also naming another thread "main".
        // In any case, since `MainThreadToken` is just best-effort, we only check this in debug builds.
        #[cfg(not(target_arch = "wasm32"))]
        debug_assert_eq!(std::thread::current().name(), Some("main"),
            "DEBUG ASSERT: Trying to construct a MainThreadToken on a thread that is not the main thread!"
        );

        Self {
            _dont_send_me: std::marker::PhantomData,
        }
    }

    /// We _should_ only create an [`egui::Ui`] on the main thread,
    /// so having it is good enough to "prove" that we are on the main thread.
    ///
    /// Use this only in a code base where you are sure that egui is running only on the main thread.
    ///
    /// In theory there is nothing preventing anyone from creating a [`egui::Ui`] on another thread,
    /// but practice that is unlikely (or intentionally malicious).
    #[cfg(feature = "egui")]
    pub fn from_egui_ui(_ui: &egui::Ui) -> Self {
        Self::i_promise_i_am_on_the_main_thread()
    }
}

assert_not_impl_any!(MainThreadToken: Send, Sync);
assert_not_impl_any!(&MainThreadToken: Send, Sync);