https://bugs.winehq.org/show_bug.cgi?id=54005
--- Comment #5 from Kevin Puetz PuetzKevinA@JohnDeere.com --- Comment on attachment 73628 --> https://bugs.winehq.org/attachment.cgi?id=73628 Text code exploring the behavior of reentrant cross-thread SendMessage
I came up with an even stricter (albiet very very ugly) black-box test to explore the behavior of SendMessage - I can use SuspendThread/ResumeThread to force the timing. I.e. suspend the thread which performed SendMessage once we get into the WndProc (knowing it the caller currently waiting for a reply), then perform both a SendNotifyMessage and a wndproc return, then have the window's thread's message pump resume the SendMessage thread. That *should* guaranteed that, when SendMessage resumes, both wakeup reasons already present (and are seen simultaneously).
the test below "passes" if SendMessage dispatches the nonqueued WM_USER+1 posted by SendNotifyMessage (the behavior assumed by test_set_clipboard_DRAWCLIPBOARD), and "fails" if this message is still pending (i.e. if it is dispatched by a second call to PeekMessage, rather than before SendMessage returned). There are two knobs to explore some weird quirks - SUSPEND_THREAD (whether or not to do this Suspend/ResumeThread dance) and USE_HWND_MESSAGE (whether or not to use a "real" top-level window, or a message-only child of HWND_MESSAGE).
As seen in test_set_clipboard_DRAWCLIPBOARD, with neither the test usually (though only about 90%) passes on windows. So the scheduler seems to switch threads immediately. Even a little bit more code after the SendMessage nearly guarantees it. This all fits with the behavior seen in test_set_clipboard_DRAWCLIPBOARD, where a message queued before the call to SendMessage just about always gets dispatched.
However, adding SUSPEND_THREAD flips it, now to about 90% failure. It still passes occasionally, which is confusing (the SuspendThread/GetThreadContext/ResumeThread dance should make it *impossible* for the SendMessage call to run dispatch the message queued by SendNotifyMessage before the WndProc has also returned). I think what's happening here is that, even with GetThreadContext, SuspendThread is still only synchronized with respect to user mode; if we catch the thread inside a kernel syscall, it will not actually suspend until the return to user space. So if SendMessage has begun some kind of wait-primitive syscall, that call's return value might still race between the QS_SENDMESSAGE and QS_SMRESULT, and return a state that shows only the former. Then, when the thread is resumed later, it will still receive this stale return value. So it makes the race narrower, but (assuming some of this is in kernel mode, which seems likely) not gone.
I am baffled by the interaction with HWND_MESSAGE - using this makes the test run much faster, makes the !SUSPEND_THREAD fail more often (now only about 50% successful), and makes SUSPEND_THREAD fail ever time (as far as I can tell). I strongly supect the "faster" is related - quite a bit of work is being skipped (e.g. a normal window gets its icon placed on the taskbar), and this shifts the timing enough to greatly affect the whatever race condition we're poking at).
I doubt we want any tests this nasty added to winetest. But I think the answers heavily favor the "consistency in thread scheduling" explanation, and the SUSPEND_THREAD case seems to disprove zf's patch (because it shows that SendMessage does not guarantee dispatch of nonqueued messages which preceded the result); It is interesting how much the pass/fail rate seems to be reciprocal; it's probably dependent on basically the basic same scheduler question (whether the thread calling SendMessage reaches the kernel wait primitive before or after a context switch to the wndproc's thread)
So it's either test_set_clipboard_DRAWCLIPBOARD is indeed just a flaky test (and windows *could* fail, but mostly doesn't since it's hard to cause so much delay), or perhaps the native implementations of OleSetClipboard (or marshaling, or who knows what) just has another PeekMessage or similar buried in it somewhere that wine lacks (given the nature of OLE/RPC being full of such, this is easy to believe). Any PeekMessage would do; the filtering is not applied to nonqueued messages.