[PATCH resend 2/2] server: Destroy child window from another thread only if it's not running.

Rémi Bernon rbernon at codeweavers.com
Wed Mar 4 08:51:51 CST 2020


On thread destroy, a WM_WINE_DESTROYWINDOW is sent to the child windows
living in other threads. However 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.

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

diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index 55b4fe2228b..3aa8c83f64d 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -8586,7 +8586,6 @@ static DWORD CALLBACK create_grand_child_thread( void *param )
     ok( !ret, "WaitForSingleObject returned %x, error: %u\n", ret, GetLastError() );
 
     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
-    todo_wine
     ok( !IsWindow( hchild ), "Child window not destroyed\n" );
 
     return 0;
@@ -8781,7 +8780,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 c9b131cba5d..cd413ba1023 100644
--- a/server/window.c
+++ b/server/window.c
@@ -1888,9 +1888,21 @@ 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 != RUNNING)
+            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 != RUNNING)
+            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.25.0




More information about the wine-devel mailing list