Skip to content

Commit 66f873d

Browse files
fix(mobile): avoid mutex deadlocks (#15491)
* Avoid mutex deadlocks by acquiring locks outside of `if let` * Add .changes * Rename change-pr-15491 to mobile-run-command-deadlock.md --------- Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
1 parent 2783e60 commit 66f873d

2 files changed

Lines changed: 23 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch:bug
3+
---
4+
5+
Adjust mutex locking in `send_channel_data_handler`, `handle_android_plugin_response`, `send_channel_data` to avoid deadlocks

crates/tauri/src/plugin/mobile.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,15 @@ pub fn handle_android_plugin_response(
9595
(false, false) => unreachable!(),
9696
};
9797

98-
if let Some(handler) = PENDING_PLUGIN_CALLS
98+
// Drop the lock before invoking the handler: it delivers the command response
99+
// to the webview (which can block on the UI thread), and holding
100+
// PENDING_PLUGIN_CALLS across that call deadlocks a concurrent `run_command`.
101+
let handler = PENDING_PLUGIN_CALLS
99102
.get_or_init(Default::default)
100103
.lock()
101104
.unwrap()
102-
.remove(&id)
103-
{
105+
.remove(&id);
106+
if let Some(handler) = handler {
104107
handler(if is_ok { Ok(payload) } else { Err(payload) });
105108
}
106109
}
@@ -115,12 +118,16 @@ pub fn send_channel_data(
115118
let data: serde_json::Value =
116119
serde_json::from_str(env.get_string(&data_str).unwrap().to_str().unwrap()).unwrap();
117120

118-
if let Some(channel) = CHANNELS
121+
// Clone the channel out and drop the lock before send(): send() can block
122+
// delivering to the webview, and holding CHANNELS across it deadlocks a
123+
// concurrent channel registration/send.
124+
let channel = CHANNELS
119125
.get_or_init(Default::default)
120126
.lock()
121127
.unwrap()
122128
.get(&(channel_id as u32))
123-
{
129+
.cloned();
130+
if let Some(channel) = channel {
124131
let _ = channel.send(data);
125132
}
126133
}
@@ -401,12 +408,16 @@ pub(crate) fn run_command<R: Runtime, C: AsRef<str>, F: FnOnce(PluginResponse) +
401408
CStr::from_ptr(payload)
402409
};
403410

404-
if let Some(channel) = CHANNELS
411+
// Clone the channel out and drop the lock before send(): send() can block
412+
// delivering to the webview, and holding CHANNELS across it deadlocks a
413+
// concurrent channel registration/send.
414+
let channel = CHANNELS
405415
.get_or_init(Default::default)
406416
.lock()
407417
.unwrap()
408418
.get(&(id as u32))
409-
{
419+
.cloned();
420+
if let Some(channel) = channel {
410421
let payload: serde_json::Value = serde_json::from_str(payload.to_str().unwrap()).unwrap();
411422
let _ = channel.send(payload);
412423
}

0 commit comments

Comments
 (0)