[PATCH v5 2/2] server: Destroy child window from another thread only if it's not running.
Rémi Bernon
rbernon at codeweavers.com
Mon Jan 13 10:11:43 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>
---
Notes:
v5: Instead of conditionally detaching child windows from the owning
thread, which left some dangling references, this checks the child
window's thread and state before destroying it. If the owning thread
is still running, it will reap its own windows on exit. Otherwise it
should be safe to destroy the windows on its behalf.
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 1dd1b5978e7..030fdfaf23b 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -8593,7 +8593,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;
@@ -8788,7 +8787,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.rc2
More information about the wine-devel
mailing list