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,
error::Error as StdError,
fmt,
rc::Rc,
sync::{
mpsc::{channel, Sender},
Arc, Mutex,
@ -18,14 +17,34 @@ use std::{
use crate::{getter, Context, Message};
use tauri_runtime::{Error, GlobalShortcutManager, Result, UserEvent};
#[cfg(desktop)]
pub use wry::application::{
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>>>>;
#[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)]
pub enum GlobalShortcutMessage {
IsRegistered(Accelerator, Sender<bool>),
@ -139,7 +158,7 @@ impl<T: UserEvent> GlobalShortcutManager for GlobalShortcutManagerHandle<T> {
pub fn handle_global_shortcut_message(
message: GlobalShortcutMessage,
global_shortcut_manager: &Rc<Mutex<WryShortcutManager>>,
global_shortcut_manager: &Mutex<WryShortcutManager>,
) {
match message {
GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx
@ -147,6 +166,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager
.lock()
.unwrap()
.0
.is_registered(&accelerator),
)
.unwrap(),
@ -155,6 +175,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager
.lock()
.unwrap()
.0
.register(accelerator)
.map(GlobalShortcutWrapper)
.map_err(|e| Error::GlobalShortcut(Box::new(e))),
@ -165,6 +186,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager
.lock()
.unwrap()
.0
.unregister(shortcut.0)
.map_err(|e| Error::GlobalShortcut(Box::new(e))),
)
@ -174,6 +196,7 @@ pub fn handle_global_shortcut_message(
global_shortcut_manager
.lock()
.unwrap()
.0
.unregister_all()
.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 create_webview(&self, pending: PendingWindow<T, Wry<T>>) -> Result<DetachedWindow<T, Wry<T>>> {
let label = pending.label.clone();
let menu_ids = pending.menu_ids.clone();
let js_event_listeners = pending.js_event_listeners.clone();
let context = self.clone();
let window_id = rand::random();
fn context_create_webview<T: UserEvent>(
context: Context<T>,
pending: PendingWindow<T, Wry<T>>,
) -> Result<DetachedWindow<T, Wry<T>>> {
let label = pending.label.clone();
let menu_ids = pending.menu_ids.clone();
let js_event_listeners = pending.js_event_listeners.clone();
send_user_message(
self,
Message::CreateWebview(
window_id,
Box::new(move |event_loop, web_context| {
create_webview(window_id, event_loop, web_context, context, pending)
}),
),
)?;
let window_id = rand::random();
let dispatcher = WryDispatcher {
let context_ = context.clone();
send_user_message(
&context,
Message::CreateWebview(
window_id,
context: self.clone(),
};
Ok(DetachedWindow {
label,
dispatcher,
menu_ids,
js_event_listeners,
})
}
Box::new(move |event_loop, web_context| {
create_webview(window_id, event_loop, web_context, context_, pending)
}),
),
)?;
let dispatcher = WryDispatcher {
window_id,
context: context.clone(),
};
Ok(DetachedWindow {
label,
dispatcher,
menu_ids,
js_event_listeners,
})
}
#[cfg(feature = "tracing")]
@ -281,7 +283,7 @@ pub struct DispatcherMainThreadContext<T: UserEvent> {
pub window_target: EventLoopWindowTarget<Message<T>>,
pub web_context: WebContextStore,
#[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.
pub windows: Arc<WindowsStore>,
#[cfg(all(desktop, feature = "system-tray"))]
@ -1452,7 +1454,7 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
&mut self,
pending: PendingWindow<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<()> {
@ -1927,7 +1929,7 @@ impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
&self,
pending: PendingWindow<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<()> {
@ -1980,7 +1982,7 @@ impl<T: UserEvent> Wry<T> {
let web_context = WebContextStore::default();
#[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())));
@ -2131,7 +2133,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let id = system_tray.id;
let mut listeners = Vec::new();
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)?;
self
@ -2144,9 +2147,9 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
.insert(
id,
TrayContext {
tray: Rc::new(RefCell::new(Some(tray))),
listeners: Rc::new(RefCell::new(listeners)),
items: Rc::new(RefCell::new(items)),
tray: Arc::new(TrayCell(RefCell::new(Some(tray)))),
listeners: Arc::new(TrayListenersCell(RefCell::new(listeners))),
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) {
let windows = self.context.main_thread.windows.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;
#[cfg(feature = "tracing")]
let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();
#[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"))]
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 windows: Arc<WindowsStore>,
#[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"))]
pub global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
#[cfg(all(desktop, feature = "system-tray"))]
@ -2363,7 +2366,7 @@ struct UserMessageContext {
windows: Arc<WindowsStore>,
webview_id_map: WebviewIdStore,
#[cfg(all(desktop, feature = "global-shortcut"))]
global_shortcut_manager: Rc<Mutex<WryShortcutManager>>,
global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
#[cfg(all(desktop, feature = "system-tray"))]
system_tray_manager: SystemTrayManager,
}
@ -2703,16 +2706,17 @@ fn handle_user_message<T: UserEvent>(
if let TrayMessage::Create(mut tray, tx) = tray_message {
let mut listeners = Vec::new();
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) {
Ok((tray, items)) => {
trays.insert(
tray_id,
TrayContext {
tray: Rc::new(RefCell::new(Some(tray))),
listeners: Rc::new(RefCell::new(listeners)),
items: Rc::new(RefCell::new(items)),
tray: Arc::new(TrayCell(RefCell::new(Some(tray)))),
listeners: Arc::new(TrayListenersCell(RefCell::new(listeners))),
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) {
match tray_message {
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");
match update {
MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
@ -2739,14 +2743,14 @@ fn handle_user_message<T: UserEvent>(
}
}
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();
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) => {
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) {
tray.set_icon(icon.0);
}
@ -2754,18 +2758,18 @@ fn handle_user_message<T: UserEvent>(
}
#[cfg(target_os = "macos")]
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);
}
}
#[cfg(target_os = "macos")]
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);
}
}
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);
}
}
@ -2773,9 +2777,9 @@ fn handle_user_message<T: UserEvent>(
// already handled
}
TrayMessage::Destroy(tx) => {
*tray_context.tray.borrow_mut() = None;
tray_context.listeners.borrow_mut().clear();
tray_context.items.borrow_mut().clear();
*tray_context.tray.0.borrow_mut() = None;
tray_context.listeners.0.borrow_mut().clear();
tray_context.items.0.borrow_mut().clear();
tx.send(Ok(())).unwrap();
}
}
@ -2905,11 +2909,11 @@ fn handle_event_loop<T: UserEvent>(
let (mut listeners, mut tray_id) = (None, 0);
for (id, tray_context) in trays_iter {
let has_menu = {
let items = tray_context.items.borrow();
let items = tray_context.items.0.borrow();
items.contains_key(&menu_id.0)
};
if has_menu {
listeners.replace(tray_context.listeners.borrow().clone());
listeners.replace(tray_context.listeners.0.borrow().clone());
tray_id = *id;
break;
}
@ -2948,7 +2952,7 @@ fn handle_event_loop<T: UserEvent>(
};
let trays = system_tray_manager.trays.lock().unwrap();
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();
for handler in iter {
handler(&event);

View File

@ -32,7 +32,6 @@ use std::{
cell::RefCell,
collections::HashMap,
fmt,
rc::Rc,
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 SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>;
pub type SystemTrayEventListeners = Rc<RefCell<Vec<Rc<SystemTrayEventHandler>>>>;
pub type SystemTrayItems = Rc<RefCell<HashMap<u16, WryCustomMenuItem>>>;
pub type SystemTrayEventListeners = Arc<TrayListenersCell>;
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)]
pub struct TrayContext {
pub tray: Rc<RefCell<Option<WrySystemTray>>>,
pub tray: Arc<TrayCell>,
pub listeners: SystemTrayEventListeners,
pub items: SystemTrayItems,
}