This test is failing from time to time. Making sure the parent thread has terminated before continuing triggers the underlying race condition, and makes the test to always fail.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Notes: v4: General clean up: * Directly check the window in grand child thread instead of waiting. It should have been destroyed already as we waited for thread completion. * Start the threads before flushing events and cleaning up sequences, it should be the same at the end but it gives more time for children to create their own windows. * Share all the handles from the main stack. Wait directly on the child thread handle from the grand child instead of using an additional event. * Increase some of the remaining timeouts, it still sometimes times out on testbot Windows VMs otherwise.
The failure was hard to reproduce. The most reliable way without the sleep added here is to start multiple runs on the testbot, changing locale for each run. The locale change seems to have an impact on the timing and triggers the race condition.
dlls/user32/tests/msg.c | 61 +++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 193a20fd958..a1deab10de5 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -8548,6 +8548,8 @@ static void test_paint_messages(void) struct wnd_event { HWND hwnd; + HWND child_hwnd; + HANDLE child; HANDLE grand_child; HANDLE start_event; HANDLE stop_event; @@ -8580,44 +8582,50 @@ static DWORD CALLBACK create_grand_child_thread( void *param ) struct wnd_event *wnd_event = param; HWND hchild; MSG msg; + DWORD ret;
hchild = CreateWindowExA(0, "TestWindowClass", "Test child", - WS_CHILD | WS_VISIBLE, 0, 0, 10, 10, wnd_event->hwnd, 0, 0, NULL); + WS_CHILD | WS_VISIBLE, 0, 0, 10, 10, wnd_event->child_hwnd, 0, 0, NULL); ok (hchild != 0, "Failed to create child window\n"); flush_events(); flush_sequence(); SetEvent( wnd_event->start_event );
- for (;;) - { - MsgWaitForMultipleObjects(0, NULL, FALSE, 1000, QS_ALLINPUT); - if (!IsWindow( hchild )) break; /* will be destroyed when parent thread exits */ - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - } + ret = WaitForSingleObject( wnd_event->child, 5000 ); + 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; }
static DWORD CALLBACK create_child_thread( void *param ) { struct wnd_event *wnd_event = param; - struct wnd_event child_event; DWORD ret, tid; MSG msg;
- child_event.hwnd = CreateWindowExA(0, "TestWindowClass", "Test child", - WS_CHILD | WS_VISIBLE, 0, 0, 10, 10, wnd_event->hwnd, 0, 0, NULL); - ok (child_event.hwnd != 0, "Failed to create child window\n"); - SetFocus( child_event.hwnd ); + wnd_event->child_hwnd = CreateWindowExA( 0, "TestWindowClass", "Test child", + WS_CHILD | WS_VISIBLE, 0, 0, 10, 10, + wnd_event->hwnd, 0, 0, NULL ); + ok (wnd_event->child_hwnd, "Failed to create child windows\n"); + SetFocus( wnd_event->child_hwnd ); + + wnd_event->grand_child = CreateThread( NULL, 0, create_grand_child_thread, wnd_event, 0, &tid ); + ok( wnd_event->grand_child, "CreateThread failed, error %u\n", GetLastError() ); + flush_events(); flush_sequence(); - child_event.start_event = wnd_event->start_event; - wnd_event->grand_child = CreateThread(NULL, 0, create_grand_child_thread, &child_event, 0, &tid); for (;;) { - DWORD ret = MsgWaitForMultipleObjects(1, &child_event.start_event, FALSE, 1000, QS_SENDMESSAGE); + ret = MsgWaitForMultipleObjects(1, &wnd_event->start_event, FALSE, 5000, QS_SENDMESSAGE); if (ret != 1) break; while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); } + ok( !ret, "MsgWaitForMultipleObjects failed %x, error: %u\n", ret, GetLastError() ); + ret = WaitForSingleObject( wnd_event->stop_event, 5000 ); ok( !ret, "WaitForSingleObject failed %x\n", ret ); return 0; @@ -8751,33 +8759,38 @@ static void test_interthread_messages(void) wnd_event.hwnd = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, 0, NULL); ok (wnd_event.hwnd != 0, "Failed to create parent window\n"); + + wnd_event.start_event = CreateEventA( NULL, TRUE, FALSE, NULL ); + wnd_event.stop_event = CreateEventA( NULL, TRUE, FALSE, NULL ); + + wnd_event.child = CreateThread( NULL, 0, create_child_thread, &wnd_event, 0, &tid ); + ok( wnd_event.child, "CreateThread failed, error %u\n", GetLastError() ); + flush_events(); flush_sequence(); log_all_parent_messages++; - wnd_event.start_event = CreateEventA( NULL, TRUE, FALSE, NULL ); - wnd_event.stop_event = CreateEventA( NULL, TRUE, FALSE, NULL ); - hThread = CreateThread( NULL, 0, create_child_thread, &wnd_event, 0, &tid ); for (;;) { - ret = MsgWaitForMultipleObjects(1, &wnd_event.start_event, FALSE, 1000, QS_SENDMESSAGE); + ret = MsgWaitForMultipleObjects(1, &wnd_event.start_event, FALSE, 5000, QS_SENDMESSAGE); if (ret != 1) break; while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); } ok( !ret, "MsgWaitForMultipleObjects failed %x\n", ret ); /* now wait for the thread without processing messages; this shouldn't deadlock */ SetEvent( wnd_event.stop_event ); - ret = WaitForSingleObject( hThread, 5000 ); + + ret = WaitForSingleObject( wnd_event.child, 5000 ); ok( !ret, "WaitForSingleObject failed %x\n", ret ); - CloseHandle( hThread );
ret = WaitForSingleObject( wnd_event.grand_child, 5000 ); - ok( !ret, "WaitForSingleObject failed %x\n", ret ); - CloseHandle( wnd_event.grand_child ); + ok( !ret, "WaitForSingleObject failed %x, error: %u\n", ret, GetLastError() );
+ CloseHandle( wnd_event.child ); + CloseHandle( wnd_event.grand_child ); CloseHandle( wnd_event.start_event ); CloseHandle( wnd_event.stop_event ); flush_events(); - ok_sequence(WmExitThreadSeq, "destroy child on thread exit", FALSE); + ok_sequence(WmExitThreadSeq, "destroy child on thread exit", TRUE); log_all_parent_messages--; DestroyWindow( wnd_event.hwnd );
On thread destroy, a WM_WINE_DESTROYWINDOW is sent for the child windows owned by 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 owning threads -which clears their message queues.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/tests/msg.c | 3 +-- server/window.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index a1deab10de5..fac340744b6 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -8595,7 +8595,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; @@ -8790,7 +8789,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..3985ea4eb20 100644 --- a/server/window.c +++ b/server/window.c @@ -1914,7 +1914,7 @@ void destroy_window( struct window *win ) post_message( win->parent->handle, WM_PARENTNOTIFY, WM_DESTROY, win->handle ); }
- detach_window_thread( win ); + if (win->thread == current) detach_window_thread( win ); if (win->win_region) free_region( win->win_region ); if (win->update_region) free_region( win->update_region ); if (win->class) release_class( win->class );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=59743
Your paranoid android.
=== w1064v1809 (32 bit report) ===
user32: msg.c:9416: Test failed: Shift+MouseButton press/release: 12: in msg 0x0202 expecting wParam 0x4 got 0x0 msg.c:9416: Test failed: Shift+MouseButton press/release: 13: in msg 0x0202 expecting wParam 0x4 got 0x0
=== debian10 (32 bit Japanese:Japan report) ===
Report errors: user32:msg prints too much data (35183 bytes)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=59742
Your paranoid android.
=== w1064v1809_ja (task log) ===
Task errors: The task timed out
=== w1064v1809_zh_CN (32 bit report) ===
user32: msg.c:10343: Test failed: did not get one count as expected (2). msg.c:10347: Test failed: killing replaced timer did not work (2).
=== debian10 (32 bit Japanese:Japan report) ===
Report errors: user32:msg prints too much data (35361 bytes)