Skip to content

Commit

Permalink
feat(core): panic when a dispatcher getter is used on the main thread (
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Aug 16, 2021
1 parent c76f4b7 commit 50ffdc0
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 55 deletions.
7 changes: 7 additions & 0 deletions .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.
18 changes: 10 additions & 8 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -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
Expand All @@ -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
Expand Down
37 changes: 24 additions & 13 deletions core/tauri-runtime/src/lib.rs
Expand Up @@ -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,
Expand All @@ -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<()>;
}

Expand All @@ -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>>;
}

Expand Down
90 changes: 60 additions & 30 deletions core/tauri/src/window.rs
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down
12 changes: 8 additions & 4 deletions core/tauri/src/window/menu.rs
Expand Up @@ -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)
}
Expand All @@ -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()
Expand Down

0 comments on commit 50ffdc0

Please sign in to comment.