diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index 219336a9e479..c56f8b8c76e7 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -70,6 +70,7 @@ macos-private-api = [ # TODO: Remove in v3 - wry does not have this feature anymore objc-exception = [] tracing = ["dep:tracing", "wry/tracing"] +tracing-borrows = ["dep:tracing"] macos-proxy = ["wry/mac-proxy"] unstable = [] common-controls-v6 = [] diff --git a/crates/tauri-runtime-wry/src/windows_store.rs b/crates/tauri-runtime-wry/src/windows_store.rs index 6336393e0382..2372dde53187 100644 --- a/crates/tauri-runtime-wry/src/windows_store.rs +++ b/crates/tauri-runtime-wry/src/windows_store.rs @@ -4,9 +4,9 @@ use crate::WindowWrapper; use std::{ - cell::{BorrowError, BorrowMutError, RefCell}, collections::BTreeMap, fmt, + sync::{RwLock, TryLockError}, }; use tauri_runtime::window::WindowId; @@ -16,17 +16,34 @@ type Result = std::result::Result; #[derive(Debug)] pub enum Error { - Borrow(BorrowError), - BorrowMut(BorrowMutError), + /// The store's lock could not be acquired (already held in a conflicting mode). + Borrow(LockKind), + /// The store's lock was poisoned by a thread panicking while holding it. + Poisoned(LockKind), WindowNotFound(WindowId), } +#[derive(Debug, Clone, Copy)] +pub enum LockKind { + Read, + Write, +} + +impl fmt::Display for LockKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + LockKind::Read => "read", + LockKind::Write => "write", + }) + } +} + impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::Borrow(e) => e.fmt(f), - Error::BorrowMut(e) => e.fmt(f), + Error::Borrow(kind) => write!(f, "windows_store {kind} lock unavailable"), + Error::Poisoned(kind) => write!(f, "windows_store {kind} lock poisoned"), Error::WindowNotFound(id) => write!(f, "Window not in store: {id:?}"), } } @@ -38,8 +55,32 @@ impl From for tauri_runtime::Error { } } +fn map_try_lock(kind: LockKind, result: std::result::Result>) -> Result { + result.map_err(|e| match e { + TryLockError::Poisoned(_) => Error::Poisoned(kind), + TryLockError::WouldBlock => Error::Borrow(kind), + }) +} + +#[cfg(feature = "tracing-borrows")] +#[track_caller] +fn trace_borrow(kind: LockKind) { + let caller = std::panic::Location::caller(); + tracing::trace!( + target: "tauri::runtime::wry::windows_store", + kind = %kind, + line = caller.line(), + column = caller.column(), + thread = format!("{:?}", std::thread::current().id()), + ); +} + +#[cfg(not(feature = "tracing-borrows"))] +#[inline(always)] +fn trace_borrow(_kind: LockKind) {} + #[derive(Default)] -pub struct WindowsStore(RefCell>); +pub struct WindowsStore(RwLock); impl fmt::Debug for WindowsStore { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -48,40 +89,46 @@ impl fmt::Debug for WindowsStore { } impl WindowsStore { + #[track_caller] pub fn window(&self, id: WindowId, f: F) -> Result where F: FnOnce(&WindowWrapper) -> T, { - let store = self.0.try_borrow().map_err(Error::Borrow)?; + trace_borrow(LockKind::Read); + let store = map_try_lock(LockKind::Read, self.0.try_read())?; let window = store.get(&id).ok_or(Error::WindowNotFound(id))?; Ok(f(window)) } + #[track_caller] pub fn window_mut(&self, id: WindowId, f: F) -> Result where F: FnOnce(&mut WindowWrapper) -> T, { - let mut store = self.0.try_borrow_mut().map_err(Error::BorrowMut)?; + trace_borrow(LockKind::Write); + let mut store = map_try_lock(LockKind::Write, self.0.try_write())?; let window = store.get_mut(&id).ok_or(Error::WindowNotFound(id))?; Ok(f(window)) } + #[track_caller] pub fn store(&self, f: F) -> Result where F: FnOnce(&WindowMap) -> T, { - self.0.try_borrow().map(|s| f(&s)).map_err(Error::Borrow) + trace_borrow(LockKind::Read); + let store = map_try_lock(LockKind::Read, self.0.try_read())?; + Ok(f(&store)) } + #[track_caller] pub fn store_mut(&self, f: F) -> Result where F: FnOnce(&mut WindowMap) -> T, { - self - .0 - .try_borrow_mut() - .map(|mut s| f(&mut s)) - .map_err(Error::BorrowMut) + trace_borrow(LockKind::Write); + let mut store = map_try_lock(LockKind::Write, self.0.try_write())?; + Ok(f(&mut store)) } pub fn insert(&self, id: WindowId, window: WindowWrapper) -> Result> { @@ -96,3 +143,4 @@ impl WindowsStore { }) } } + diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 7e6a3669de25..edeb62458ef5 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -61,7 +61,7 @@ tauri-macros = { version = "2.6.1", path = "../tauri-macros" } tauri-utils = { version = "2.9.1", features = [ "resources", ], path = "../tauri-utils" } -tauri-runtime-wry = { version = "2.11.1", path = "../tauri-runtime-wry", default-features = false, optional = true } +tauri-runtime-wry = { version = "2.11.1", path = "../tauri-runtime-wry", default-features = false, optional = true, features = ["tracing-borrows"] } getrandom = "0.3" serde_repr = "0.1" http = "1"