wry: Deadlock creating a new window on the IPC handler on Windows
Describe the bug
On Tauri, we allow users to create a window from a IPC handler. This results in a deadlock on Windows on the create_controller function (the second closure is never called). This might be an issue on the windows crate instead.
Steps To Reproduce I’ve changed the multi_window example to reproduce this issue (note that I’m not using the event loop proxy to send a message because Tauri needs a response from the event loop (the window id).
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
fn main() -> wry::Result<()> {
use std::collections::HashMap;
use std::ops::Deref;
use wry::{
application::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
window::{Window, WindowBuilder, WindowId},
},
webview::{WebView, WebViewBuilder},
};
#[derive(Clone)]
enum UserEvents {
CloseWindow(WindowId),
NewWindow(),
}
fn create_new_window(
title: String,
event_loop: EventLoopWindowTarget<UserEvents>,
proxy: EventLoopProxy<UserEvents>,
) -> (WindowId, WebView) {
let window = WindowBuilder::new()
.with_title(title)
.build(&event_loop)
.unwrap();
let window_id = window.id();
let event_loop_ = event_loop.clone();
let handler = move |window: &Window, req: String| match req.as_str() {
"new-window" => {
//let _ = proxy.send_event(UserEvents::NewWindow());
create_new_window("something".into(), event_loop_.clone(), proxy.clone());
}
"close" => {
let _ = proxy.send_event(UserEvents::CloseWindow(window.id()));
}
_ if req.starts_with("change-title") => {
let title = req.replace("change-title:", "");
window.set_title(title.as_str());
}
_ => {}
};
let webview = WebViewBuilder::new(window)
.unwrap()
.with_html(
r#"
<button onclick="window.ipc.postMessage('new-window')">Open a new window</button>
<button onclick="window.ipc.postMessage('close')">Close current window</button>
<input oninput="window.ipc.postMessage(`change-title:${this.value}`)" />
"#,
)
.unwrap()
.with_ipc_handler(handler)
.build()
.unwrap();
(window_id, webview)
}
let event_loop = EventLoop::<UserEvents>::with_user_event();
let mut webviews = HashMap::new();
let proxy = event_loop.create_proxy();
let new_window = create_new_window(
format!("Window {}", webviews.len() + 1),
event_loop.deref().clone(),
proxy.clone(),
);
webviews.insert(new_window.0, new_window.1);
event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event, window_id, ..
} => match event {
WindowEvent::CloseRequested => {
webviews.remove(&window_id);
if webviews.is_empty() {
*control_flow = ControlFlow::Exit
}
}
WindowEvent::Resized(_) => {
let _ = webviews[&window_id].resize();
}
_ => (),
},
Event::UserEvent(UserEvents::NewWindow()) => {
let new_window = create_new_window(
format!("Window {}", webviews.len() + 1),
event_loop.clone(),
proxy.clone(),
);
webviews.insert(new_window.0, new_window.1);
}
Event::UserEvent(UserEvents::CloseWindow(id)) => {
webviews.remove(&id);
if webviews.is_empty() {
*control_flow = ControlFlow::Exit
}
}
_ => (),
}
});
}
Expected behavior A clear and concise description of what you expected to happen.
Screenshots If applicable, add screenshots to help explain your problem.
Platform and Versions (please complete the following information): OS: Rustc:
Would you assign yourself to resolve this bug?
- Yes
- No
Additional context Add any other context about the problem here.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 2
- Comments: 20 (19 by maintainers)
So my conclusion is this is really rare case that
add_WebMessageReceivedandcreat_controllercould cause deadlock. One work around I could think of is we return a IPC receiver instead of IPC handler callback. So users could decide when and how to process message. I also want to do this in the future since mac and linux can return more than just strings.As of now, Lucas and I decided we stay with it and document that it must use async or create thread to spawn another window on Windows.
Yeah, that’s how tao handles event loop. But this should be normal and
envis still on main thread I believe. I think this is typical deadlock scenario and thanks to @lucasfernog 's help that it seems problem still remains in both moving controller creation into env closure and using the same env on window creation. I suspect if we do any webview operation inadd_WebMessageReceivedcallback will result in deadlock. I’ll test it more to confirm that.