Quellcode durchsuchen

fix(core): dialog API deadlock, closes #1695 (#1696)

Lucas Fernandes Nogueira vor 4 Jahren
Ursprung
Commit
2747bb6c11

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
core/tauri/scripts/bundle.js


+ 6 - 2
core/tauri/src/endpoints.rs

@@ -97,8 +97,12 @@ impl Module {
           .and_then(|r| r.json)
           .map_err(InvokeError::from)
       }),
-      Self::Dialog(cmd) => resolver
-        .respond_async(async move { cmd.run().and_then(|r| r.json).map_err(InvokeError::from) }),
+      Self::Dialog(cmd) => {
+        let _ = window.run_on_main_thread(|| {
+          resolver
+            .respond_closure(move || cmd.run().and_then(|r| r.json).map_err(InvokeError::from))
+        });
+      }
       Self::Cli(cmd) => {
         if let Some(cli_config) = config.tauri.cli.clone() {
           resolver.respond_async(async move {

+ 4 - 12
core/tauri/src/hooks.rs

@@ -101,16 +101,14 @@ impl<T: Serialize> From<Result<T, InvokeError>> for InvokeResponse {
 /// Resolver of a invoke message.
 pub struct InvokeResolver<M: Params> {
   window: Window<M>,
-  pub(crate) main_thread: bool,
   pub(crate) callback: String,
   pub(crate) error: String,
 }
 
 impl<P: Params> InvokeResolver<P> {
-  pub(crate) fn new(window: Window<P>, main_thread: bool, callback: String, error: String) -> Self {
+  pub(crate) fn new(window: Window<P>, callback: String, error: String) -> Self {
     Self {
       window,
-      main_thread,
       callback,
       error,
     }
@@ -122,15 +120,9 @@ impl<P: Params> InvokeResolver<P> {
     T: Serialize,
     F: Future<Output = Result<T, InvokeError>> + Send + 'static,
   {
-    if self.main_thread {
-      crate::async_runtime::block_on(async move {
-        Self::return_task(self.window, task, self.callback, self.error).await;
-      });
-    } else {
-      crate::async_runtime::spawn(async move {
-        Self::return_task(self.window, task, self.callback, self.error).await;
-      });
-    }
+    crate::async_runtime::spawn(async move {
+      Self::return_task(self.window, task, self.callback, self.error).await;
+    });
   }
 
   /// Reply to the invoke promise running the given closure.

+ 43 - 7
core/tauri/src/runtime/flavors/wry.rs

@@ -35,13 +35,14 @@ use std::{
   collections::HashMap,
   convert::TryFrom,
   sync::{
-    mpsc::{channel, Sender},
+    mpsc::{channel, Receiver, Sender},
     Arc, Mutex,
   },
 };
 
 type CreateWebviewHandler =
   Box<dyn FnOnce(&EventLoopWindowTarget<Message>) -> crate::Result<WebView> + Send>;
+type MainThreadTask = Box<dyn FnOnce() + Send>;
 
 #[repr(C)]
 #[derive(Debug)]
@@ -233,12 +234,20 @@ enum Message {
 pub struct WryDispatcher {
   window_id: WindowId,
   proxy: EventLoopProxy<Message>,
+  task_tx: Sender<MainThreadTask>,
 }
 
 impl Dispatch for WryDispatcher {
   type Runtime = Wry;
   type WindowBuilder = WryWindowBuilder;
 
+  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
+    self
+      .task_tx
+      .send(Box::new(f))
+      .map_err(|_| crate::Error::FailedToSendMessage)
+  }
+
   fn create_window<M: Params<Runtime = Self::Runtime>>(
     &mut self,
     pending: PendingWindow<M>,
@@ -246,11 +255,12 @@ impl Dispatch for WryDispatcher {
     let (tx, rx) = channel();
     let label = pending.label.clone();
     let proxy = self.proxy.clone();
+    let task_tx = self.task_tx.clone();
     self
       .proxy
       .send_event(Message::CreateWebview(
         Arc::new(Mutex::new(Some(Box::new(move |event_loop| {
-          create_webview(event_loop, proxy, pending)
+          create_webview(event_loop, proxy, task_tx, pending)
         })))),
         tx,
       ))
@@ -259,6 +269,7 @@ impl Dispatch for WryDispatcher {
     let dispatcher = WryDispatcher {
       window_id,
       proxy: self.proxy.clone(),
+      task_tx: self.task_tx.clone(),
     };
     Ok(DetachedWindow { label, dispatcher })
   }
@@ -474,6 +485,8 @@ impl Dispatch for WryDispatcher {
 pub struct Wry {
   event_loop: EventLoop<Message>,
   webviews: HashMap<WindowId, WebView>,
+  task_tx: Sender<MainThreadTask>,
+  task_rx: Receiver<MainThreadTask>,
 }
 
 impl Runtime for Wry {
@@ -481,9 +494,12 @@ impl Runtime for Wry {
 
   fn new() -> crate::Result<Self> {
     let event_loop = EventLoop::<Message>::with_user_event();
+    let (task_tx, task_rx) = channel();
     Ok(Self {
       event_loop,
       webviews: Default::default(),
+      task_tx,
+      task_rx,
     })
   }
 
@@ -493,11 +509,17 @@ impl Runtime for Wry {
   ) -> crate::Result<DetachedWindow<M>> {
     let label = pending.label.clone();
     let proxy = self.event_loop.create_proxy();
-    let webview = create_webview(&self.event_loop, proxy.clone(), pending)?;
+    let webview = create_webview(
+      &self.event_loop,
+      proxy.clone(),
+      self.task_tx.clone(),
+      pending,
+    )?;
 
     let dispatcher = WryDispatcher {
       window_id: webview.window().id(),
       proxy,
+      task_tx: self.task_tx.clone(),
     };
 
     self.webviews.insert(webview.window().id(), webview);
@@ -507,6 +529,7 @@ impl Runtime for Wry {
 
   fn run(self) {
     let mut webviews = self.webviews;
+    let task_rx = self.task_rx;
     self.event_loop.run(move |event, event_loop, control_flow| {
       *control_flow = ControlFlow::Wait;
 
@@ -516,6 +539,10 @@ impl Runtime for Wry {
         }
       }
 
+      while let Ok(task) = task_rx.try_recv() {
+        task();
+      }
+
       match event {
         Event::WindowEvent { event, window_id } => match event {
           WindowEvent::CloseRequested => {
@@ -647,6 +674,7 @@ impl Runtime for Wry {
 fn create_webview<M: Params<Runtime = Wry>>(
   event_loop: &EventLoopWindowTarget<Message>,
   proxy: EventLoopProxy<Message>,
+  task_tx: Sender<MainThreadTask>,
   pending: PendingWindow<M>,
 ) -> crate::Result<WebView> {
   let PendingWindow {
@@ -665,12 +693,16 @@ fn create_webview<M: Params<Runtime = Wry>>(
     .with_url(&url)
     .unwrap(); // safe to unwrap because we validate the URL beforehand
   if let Some(handler) = rpc_handler {
-    webview_builder =
-      webview_builder.with_rpc_handler(create_rpc_handler(proxy.clone(), label.clone(), handler));
+    webview_builder = webview_builder.with_rpc_handler(create_rpc_handler(
+      proxy.clone(),
+      task_tx.clone(),
+      label.clone(),
+      handler,
+    ));
   }
   if let Some(handler) = file_drop_handler {
-    webview_builder =
-      webview_builder.with_file_drop_handler(create_file_drop_handler(proxy, label, handler));
+    webview_builder = webview_builder
+      .with_file_drop_handler(create_file_drop_handler(proxy, task_tx, label, handler));
   }
   for (scheme, protocol) in webview_attributes.uri_scheme_protocols {
     webview_builder = webview_builder.with_custom_protocol(scheme, move |_window, url| {
@@ -692,6 +724,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
 /// Create a wry rpc handler from a tauri rpc handler.
 fn create_rpc_handler<M: Params<Runtime = Wry>>(
   proxy: EventLoopProxy<Message>,
+  task_tx: Sender<MainThreadTask>,
   label: M::Label,
   handler: WebviewRpcHandler<M>,
 ) -> Box<dyn Fn(&Window, WryRpcRequest) -> Option<RpcResponse> + 'static> {
@@ -701,6 +734,7 @@ fn create_rpc_handler<M: Params<Runtime = Wry>>(
         dispatcher: WryDispatcher {
           window_id: window.id(),
           proxy: proxy.clone(),
+          task_tx: task_tx.clone(),
         },
         label: label.clone(),
       },
@@ -713,6 +747,7 @@ fn create_rpc_handler<M: Params<Runtime = Wry>>(
 /// Create a wry file drop handler from a tauri file drop handler.
 fn create_file_drop_handler<M: Params<Runtime = Wry>>(
   proxy: EventLoopProxy<Message>,
+  task_tx: Sender<MainThreadTask>,
   label: M::Label,
   handler: FileDropHandler<M>,
 ) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
@@ -723,6 +758,7 @@ fn create_file_drop_handler<M: Params<Runtime = Wry>>(
         dispatcher: WryDispatcher {
           window_id: window.id(),
           proxy: proxy.clone(),
+          task_tx: task_tx.clone(),
         },
         label: label.clone(),
       },

+ 3 - 0
core/tauri/src/runtime/mod.rs

@@ -42,6 +42,9 @@ pub trait Dispatch: Clone + Send + Sized + 'static {
   /// The winoow builder type.
   type WindowBuilder: WindowBuilder + Clone;
 
+  /// Run a task on the main thread.
+  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()>;
+
   /// Create a new webview window.
   fn create_window<P: Params<Runtime = Self::Runtime>>(
     &mut self,

+ 0 - 2
core/tauri/src/runtime/webview.rs

@@ -174,8 +174,6 @@ pub(crate) struct InvokePayload {
   pub(crate) tauri_module: Option<String>,
   pub(crate) callback: String,
   pub(crate) error: String,
-  #[serde(rename = "mainThread", default)]
-  pub(crate) main_thread: bool,
   #[serde(flatten)]
   pub(crate) inner: JsonValue,
 }

+ 8 - 2
core/tauri/src/runtime/window.rs

@@ -186,6 +186,13 @@ pub(crate) mod export {
       self.window.dispatcher.clone()
     }
 
+    pub(crate) fn run_on_main_thread<F: FnOnce() + Send + 'static>(
+      &self,
+      f: F,
+    ) -> crate::Result<()> {
+      self.window.dispatcher.run_on_main_thread(f)
+    }
+
     /// How to handle this window receiving an [`InvokeMessage`].
     pub(crate) fn on_message(self, command: String, payload: InvokePayload) -> crate::Result<()> {
       let manager = self.manager.clone();
@@ -201,8 +208,7 @@ pub(crate) mod export {
             command.to_string(),
             payload.inner,
           );
-          let resolver =
-            InvokeResolver::new(self, payload.main_thread, payload.callback, payload.error);
+          let resolver = InvokeResolver::new(self, payload.callback, payload.error);
           let invoke = Invoke { message, resolver };
           if let Some(module) = &payload.tauri_module {
             let module = module.to_string();

+ 0 - 3
tooling/api/src/app.ts

@@ -12,7 +12,6 @@ import { invokeTauriCommand } from './helpers/tauri'
 async function getVersion(): Promise<string> {
   return invokeTauriCommand<string>({
     __tauriModule: 'App',
-    mainThread: true,
     message: {
       cmd: 'getAppVersion'
     }
@@ -27,7 +26,6 @@ async function getVersion(): Promise<string> {
 async function getName(): Promise<string> {
   return invokeTauriCommand<string>({
     __tauriModule: 'App',
-    mainThread: true,
     message: {
       cmd: 'getAppName'
     }
@@ -42,7 +40,6 @@ async function getName(): Promise<string> {
 async function getTauriVersion(): Promise<string> {
   return invokeTauriCommand<string>({
     __tauriModule: 'App',
-    mainThread: true,
     message: {
       cmd: 'getTauriVersion'
     }

+ 0 - 2
tooling/api/src/dialog.ts

@@ -36,7 +36,6 @@ async function open(
 
   return invokeTauriCommand<string | string[]>({
     __tauriModule: 'Dialog',
-    mainThread: true,
     message: {
       cmd: 'openDialog',
       options
@@ -56,7 +55,6 @@ async function save(options: SaveDialogOptions = {}): Promise<string> {
 
   return invokeTauriCommand<string>({
     __tauriModule: 'Dialog',
-    mainThread: true,
     message: {
       cmd: 'saveDialog',
       options

+ 0 - 2
tooling/api/src/process.ts

@@ -13,7 +13,6 @@ import { invokeTauriCommand } from './helpers/tauri'
 async function exit(exitCode: number = 0): Promise<void> {
   return invokeTauriCommand({
     __tauriModule: 'Process',
-    mainThread: true,
     message: {
       cmd: 'exit',
       exitCode
@@ -29,7 +28,6 @@ async function exit(exitCode: number = 0): Promise<void> {
 async function relaunch(): Promise<void> {
   return invokeTauriCommand({
     __tauriModule: 'Process',
-    mainThread: true,
     message: {
       cmd: 'relaunch'
     }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.