[PATCH] ntdll: Make RtlDeregisterWaitEx(handle, INVALID_HANDLE_VALUE) thread safe.
Stefan Dösinger
stefan at codeweavers.com
Tue Sep 5 10:38:30 CDT 2017
Hi,
Is the test I was referring to in the description of the previous patch.
It causes seemingly unrelated test failures like
https://testbot.winehq.org/JobDetails.pl?Key=32956 (previous iteration
of the test that used a different implementation)
Stefan
Am 2017-09-05 um 17:34 schrieb Stefan Dösinger:
> Chromium signals the wait semaphore and calls DeregisterWaitEx with
> CompletionHandle = INVALID_HANDLE_VALUE in close succession. Sometimes
> the worker thread decides to run the callback, but before it sets
> CallbackInProgress RtlDeregisterWaitEx decides that the callback is not
> running and returns STATUS_SUCCESS. Chromium then releases resources
> that the callback needs to run, resulting in random crashes.
>
> Signed-off-by: Stefan Dösinger <stefan at codeweavers.com>
>
> ---
>
> The tests show that we're only supposed to return STATUS_PENDING if the
> callback is running. Note that the choice between waiting and returning
> STATUS_SUCCESS and not waiting and returning STATUS_PENDING is still
> racy, but we'll no longer return STATUS_SUCCESS and run the callback
> afterwards.
>
> I tried to extend the tests to show what happens when both events are
> available simultanously. The answer is that it is fairly random.
> INVALID_HANDLE_VALUE blocks and runs the callback one last time, the
> other ways of calling RtlDeregisterWait[Ex] randomly return STATUS_SUCCESS
> and do not run the callback, or they return STATUS_PENDING and may or
> may not run the callback. However, in no circumstance does the callback
> run after STATUS_SUCCESS is returned.
>
> I am not including the tests because of the random outcome and because
> it plays with SuspendThread and makes the random failures in follow up
> tests worse. I'll send it to wine-devel for reference.
> ---
> dlls/ntdll/threadpool.c | 59 +++++++++++++++++++++++++++----------------------
> 1 file changed, 32 insertions(+), 27 deletions(-)
>
> diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
> index 6063d51d9f..1b0546a781 100644
> --- a/dlls/ntdll/threadpool.c
> +++ b/dlls/ntdll/threadpool.c
> @@ -541,11 +541,13 @@ static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
> break;
> }
>
> - completion_event = wait_work_item->CompletionEvent;
> - if (completion_event) NtSetEvent( completion_event, NULL );
>
> if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
> + {
> + completion_event = wait_work_item->CompletionEvent;
> delete_wait_work_item( wait_work_item );
> + if (completion_event) NtSetEvent( completion_event, NULL );
> + }
>
> return 0;
> }
> @@ -633,44 +635,47 @@ NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
> NTSTATUS WINAPI RtlDeregisterWaitEx(HANDLE WaitHandle, HANDLE CompletionEvent)
> {
> struct wait_work_item *wait_work_item = WaitHandle;
> - NTSTATUS status = STATUS_SUCCESS;
> + NTSTATUS status;
> + HANDLE LocalEvent = NULL;
> + BOOLEAN CallbackInProgress;
>
> - TRACE( "(%p)\n", WaitHandle );
> + TRACE( "(%p %p)\n", WaitHandle, CompletionEvent );
>
> if (WaitHandle == NULL)
> return STATUS_INVALID_HANDLE;
>
> - NtSetEvent( wait_work_item->CancelEvent, NULL );
> - if (wait_work_item->CallbackInProgress)
> + CallbackInProgress = wait_work_item->CallbackInProgress;
> + if (CompletionEvent == INVALID_HANDLE_VALUE || !CallbackInProgress)
> {
> - if (CompletionEvent != NULL)
> - {
> - if (CompletionEvent == INVALID_HANDLE_VALUE)
> - {
> - status = NtCreateEvent( &CompletionEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
> - if (status != STATUS_SUCCESS)
> - return status;
> - interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
> - if (wait_work_item->CallbackInProgress)
> - NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
> - NtClose( CompletionEvent );
> - }
> - else
> - {
> - interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
> - if (wait_work_item->CallbackInProgress)
> - status = STATUS_PENDING;
> - }
> - }
> - else
> - status = STATUS_PENDING;
> + status = NtCreateEvent( &LocalEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
> + if (status != STATUS_SUCCESS)
> + return status;
> + interlocked_xchg_ptr( &wait_work_item->CompletionEvent, LocalEvent );
> + }
> + else if (CompletionEvent != NULL)
> + {
> + interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
> }
>
> + NtSetEvent( wait_work_item->CancelEvent, NULL );
> +
> if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
> {
> status = STATUS_SUCCESS;
> delete_wait_work_item( wait_work_item );
> }
> + else if (LocalEvent)
> + {
> + NtWaitForSingleObject( LocalEvent, FALSE, NULL );
> + status = STATUS_SUCCESS;
> + }
> + else
> + {
> + status = STATUS_PENDING;
> + }
> +
> + if (LocalEvent)
> + NtClose( LocalEvent );
>
> return status;
> }
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0008-ntdll-Add-tests-for-simultaneous-signalling-and-dere.patch
Type: text/x-patch
Size: 12025 bytes
Desc: not available
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20170905/f90b4aa4/attachment-0001.bin>
More information about the wine-devel
mailing list