fix(runtime-wry): Fix mobile crash: RefCell already borrowed - #15502
Conversation
|
This is less of a review than a shooting-from-the-hip initial remark, but while this fixed the crash it isn't super obvious from the description if the original implementation assumed the data under the RefCell is not modified. If the callback tries to |
It's a good point to raise - on some level the original did assume that the data under the RefCell wasn't modified, but the method of enforcing it (panicking) wasn't ideal. With this change, any code that didn't previously panic should work exactly the same, but behind the scenes, the borrow on |
|
Yes, a rephrasing of my imprecise question would be what invariants are expected to hold across the loop. |
Package Changes Through bce3221There are 6 changes which include tauri-utils with patch, tauri-runtime-wry with patch, tauri with patch, tauri-bundler with patch, tauri-cli with patch, @tauri-apps/cli with patch Planned Package VersionsThe following package releases are the planned based on the context of changes in this pull request.
Add another change file through the GitHub UI by following this link. Read about change files or the docs at github.com/jbolda/covector |
…pps#15502) * fix(runtime-wry): RefCell borrow held across callbacks on mobile Resumed/Suspended * Add .changes * Rename mobile-resume-suspend-deadlock to mobile-resume-suspend-deadlock.md
Summary
On mobile, the wry event loop's
Event::Resumed | Event::Suspendedbranch held awindows.0RefCellborrow across both the per-window event handlers and the userRunEventcallback. If any of those mutates the window set — i.e. takeswindows.0.borrow_mut()(creating or closing a window) — it panics withalready borrowed: BorrowMutError. These events fire on every app foreground/background, so e.g. creating a window from a resume/suspend handler hard-crashes the app.Creating/closing a window from a window/lifecycle handler is a supported pattern on desktop, so this is effectively a desktop→mobile parity crash.
MRE repo available here: https://github.com/bclarke123/tauri-resume-mre - install as normal,
bun run tauri ios initorbun run tauri android init, run on device, follow repro instructions belowRoot cause
The window-mutating dispatch runs inline on the main thread (send_user_message short-circuits to handle_user_message when already on the UI thread), so it re-enters windows.0.borrow_mut() while the borrow above is still held → panic.
The three desktop WindowEvent branches (UserEvent synthesized window events, SynthesizedWindowEvent, and Event::WindowEvent) all drop the windows borrow before invoking the callback/handlers — precisely so they can mutate windows. This mobile branch was the exception; that asymmetry is the bug.
Fix
Collect the per-window listener handles, release the windows borrow, then dispatch - matching the desktop branches. Dispatch order (handlers then callback) is unchanged.
Reproduction
Minimal app: create a window from a resume/suspend handler.
Background the app, then foreground it. On unpatched Tauri this crashes:
thread '' panicked at .../tauri-runtime-wry-2.11.2/src/lib.rs:4086:19: RefCell already borrowed
iOS backtrace (abridged) shows the re-entry clearly: will_resign_active → handle_event_loop → for_each (windows.0.borrow) → RunEvent callback → WebviewWindowBuilder::build → create_window → send_user_message (inline) → handle_user_message → RefCell::borrow_mut (lib.rs:4086).
Reproduced on a physical device on both iOS and Android (identical panic at lib.rs:4086, RefCell already borrowed). With this fix applied, both platforms create the window cleanly with no crash through repeated background/foreground cycles.
Testing
No automated test: the branch is #[cfg(mobile)] and reproducing requires the live tao/wry event loop emitting Resumed/Suspended plus a window-mutating handler — which the desktop-based test suite can't exercise. Verified to compile on the affected target:
cargo check -p tauri-runtime-wry --target aarch64-apple-ios
plus the manual on-device red→green above (iOS + Android).
Notes