[PATCH v2 2/2] server: Don't destroy child windows from live other threads.

Rémi Bernon rbernon at codeweavers.com
Tue May 18 09:03:12 CDT 2021


On thread destroy, a WM_WINE_DESTROYWINDOW is sent to the child windows
living in other threads.

There's then a race condition between these threads peeking for messages
and the current thread detaching its child windows from their threads
and clearing their message queues, and the message may never be received
from these threads and the windows kept alive.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/user32/tests/msg.c |  4 ++--
 server/window.c         | 12 ++++++++++--
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index 462053a951d..6469bc48ba8 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -8843,7 +8843,7 @@ static DWORD CALLBACK create_grand_child_thread( void *param )
     ok( !ret, "WaitForSingleObject returned %x, error: %u\n", ret, GetLastError() );
     ok( IsWindow( hchild ), "Child window already destroyed\n" );
     flush_events();
-    todo_wine ok( !IsWindow( hchild ), "Child window not destroyed\n" );
+    ok( !IsWindow( hchild ), "Child window not destroyed\n" );
 
     return 0;
 }
@@ -9032,7 +9032,7 @@ static void test_interthread_messages(void)
     CloseHandle( wnd_event.stop_event );
     CloseHandle( wnd_event.ready_event );
     flush_events();
-    ok_sequence( WmExitThreadSeq, "destroy child on thread exit", TRUE );
+    ok_sequence( WmExitThreadSeq, "destroy child on thread exit", FALSE );
     log_all_parent_messages--;
     DestroyWindow( wnd_event.hwnd );
 
diff --git a/server/window.c b/server/window.c
index ac12912db3a..026d54e6d32 100644
--- a/server/window.c
+++ b/server/window.c
@@ -1890,9 +1890,17 @@ void destroy_window( struct window *win )
 
     /* destroy all children */
     while (!list_empty(&win->children))
-        destroy_window( LIST_ENTRY( list_head(&win->children), struct window, entry ));
+    {
+        struct window *child = LIST_ENTRY( list_head( &win->children ), struct window, entry );
+        if (!child->thread || child->thread == win->thread) destroy_window( child );
+        else list_remove( &child->entry );
+    }
     while (!list_empty(&win->unlinked))
-        destroy_window( LIST_ENTRY( list_head(&win->unlinked), struct window, entry ));
+    {
+        struct window *child = LIST_ENTRY( list_head( &win->unlinked ), struct window, entry );
+        if (!child->thread || child->thread == win->thread) destroy_window( child );
+        else list_remove( &child->entry );
+    }
 
     /* reset global window pointers, if the corresponding window is destroyed */
     if (win == shell_window) shell_window = NULL;
-- 
2.31.0




More information about the wine-devel mailing list