Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changes/panic-dispatcher-getter-main-thread.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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