Przeglądaj źródła

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

Lucas Fernandes Nogueira 1 rok temu
rodzic
commit
937849f28c

+ 5 - 0
.changes/wry-arc.md

@@ -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.

+ 27 - 4
core/tauri-runtime-wry/src/global_shortcut.rs

@@ -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))),
       )

+ 59 - 55
core/tauri-runtime-wry/src/lib.rs

@@ -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);

+ 36 - 4
core/tauri-runtime-wry/src/system_tray.rs

@@ -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,
 }