Skip to content

fix(runtime-wry): query monitors on main thread from runtime handle#15630

Open
tenderdeve wants to merge 4 commits into
tauri-apps:devfrom
tenderdeve:fix/15170-monitor-queries-off-thread
Open

fix(runtime-wry): query monitors on main thread from runtime handle#15630
tenderdeve wants to merge 4 commits into
tauri-apps:devfrom
tenderdeve:fix/15170-monitor-queries-off-thread

Conversation

@tenderdeve

Copy link
Copy Markdown

Closes #15170

WryHandle (the app/AppHandle-level runtime handle) implemented primary_monitor, monitor_from_point and available_monitors by reading self.context.main_thread.window_target directly on the calling thread. The tao EventLoopWindowTarget is not thread safe (GTK on Linux, Win32 on Windows), so calling these from a background thread races with the UI thread — the reporter sees Aborted (core dumped), Segmentation fault, and malloc(): unaligned fastbin chunk within seconds.

cursor_position on the same handle already avoids this by marshalling through the event loop. This routes the three monitor queries the same way, via new EventLoopWindowTargetMessage variants handled on the main thread. send_user_message runs inline when already on the main thread, so no deadlock and no behavior change for main-thread callers.

The main-thread Runtime impl keeps its direct access — it already runs on the UI thread.

No repro on my machine (crash is Linux/Windows only), but the fix removes the off-thread window_target access that is the documented cause, mirroring the existing cursor_position path.

WryHandle::primary_monitor, monitor_from_point and available_monitors
read the event loop's window target directly on the calling thread. That
window target is not thread safe, so calling these functions from a
background thread races with the UI thread and can segfault or corrupt
the heap.

Route the three queries through the event loop like cursor_position
already does, so the window target is only accessed on the main thread.

Closes tauri-apps#15170
@tenderdeve tenderdeve requested a review from a team as a code owner July 1, 2026 10:47
Comment thread crates/tauri-runtime-wry/src/lib.rs
Reuse the existing event_loop_window_getter! macro (as cursor_position does)
instead of hand-rolling the channel + send_user_message boilerplate in
primary_monitor, monitor_from_point and available_monitors. These methods
return bare Option/Vec, so a small closure absorbs the macro's internal `?`.
@tenderdeve

Copy link
Copy Markdown
Author

@FabianLars done in fc2078b — switched all three (primary_monitor, monitor_from_point, available_monitors) to event_loop_window_getter! like cursor_position.

One wrinkle: the macro expands to send_user_message(..)?, so it needs a Result/Option-returning scope. cursor_position returns Result, but these three RuntimeHandle methods return a bare Option<Monitor> / Vec<Monitor> (the Dispatcher versions return Result<..>, which is why they use it directly). So I wrapped each call in a small (|| ..)() closure to absorb the ?, then .ok().flatten(). monitor_from_point also passes a |tx| .. closure for the extra point arg.

Reads cleaner for primary_monitor/available_monitors; monitor_from_point ends up slightly nested. If you'd rather keep that one explicit (or want the trait methods changed to return Result — bigger change), say the word.

@FabianLars

Copy link
Copy Markdown
Member

Ah shit i didn't see the ? in the macro. I think the previous version was better than the closure wrapper then - sorry for the back and forth. Please revert the last commit then, and while you're at it i think it'd be better to return None if send_user_message returns an error, otherwise there's a chance we're getting stuck on the recv() call.

If send_user_message errors the event loop will never reply, so recv()
would block the calling thread forever. Return None / an empty Vec on a
send error instead of waiting.
@tenderdeve

Copy link
Copy Markdown
Author

@FabianLars done in a989173 (revert in 57bd57c) — no worries on the back-and-forth. Back to the explicit send_user_message + recv() version, and now each of the three bails out early (None / empty Vec) if the send fails, so a dropped message can no longer leave the caller blocked on recv(). cargo check -p tauri-runtime-wry is green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Multiple app handle functions can crash Tauri, including available_monitors and cursor_position

2 participants