refactor(tauri-runtime-wry): Arc instead of Rc, closes #9775 (#10587)

This commit is contained in:
Lucas Fernandes Nogueira 2024-08-13 08:37:46 -03:00 committed by GitHub
parent 48a7415c21
commit 937849f28c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 127 additions and 63 deletions

5
.changes/wry-arc.md Normal file
View File

@ -0,0 +1,5 @@
---
"tauri-runtime-wry": patch:bug
---
Use `Arc` instead of `Rc` on global shortcut and tray types to prevent crashes on macOS.

View File

@ -8,7 +8,6 @@ use std::{
collections::HashMap, collections::HashMap,
error::Error as StdError, error::Error as StdError,
fmt, fmt,
rc::Rc,
sync::{ sync::{
mpsc::{channel, Sender}, mpsc::{channel, Sender},
Arc, Mutex, Arc, Mutex,
@ -18,14 +17,34 @@ use std::{
use crate::{getter, Context, Message}; use crate::{getter, Context, Message};
use tauri_runtime::{Error, GlobalShortcutManager, Result, UserEvent}; use tauri_runtime::{Error, GlobalShortcutManager, Result, UserEvent};
#[cfg(desktop)]
pub use wry::application::{ pub use wry::application::{
accelerator::{Accelerator, AcceleratorId, AcceleratorParseError}, accelerator::{Accelerator, AcceleratorId, AcceleratorParseError},
global_shortcut::{GlobalShortcut, ShortcutManager as WryShortcutManager}, event_loop::EventLoopWindowTarget,
global_shortcut::GlobalShortcut,
}; };
pub type GlobalShortcutListeners = Arc<Mutex<HashMap<AcceleratorId, Box<dyn Fn() + Send>>>>; pub type GlobalShortcutListeners = Arc<Mutex<HashMap<AcceleratorId, Box<dyn Fn() + Send>>>>;
#[derive(Debug)]
pub struct WryShortcutManager(pub wry::application::global_shortcut::ShortcutManager);
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for WryShortcutManager {}
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Sync for WryShortcutManager {}
impl WryShortcutManager {
pub fn new<T: 'static>(event_loop: &EventLoopWindowTarget<T>) -> Self {
Self(wry::application::global_shortcut::ShortcutManager::new(
event_loop,
))
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum GlobalShortcutMessage { pub enum GlobalShortcutMessage {
IsRegistered(Accelerator, Sender<bool>), IsRegistered(Accelerator, Sender<bool>),
@ -139,7 +158,7 @@ impl<T: UserEvent> GlobalShortcutManager for GlobalShortcutManagerHandle<T> {
pub fn handle_global_shortcut_message( pub fn handle_global_shortcut_message(
message: GlobalShortcutMessage, message: GlobalShortcutMessage,
global_shortcut_manager: &Rc<Mutex<WryShortcutManager>>, global_shortcut_manager: &Mutex<WryShortcutManager>,
) { ) {
match message { match message {
GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx
@ -147,6 +166,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager global_shortcut_manager
.lock() .lock()
.unwrap() .unwrap()
.0
.is_registered(&accelerator), .is_registered(&accelerator),
) )
.unwrap(), .unwrap(),
@ -155,6 +175,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager global_shortcut_manager
.lock() .lock()
.unwrap() .unwrap()
.0
.register(accelerator) .register(accelerator)
.map(GlobalShortcutWrapper) .map(GlobalShortcutWrapper)
.map_err(|e| Error::GlobalShortcut(Box::new(e))), .map_err(|e| Error::GlobalShortcut(Box::new(e))),
@ -165,6 +186,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager global_shortcut_manager
.lock() .lock()
.unwrap() .unwrap()
.0
.unregister(shortcut.0) .unregister(shortcut.0)
.map_err(|e| Error::GlobalShortcut(Box::new(e))), .map_err(|e| Error::GlobalShortcut(Box::new(e))),
) )
@ -174,6 +196,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager global_shortcut_manager
.lock() .lock()
.unwrap() .unwrap()
.0
.unregister_all() .unregister_all()
.map_err(|e| Error::GlobalShortcut(Box::new(e))), .map_err(|e| Error::GlobalShortcut(Box::new(e))),
) )

View File

@ -211,35 +211,37 @@ impl<T: UserEvent> Context<T> {
} }
} }
impl<T: UserEvent> Context<T> { fn context_create_webview<T: UserEvent>(
fn create_webview(&self, pending: PendingWindow<T, Wry<T>>) -> Result<DetachedWindow<T, Wry<T>>> { context: Context<T>,
let label = pending.label.clone(); pending: PendingWindow<T, Wry<T>>,
let menu_ids = pending.menu_ids.clone(); ) -> Result<DetachedWindow<T, Wry<T>>> {
let js_event_listeners = pending.js_event_listeners.clone(); let label = pending.label.clone();
let context = self.clone(); let menu_ids = pending.menu_ids.clone();
let window_id = rand::random(); let js_event_listeners = pending.js_event_listeners.clone();
send_user_message( let window_id = rand::random();
self,
Message::CreateWebview(
window_id,
Box::new(move |event_loop, web_context| {
create_webview(window_id, event_loop, web_context, context, pending)
}),
),
)?;
let dispatcher = WryDispatcher { let context_ = context.clone();
send_user_message(
&context,
Message::CreateWebview(
window_id, window_id,
context: self.clone(), Box::new(move |event_loop, web_context| {
}; create_webview(window_id, event_loop, web_context, context_, pending)
Ok(DetachedWindow { }),
label, ),
dispatcher, )?;
menu_ids,
js_event_listeners, let dispatcher = WryDispatcher {
}) window_id,
} context: context.clone(),
};
Ok(DetachedWindow {
label,
dispatcher,
menu_ids,
js_event_listeners,
})
} }
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@ -281,7 +283,7 @@ pub struct DispatcherMainThreadContext<T: UserEvent> {
pub window_target: EventLoopWindowTarget<Message<T>>, pub window_target: EventLoopWindowTarget<Message<T>>,
pub web_context: WebContextStore, pub web_context: WebContextStore,
#[cfg(all(desktop, feature = "global-shortcut"))] #[cfg(all(desktop, feature = "global-shortcut"))]
pub global_shortcut_manager: Rc<Mutex<WryShortcutManager>>, pub global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
// changing this to an Rc will cause frequent app crashes. // changing this to an Rc will cause frequent app crashes.
pub windows: Arc<WindowsStore>, pub windows: Arc<WindowsStore>,
#[cfg(all(desktop, feature = "system-tray"))] #[cfg(all(desktop, feature = "system-tray"))]
@ -1452,7 +1454,7 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
&mut self, &mut self,
pending: PendingWindow<T, Self::Runtime>, pending: PendingWindow<T, Self::Runtime>,
) -> Result<DetachedWindow<T, Self::Runtime>> { ) -> Result<DetachedWindow<T, Self::Runtime>> {
self.context.create_webview(pending) context_create_webview(self.context.clone(), pending)
} }
fn set_resizable(&self, resizable: bool) -> Result<()> { fn set_resizable(&self, resizable: bool) -> Result<()> {
@ -1927,7 +1929,7 @@ impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
&self, &self,
pending: PendingWindow<T, Self::Runtime>, pending: PendingWindow<T, Self::Runtime>,
) -> Result<DetachedWindow<T, Self::Runtime>> { ) -> Result<DetachedWindow<T, Self::Runtime>> {
self.context.create_webview(pending) context_create_webview(self.context.clone(), pending)
} }
fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> { fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
@ -1980,7 +1982,7 @@ impl<T: UserEvent> Wry<T> {
let web_context = WebContextStore::default(); let web_context = WebContextStore::default();
#[cfg(all(desktop, feature = "global-shortcut"))] #[cfg(all(desktop, feature = "global-shortcut"))]
let global_shortcut_manager = Rc::new(Mutex::new(WryShortcutManager::new(&event_loop))); let global_shortcut_manager = Arc::new(Mutex::new(WryShortcutManager::new(&event_loop)));
let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default()))); let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default())));
@ -2131,7 +2133,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let id = system_tray.id; let id = system_tray.id;
let mut listeners = Vec::new(); let mut listeners = Vec::new();
if let Some(l) = system_tray.on_event.take() { if let Some(l) = system_tray.on_event.take() {
listeners.push(Rc::new(l)); #[allow(clippy::arc_with_non_send_sync)]
listeners.push(Arc::new(l));
} }
let (tray, items) = create_tray(WryTrayId(id), system_tray, &self.event_loop)?; let (tray, items) = create_tray(WryTrayId(id), system_tray, &self.event_loop)?;
self self
@ -2144,9 +2147,9 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
.insert( .insert(
id, id,
TrayContext { TrayContext {
tray: Rc::new(RefCell::new(Some(tray))), tray: Arc::new(TrayCell(RefCell::new(Some(tray)))),
listeners: Rc::new(RefCell::new(listeners)), listeners: Arc::new(TrayListenersCell(RefCell::new(listeners))),
items: Rc::new(RefCell::new(items)), items: Arc::new(TrayItemsCell(RefCell::new(items))),
}, },
); );
@ -2280,14 +2283,14 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) { fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
let windows = self.context.main_thread.windows.clone(); let windows = self.context.main_thread.windows.clone();
let webview_id_map = self.context.webview_id_map.clone(); let webview_id_map = self.context.webview_id_map.clone();
let web_context = self.context.main_thread.web_context; let web_context = self.context.main_thread.web_context.clone();
let mut plugins = self.plugins; let mut plugins = self.plugins;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone(); let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();
#[cfg(all(desktop, feature = "system-tray"))] #[cfg(all(desktop, feature = "system-tray"))]
let system_tray_manager = self.context.main_thread.system_tray_manager; let system_tray_manager = self.context.main_thread.system_tray_manager.clone();
#[cfg(all(desktop, feature = "global-shortcut"))] #[cfg(all(desktop, feature = "global-shortcut"))]
let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone(); let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone();
@ -2350,7 +2353,7 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> {
pub webview_id_map: WebviewIdStore, pub webview_id_map: WebviewIdStore,
pub windows: Arc<WindowsStore>, pub windows: Arc<WindowsStore>,
#[cfg(all(desktop, feature = "global-shortcut"))] #[cfg(all(desktop, feature = "global-shortcut"))]
pub global_shortcut_manager: Rc<Mutex<WryShortcutManager>>, pub global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
#[cfg(all(desktop, feature = "global-shortcut"))] #[cfg(all(desktop, feature = "global-shortcut"))]
pub global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>, pub global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
#[cfg(all(desktop, feature = "system-tray"))] #[cfg(all(desktop, feature = "system-tray"))]
@ -2363,7 +2366,7 @@ struct UserMessageContext {
windows: Arc<WindowsStore>, windows: Arc<WindowsStore>,
webview_id_map: WebviewIdStore, webview_id_map: WebviewIdStore,
#[cfg(all(desktop, feature = "global-shortcut"))] #[cfg(all(desktop, feature = "global-shortcut"))]
global_shortcut_manager: Rc<Mutex<WryShortcutManager>>, global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
#[cfg(all(desktop, feature = "system-tray"))] #[cfg(all(desktop, feature = "system-tray"))]
system_tray_manager: SystemTrayManager, system_tray_manager: SystemTrayManager,
} }
@ -2703,16 +2706,17 @@ fn handle_user_message<T: UserEvent>(
if let TrayMessage::Create(mut tray, tx) = tray_message { if let TrayMessage::Create(mut tray, tx) = tray_message {
let mut listeners = Vec::new(); let mut listeners = Vec::new();
if let Some(l) = tray.on_event.take() { if let Some(l) = tray.on_event.take() {
listeners.push(Rc::new(l)); #[allow(clippy::arc_with_non_send_sync)]
listeners.push(Arc::new(l));
} }
match create_tray(WryTrayId(tray_id), tray, event_loop) { match create_tray(WryTrayId(tray_id), tray, event_loop) {
Ok((tray, items)) => { Ok((tray, items)) => {
trays.insert( trays.insert(
tray_id, tray_id,
TrayContext { TrayContext {
tray: Rc::new(RefCell::new(Some(tray))), tray: Arc::new(TrayCell(RefCell::new(Some(tray)))),
listeners: Rc::new(RefCell::new(listeners)), listeners: Arc::new(TrayListenersCell(RefCell::new(listeners))),
items: Rc::new(RefCell::new(items)), items: Arc::new(TrayItemsCell(RefCell::new(items))),
}, },
); );
@ -2726,7 +2730,7 @@ fn handle_user_message<T: UserEvent>(
} else if let Some(tray_context) = trays.get(&tray_id) { } else if let Some(tray_context) = trays.get(&tray_id) {
match tray_message { match tray_message {
TrayMessage::UpdateItem(menu_id, update) => { TrayMessage::UpdateItem(menu_id, update) => {
let mut tray = tray_context.items.as_ref().borrow_mut(); let mut tray = tray_context.items.as_ref().0.borrow_mut();
let item = tray.get_mut(&menu_id).expect("menu item not found"); let item = tray.get_mut(&menu_id).expect("menu item not found");
match update { match update {
MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled), MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
@ -2739,14 +2743,14 @@ fn handle_user_message<T: UserEvent>(
} }
} }
TrayMessage::UpdateMenu(menu) => { TrayMessage::UpdateMenu(menu) => {
if let Some(tray) = &mut *tray_context.tray.borrow_mut() { if let Some(tray) = &mut *tray_context.tray.0.borrow_mut() {
let mut items = HashMap::new(); let mut items = HashMap::new();
tray.set_menu(&to_wry_context_menu(&mut items, menu)); tray.set_menu(&to_wry_context_menu(&mut items, menu));
*tray_context.items.borrow_mut() = items; *tray_context.items.0.borrow_mut() = items;
} }
} }
TrayMessage::UpdateIcon(icon) => { TrayMessage::UpdateIcon(icon) => {
if let Some(tray) = &mut *tray_context.tray.borrow_mut() { if let Some(tray) = &mut *tray_context.tray.0.borrow_mut() {
if let Ok(icon) = TrayIcon::try_from(icon) { if let Ok(icon) = TrayIcon::try_from(icon) {
tray.set_icon(icon.0); tray.set_icon(icon.0);
} }
@ -2754,18 +2758,18 @@ fn handle_user_message<T: UserEvent>(
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
TrayMessage::UpdateIconAsTemplate(is_template) => { TrayMessage::UpdateIconAsTemplate(is_template) => {
if let Some(tray) = &mut *tray_context.tray.borrow_mut() { if let Some(tray) = &mut *tray_context.tray.0.borrow_mut() {
tray.set_icon_as_template(is_template); tray.set_icon_as_template(is_template);
} }
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
TrayMessage::UpdateTitle(title) => { TrayMessage::UpdateTitle(title) => {
if let Some(tray) = &mut *tray_context.tray.borrow_mut() { if let Some(tray) = &mut *tray_context.tray.0.borrow_mut() {
tray.set_title(&title); tray.set_title(&title);
} }
} }
TrayMessage::UpdateTooltip(tooltip) => { TrayMessage::UpdateTooltip(tooltip) => {
if let Some(tray) = &mut *tray_context.tray.borrow_mut() { if let Some(tray) = &mut *tray_context.tray.0.borrow_mut() {
tray.set_tooltip(&tooltip); tray.set_tooltip(&tooltip);
} }
} }
@ -2773,9 +2777,9 @@ fn handle_user_message<T: UserEvent>(
// already handled // already handled
} }
TrayMessage::Destroy(tx) => { TrayMessage::Destroy(tx) => {
*tray_context.tray.borrow_mut() = None; *tray_context.tray.0.borrow_mut() = None;
tray_context.listeners.borrow_mut().clear(); tray_context.listeners.0.borrow_mut().clear();
tray_context.items.borrow_mut().clear(); tray_context.items.0.borrow_mut().clear();
tx.send(Ok(())).unwrap(); tx.send(Ok(())).unwrap();
} }
} }
@ -2905,11 +2909,11 @@ fn handle_event_loop<T: UserEvent>(
let (mut listeners, mut tray_id) = (None, 0); let (mut listeners, mut tray_id) = (None, 0);
for (id, tray_context) in trays_iter { for (id, tray_context) in trays_iter {
let has_menu = { let has_menu = {
let items = tray_context.items.borrow(); let items = tray_context.items.0.borrow();
items.contains_key(&menu_id.0) items.contains_key(&menu_id.0)
}; };
if has_menu { if has_menu {
listeners.replace(tray_context.listeners.borrow().clone()); listeners.replace(tray_context.listeners.0.borrow().clone());
tray_id = *id; tray_id = *id;
break; break;
} }
@ -2948,7 +2952,7 @@ fn handle_event_loop<T: UserEvent>(
}; };
let trays = system_tray_manager.trays.lock().unwrap(); let trays = system_tray_manager.trays.lock().unwrap();
if let Some(tray_context) = trays.get(&id.0) { if let Some(tray_context) = trays.get(&id.0) {
let listeners = tray_context.listeners.borrow(); let listeners = tray_context.listeners.0.borrow();
let iter = listeners.iter(); let iter = listeners.iter();
for handler in iter { for handler in iter {
handler(&event); handler(&event);

View File

@ -32,7 +32,6 @@ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::HashMap,
fmt, fmt,
rc::Rc,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -40,12 +39,45 @@ pub type GlobalSystemTrayEventHandler = Box<dyn Fn(TrayId, &SystemTrayEvent) + S
pub type GlobalSystemTrayEventListeners = Arc<Mutex<Vec<Arc<GlobalSystemTrayEventHandler>>>>; pub type GlobalSystemTrayEventListeners = Arc<Mutex<Vec<Arc<GlobalSystemTrayEventHandler>>>>;
pub type SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>; pub type SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>;
pub type SystemTrayEventListeners = Rc<RefCell<Vec<Rc<SystemTrayEventHandler>>>>; pub type SystemTrayEventListeners = Arc<TrayListenersCell>;
pub type SystemTrayItems = Rc<RefCell<HashMap<u16, WryCustomMenuItem>>>; pub type SystemTrayItems = Arc<TrayItemsCell>;
#[derive(Debug, Default)]
pub struct TrayItemsCell(pub RefCell<HashMap<u16, WryCustomMenuItem>>);
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for TrayItemsCell {}
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Sync for TrayItemsCell {}
#[derive(Default)]
pub struct TrayCell(pub RefCell<Option<WrySystemTray>>);
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for TrayCell {}
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Sync for TrayCell {}
#[derive(Default)]
pub struct TrayListenersCell(pub RefCell<Vec<Arc<SystemTrayEventHandler>>>);
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for TrayListenersCell {}
// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Sync for TrayListenersCell {}
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct TrayContext { pub struct TrayContext {
pub tray: Rc<RefCell<Option<WrySystemTray>>>, pub tray: Arc<TrayCell>,
pub listeners: SystemTrayEventListeners, pub listeners: SystemTrayEventListeners,
pub items: SystemTrayItems, pub items: SystemTrayItems,
} }