Compare commits

...

2 commits

Author SHA1 Message Date
Zhiyi Zhang
202273ed97 Merge branch 'fix-explorer-msg-queue-hang' into 'master'
winex11.drv: Fix a possible desktop window message queue hang.

See merge request wine/wine!5890
2024-11-16 13:44:11 +00:00
Zhiyi Zhang
360dc82fba winex11.drv: Fix a possible desktop window message queue hang.
After an X11 event handler queues messages to another thread, TRUE should be returned and eventually
propagated to X11DRV_ProcessEvents().

When FALSE is always returned in xrandr14_device_change_handler(), a possible hang can happen for
the desktop message queue as follows:

1. The explorer.exe calls GetMessageW() -> NtUserGetMessage() -> wait_objects() -> wait_message().
2. In wait_message(), user_driver->pProcessEvents() gets called in the desktop window thread to
handle RRNotify events and calls xrandr14_device_change_handler() -> display_mode_changed(FALSE)
-> send_message(get_desktop_window(), WM_DISPLAYCHANGE, ...) -> desktop_window_proc() ->
send_message_timeout() -> send_client_message() -> process_message() -> broadcast_message() ->
send_message_timeout() -> send_client_message() -> process_message() -> send_inter_thread_message()
-> wait_message_reply() -> a server set_queue_mask() with skip_wait being 1 -> wake_mask and
changed_mask are set to 0.
3. In wait_message(), user_driver->pProcessEvents() returns FALSE from xrandr14_device_change_handler().
So wait_message() continues to call NtWaitForMultipleObjects().
4. Now NtWaitForMultipleObjects() hangs for INFINITE timeout because wake_mask and changed_mask
for the message queue are set to 0 so the thread is not woke up.

The hang is sensitive to message ordering and only happens in this specific case so it's hard to
reproduce with tests. I believe some of the past test timeouts on TestBots can be attributed to this
bug.
2024-08-23 12:10:16 +08:00

View file

@ -1215,15 +1215,19 @@ static void xrandr14_free_monitors( struct gdi_monitor *monitors, int count )
static BOOL xrandr14_device_change_handler( HWND hwnd, XEvent *event )
{
BOOL ret = FALSE;
RECT rect;
xrandr14_invalidate_current_mode_cache();
if (hwnd == NtUserGetDesktopWindow() && NtUserGetWindowThread( hwnd, NULL ) == GetCurrentThreadId())
{
NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged );
ret = TRUE;
}
/* Update xinerama monitors for xinerama_get_fullscreen_monitors() */
rect = get_host_primary_monitor_rect();
xinerama_init( rect.right - rect.left, rect.bottom - rect.top );
return FALSE;
return ret;
}
static void xrandr14_register_event_handlers(void)