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

Rémi Bernon rbernon at codeweavers.com
Tue May 18 07:51:45 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 13a657e7a40..be3a4802d6c 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -8838,7 +8838,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;
 }
@@ -9024,7 +9024,7 @@ static void test_interthread_messages(void)
     CloseHandle( wnd_event.start_event );
     CloseHandle( wnd_event.stop_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..0780681cd53 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 || child->thread->state == TERMINATED) 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 || child->thread->state == TERMINATED) 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