fix(windows): use CoWaitForMultipleHandles for ARM64 WebView2 deadlock #1666
+432
−39
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Replaces
mpsc::channel+wait_with_pumppattern withCoWaitForMultipleHandlesin three WebView2 call sites, fixing deterministic deadlock on Windows ARM64 and latent reentrancy issue on all architectures.Closes #1665. Related: #583.
Problem
On Windows ARM64 (Snapdragon X Elite,
aarch64-pc-windows-msvc), creating second WebView2 controller from main STA thread deadlocks insideMsgWaitForMultipleObjectsEx. Root cause is thatwebview2_com::wait_with_pumpruns nested Win32 message pump (GetMessage/PeekMessage), which does not allow COM to re-enter apartment to deliver async completion callbacks. On x86/x64 this usually works by accident because WebView2's internal RPC piggybacks on window messages, but on ARM64 timing exposes bug deterministically.Root cause behind #583 (deadlock creating new window from an IPC handler), which was worked around but never fixed at source.
Solution
Replace all three
mpsc::channel+wait_with_pumpcall sites with:ust CoWaitForMultipleHandles( COWAIT_DISPATCH_CALLS | COWAIT_DISPATCH_WINDOW_MESSAGES, INFINITE, &[event], )COM-sanctioned mechanism for yielding STA thread while preserving reentrancy:
COWAIT_DISPATCH_CALLSdispatches incoming COM calls (required for WebView2 async completion callbacks)COWAIT_DISPATCH_WINDOW_MESSAGESdispatches window messages (keeps the event loop responsive)Approach is consistent with Microsoft's WebView2 threading model documentation on reentrancy.
Design notes
mpsc::channel: previous pattern usedmpsc::channel+wait_with_pump(and comments explained whywait_for_asyncwas avoided). PR removes approach entirely because underlying Win32 message pump prevents COM STA reentrancy, exact mechanism WebView2 needs to deliver completion callbacks.Rc<RefCell<_>>instead ofArc<Mutex<_>>: completion callbacks run on same STA thread (dispatched byCoWaitForMultipleHandles), soRc/RefCellis correct COM types inside aren'tSend/Sync.Win32_Securityfeature gate: Required bywindowscrate'sCreateEventWsignature (SECURITY_ATTRIBUTES). Verified won't compile without it.Affected call sites
create_environment()CreateCoreWebView2EnvironmentWithOptionscreate_controller()CreateCoreWebView2Controller/CreateCoreWebView2ControllerWithOptionscookies_inner()CookieManager().GetCookiesTesting
co_wait_for_handlebehavior, theRc<RefCell<Option<T>>>+ event signaling pattern, STA reentrancy dispatch, and fullcreate_environment/create_controllerintegrationcargo testpasses (8 passed, 0 failed)cargo clippyclean (0 warnings)#[cfg(target_os = windows)])Minimal Reproduction
https://github.com/npiesco/wry-arm64-deadlock
mainbranch: deadlocks on ARM64 (uses upstream wry)fix/vendored-wry-tauribranch: works (uses patched wry fork)