Prechádzať zdrojové kódy

feat(core): panic when a dispatcher getter is used on the main thread (#2455)

Lucas Fernandes Nogueira 4 rokov pred
rodič
commit
50ffdc06fb

+ 7 - 0
.changes/panic-dispatcher-getter-main-thread.md

@@ -0,0 +1,7 @@
+---
+"tauri": patch
+"tauri-runtime": patch
+"tauri-runtime-wry": patch
+---
+
+Panic when a dispatcher getter method (`Window`, `GlobalShortcutHandle`, `ClipboardManager` and `MenuHandle` APIs) is called on the main thread.

+ 10 - 8
core/tauri-runtime-wry/src/lib.rs

@@ -112,10 +112,11 @@ pub type WindowMenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
 
 macro_rules! dispatcher_getter {
   ($self: ident, $message: expr) => {{
-    if current_thread().id() == $self.context.main_thread_id
-      && !$self.context.is_event_loop_running.load(Ordering::Relaxed)
-    {
-      panic!("This API cannot be called when the event loop is not running");
+    if current_thread().id() == $self.context.main_thread_id {
+      panic!("This API cannot be called on the main thread. Try using `std::thread::spawn` or `tauri::async_runtime::spawn`.");
+    }
+    if !$self.context.is_event_loop_running.load(Ordering::Relaxed) {
+      panic!("This API cannot be called when the event loop is not running. Try using `std::thread::spawn` or `tauri::async_runtime::spawn`.");
     }
     let (tx, rx) = channel();
     $self
@@ -129,10 +130,11 @@ macro_rules! dispatcher_getter {
 
 macro_rules! getter {
   ($self: ident, $rx: expr, $message: expr) => {{
-    if current_thread().id() == $self.context.main_thread_id
-      && !$self.context.is_event_loop_running.load(Ordering::Relaxed)
-    {
-      panic!("This API cannot be called when the event loop is not running");
+    if current_thread().id() == $self.context.main_thread_id {
+      panic!("This API cannot be called on the main thread. Try using `std::thread::spawn` or `tauri::async_runtime::spawn`.");
+    }
+    if !$self.context.is_event_loop_running.load(Ordering::Relaxed) {
+      panic!("This API cannot be called when the event loop is not running. Try using `std::thread::spawn` or `tauri::async_runtime::spawn`.");
     }
     $self
       .context

+ 24 - 13
core/tauri-runtime/src/lib.rs

@@ -258,16 +258,20 @@ pub trait GlobalShortcutManager: Debug {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// You can spawn a task to use the API using the `tauri::async_runtime` to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
+  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
+  ///
+  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn is_registered(&self, accelerator: &str) -> crate::Result<bool>;
 
   /// Register a global shortcut of `accelerator`.
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// You can spawn a task to use the API using the `tauri::async_runtime` to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
+  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
+  ///
+  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn register<F: Fn() + Send + 'static>(
     &mut self,
     accelerator: &str,
@@ -278,16 +282,20 @@ pub trait GlobalShortcutManager: Debug {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// You can spawn a task to use the API using the `tauri::async_runtime` to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
+  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
+  ///
+  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn unregister_all(&mut self) -> crate::Result<()>;
 
   /// Unregister the provided `accelerator`.
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// You can spawn a task to use the API using the `tauri::async_runtime` to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
+  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
+  ///
+  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn unregister(&mut self, accelerator: &str) -> crate::Result<()>;
 }
 
@@ -297,16 +305,19 @@ pub trait ClipboardManager: Debug {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// You can spawn a task to use the API using the `tauri::async_runtime` to prevent the panic.
-
+  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
+  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
+  ///
+  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn write_text<T: Into<String>>(&mut self, text: T) -> Result<()>;
   /// Read the content in the clipboard as plain text.
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the `tauri::Builder#setup` closure.
-  /// You can spawn a task to use the API using the `tauri::async_runtime` to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the `tauri::Builder#setup` closure.
+  /// - Panics when called on the main thread, usually on the `tauri::App#run`closure.
+  ///
+  /// You can spawn a task to use the API using `tauri::async_runtime::spawn` or [`std::thread::spawn`] to prevent the panic.
   fn read_text(&self) -> Result<Option<String>>;
 }
 

+ 60 - 30
core/tauri/src/window.rs

@@ -313,8 +313,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn scale_factor(&self) -> crate::Result<f64> {
     self.window.dispatcher.scale_factor().map_err(Into::into)
   }
@@ -323,8 +325,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
     self.window.dispatcher.inner_position().map_err(Into::into)
   }
@@ -333,8 +337,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
     self.window.dispatcher.outer_position().map_err(Into::into)
   }
@@ -345,8 +351,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
     self.window.dispatcher.inner_size().map_err(Into::into)
   }
@@ -357,8 +365,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
     self.window.dispatcher.outer_size().map_err(Into::into)
   }
@@ -367,8 +377,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_fullscreen(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_fullscreen().map_err(Into::into)
   }
@@ -377,8 +389,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_maximized(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_maximized().map_err(Into::into)
   }
@@ -387,8 +401,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_decorated(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_decorated().map_err(Into::into)
   }
@@ -397,8 +413,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_resizable(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_resizable().map_err(Into::into)
   }
@@ -407,8 +425,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_visible(&self) -> crate::Result<bool> {
     self.window.dispatcher.is_visible().map_err(Into::into)
   }
@@ -423,8 +443,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
     self
       .window
@@ -444,8 +466,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
     self
       .window
@@ -463,8 +487,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
     self
       .window
@@ -478,8 +504,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   #[cfg(target_os = "macos")]
   pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
     self.window.dispatcher.ns_window().map_err(Into::into)
@@ -488,8 +516,10 @@ impl<R: Runtime> Window<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   #[cfg(windows)]
   pub fn hwnd(&self) -> crate::Result<*mut std::ffi::c_void> {
     self

+ 8 - 4
core/tauri/src/window/menu.rs

@@ -86,8 +86,10 @@ impl<R: Runtime> MenuHandle<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn is_visible(&self) -> crate::Result<bool> {
     self.dispatcher.is_menu_visible().map_err(Into::into)
   }
@@ -96,8 +98,10 @@ impl<R: Runtime> MenuHandle<R> {
   ///
   /// # Panics
   ///
-  /// Panics if the app is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
-  /// You can spawn a task to use the API using the [`async_runtime`](crate::async_runtime) to prevent the panic.
+  /// - Panics if the event loop is not running yet, usually when called on the [`setup`](crate::Builder#method.setup) closure.
+  /// - Panics when called on the main thread, usually on the [`run`](crate::App#method.run) closure.
+  ///
+  /// You can spawn a task to use the API using [`crate::async_runtime::spawn`] or [`std::thread::spawn`] to prevent the panic.
   pub fn toggle(&self) -> crate::Result<()> {
     if self.is_visible()? {
       self.hide()