[PATCH 6/7] ntdll: Re-implement RtlRegisterWait using TpSetWait.
Rémi Bernon
rbernon at codeweavers.com
Wed Dec 2 04:01:15 CST 2020
This adds several internal flags to TP_WAIT object to support the
implementation:
* WT_EXECUTEONLYONCE: waits are re-queued unless it is set.
* WT_EXECUTEINWAITTHREAD: call the callback in the wait thread when set.
* WT_EXECUTEINIOTHREAD: call alertable NtWaitForMultipleObjects in wait
thread when set, as well the callback in the wait thread, as for
WT_EXECUTEINWAITTHREAD. The worker threads use non-alertable waits
otherwise.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47843
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
dlls/kernel32/tests/thread.c | 1 -
dlls/ntdll/tests/threadpool.c | 6 +-
dlls/ntdll/threadpool.c | 264 +++++++++++++---------------------
3 files changed, 100 insertions(+), 171 deletions(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c
index b69f69fadaa..e861aa751e9 100644
--- a/dlls/kernel32/tests/thread.c
+++ b/dlls/kernel32/tests/thread.c
@@ -1367,7 +1367,6 @@ static void CALLBACK waitthread_test_function(PVOID p, BOOLEAN TimerOrWaitFired)
SetEvent(param->trigger_event);
ret = WaitForSingleObject(param->wait_event, 100);
- todo_wine
ok(ret == WAIT_TIMEOUT, "wait should have timed out\n");
SetEvent(param->complete_event);
}
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index e18ad4dd76d..6b301eafcbd 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -319,7 +319,7 @@ static void test_RtlRegisterWait(void)
result = WaitForSingleObject(semaphores[0], 200);
ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
- todo_wine ok(info.threadid == threadid, "unexpected different wait thread id %x\n", info.threadid);
+ ok(info.threadid == threadid, "unexpected different wait thread id %x\n", info.threadid);
result = WaitForSingleObject(semaphores[1], 0);
ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
Sleep(50);
@@ -350,7 +350,7 @@ static void test_RtlRegisterWait(void)
result = WaitForSingleObject(semaphores[0], 200);
ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
- todo_wine ok(info.threadid == threadid, "unexpected different wait thread id %x\n", info.threadid);
+ ok(info.threadid == threadid, "unexpected different wait thread id %x\n", info.threadid);
result = WaitForSingleObject(semaphores[1], 0);
ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
Sleep(50);
@@ -433,7 +433,6 @@ static void test_RtlRegisterWait(void)
ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status);
ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
result = WaitForSingleObject(event, 200);
- todo_wine
ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
/* test RtlDeregisterWaitEx after wait expired */
@@ -470,7 +469,6 @@ static void test_RtlRegisterWait(void)
ok(!status, "RtlDeregisterWaitEx failed with status %x\n", status);
ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
result = WaitForSingleObject(event, 200);
- todo_wine
ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
/* test RtlDeregisterWaitEx while callback is running */
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index e9ca32a3be7..331149855ab 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -68,19 +68,6 @@ static RTL_CRITICAL_SECTION_DEBUG critsect_compl_debug =
0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_compl_cs") }
};
-struct wait_work_item
-{
- HANDLE Object;
- HANDLE CancelEvent;
- WAITORTIMERCALLBACK Callback;
- PVOID Context;
- ULONG Milliseconds;
- ULONG Flags;
- HANDLE CompletionEvent;
- LONG DeleteCount;
- int CallbackInProgress;
-};
-
struct timer_queue;
struct queue_timer
{
@@ -170,6 +157,7 @@ struct threadpool_object
struct list pool_entry;
RTL_CONDITION_VARIABLE finished_event;
RTL_CONDITION_VARIABLE group_finished_event;
+ HANDLE completed_event;
LONG num_pending_callbacks;
LONG num_running_callbacks;
LONG num_associated_callbacks;
@@ -206,6 +194,8 @@ struct threadpool_object
struct list wait_entry;
ULONGLONG timeout;
HANDLE handle;
+ DWORD flags;
+ RTL_WAITORTIMERCALLBACKFUNC rtl_callback;
} wait;
struct
{
@@ -302,6 +292,7 @@ struct waitqueue_bucket
struct list reserved;
struct list waiting;
HANDLE update_event;
+ BOOL alertable;
};
/* global I/O completion queue object */
@@ -372,7 +363,7 @@ static inline struct threadpool_instance *impl_from_TP_CALLBACK_INSTANCE( TP_CAL
static void CALLBACK threadpool_worker_proc( void *param );
static void tp_object_submit( struct threadpool_object *object, BOOL signaled );
-static void tp_object_execute( struct threadpool_object *object );
+static void tp_object_execute( struct threadpool_object *object, BOOL wait_thread );
static void tp_object_prepare_shutdown( struct threadpool_object *object );
static BOOL tp_object_release( struct threadpool_object *object );
static struct threadpool *default_threadpool = NULL;
@@ -1266,9 +1257,21 @@ static void CALLBACK waitqueue_thread_proc( void *param )
if (wait->u.wait.timeout <= now.QuadPart)
{
/* Wait object timed out. */
- list_remove( &wait->u.wait.wait_entry );
- list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
- tp_object_submit( wait, FALSE );
+ if ((wait->u.wait.flags & WT_EXECUTEONLYONCE))
+ {
+ list_remove( &wait->u.wait.wait_entry );
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ }
+ if ((wait->u.wait.flags & (WT_EXECUTEINWAITTHREAD | WT_EXECUTEINIOTHREAD)))
+ {
+ InterlockedIncrement( &wait->refcount );
+ wait->num_pending_callbacks++;
+ RtlEnterCriticalSection( &wait->pool->cs );
+ tp_object_execute( wait, TRUE );
+ RtlLeaveCriticalSection( &wait->pool->cs );
+ tp_object_release( wait );
+ }
+ else tp_object_submit( wait, FALSE );
}
else
{
@@ -1290,7 +1293,7 @@ static void CALLBACK waitqueue_thread_proc( void *param )
assert( num_handles == 0 );
RtlLeaveCriticalSection( &waitqueue.cs );
timeout.QuadPart = (ULONGLONG)THREADPOOL_WORKER_TIMEOUT * -10000;
- status = NtWaitForMultipleObjects( 1, &bucket->update_event, TRUE, FALSE, &timeout );
+ status = NtWaitForMultipleObjects( 1, &bucket->update_event, TRUE, bucket->alertable, &timeout );
RtlEnterCriticalSection( &waitqueue.cs );
if (status == STATUS_TIMEOUT && !bucket->objcount)
@@ -1300,7 +1303,7 @@ static void CALLBACK waitqueue_thread_proc( void *param )
{
handles[num_handles] = bucket->update_event;
RtlLeaveCriticalSection( &waitqueue.cs );
- status = NtWaitForMultipleObjects( num_handles + 1, handles, TRUE, FALSE, &timeout );
+ status = NtWaitForMultipleObjects( num_handles + 1, handles, TRUE, bucket->alertable, &timeout );
RtlEnterCriticalSection( &waitqueue.cs );
if (status >= STATUS_WAIT_0 && status < STATUS_WAIT_0 + num_handles)
@@ -1311,9 +1314,20 @@ static void CALLBACK waitqueue_thread_proc( void *param )
{
/* Wait object signaled. */
assert( wait->u.wait.bucket == bucket );
- list_remove( &wait->u.wait.wait_entry );
- list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
- tp_object_submit( wait, TRUE );
+ if ((wait->u.wait.flags & WT_EXECUTEONLYONCE))
+ {
+ list_remove( &wait->u.wait.wait_entry );
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ }
+ if ((wait->u.wait.flags & (WT_EXECUTEINWAITTHREAD | WT_EXECUTEINIOTHREAD)))
+ {
+ wait->u.wait.signaled++;
+ wait->num_pending_callbacks++;
+ RtlEnterCriticalSection( &wait->pool->cs );
+ tp_object_execute( wait, TRUE );
+ RtlLeaveCriticalSection( &wait->pool->cs );
+ }
+ else tp_object_submit( wait, TRUE );
}
else
WARN("wait object %p triggered while object was destroyed\n", wait);
@@ -1335,7 +1349,7 @@ static void CALLBACK waitqueue_thread_proc( void *param )
struct waitqueue_bucket *other_bucket;
LIST_FOR_EACH_ENTRY( other_bucket, &waitqueue.buckets, struct waitqueue_bucket, bucket_entry )
{
- if (other_bucket != bucket && other_bucket->objcount &&
+ if (other_bucket != bucket && other_bucket->objcount && other_bucket->alertable == bucket->alertable &&
other_bucket->objcount + bucket->objcount <= MAXIMUM_WAITQUEUE_OBJECTS * 2 / 3)
{
other_bucket->objcount += bucket->objcount;
@@ -1395,6 +1409,7 @@ static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait )
struct waitqueue_bucket *bucket;
NTSTATUS status;
HANDLE thread;
+ BOOL alertable = (wait->u.wait.flags & WT_EXECUTEINIOTHREAD) != 0;
assert( wait->type == TP_OBJECT_TYPE_WAIT );
wait->u.wait.signaled = 0;
@@ -1408,7 +1423,7 @@ static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait )
/* Try to assign to existing bucket if possible. */
LIST_FOR_EACH_ENTRY( bucket, &waitqueue.buckets, struct waitqueue_bucket, bucket_entry )
{
- if (bucket->objcount < MAXIMUM_WAITQUEUE_OBJECTS)
+ if (bucket->objcount < MAXIMUM_WAITQUEUE_OBJECTS && bucket->alertable == alertable)
{
list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
wait->u.wait.bucket = bucket;
@@ -1428,6 +1443,7 @@ static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait )
}
bucket->objcount = 0;
+ bucket->alertable = alertable;
list_init( &bucket->reserved );
list_init( &bucket->waiting );
@@ -1860,6 +1876,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
memset( &object->pool_entry, 0, sizeof(object->pool_entry) );
RtlInitializeConditionVariable( &object->finished_event );
RtlInitializeConditionVariable( &object->group_finished_event );
+ object->completed_event = NULL;
object->num_pending_callbacks = 0;
object->num_running_callbacks = 0;
object->num_associated_callbacks = 0;
@@ -2077,6 +2094,9 @@ static BOOL tp_object_release( struct threadpool_object *object )
if (object->race_dll)
LdrUnloadDll( object->race_dll );
+ if (object->completed_event && object->completed_event != INVALID_HANDLE_VALUE)
+ NtSetEvent( object->completed_event, NULL );
+
RtlFreeHeap( GetProcessHeap(), 0, object );
return TRUE;
}
@@ -2101,7 +2121,7 @@ static struct list *threadpool_get_next_item( const struct threadpool *pool )
* Executes a threadpool object callback, object->pool->cs has to be
* held.
*/
-static void tp_object_execute( struct threadpool_object *object )
+static void tp_object_execute( struct threadpool_object *object, BOOL wait_thread )
{
TP_CALLBACK_INSTANCE *callback_instance;
struct threadpool_instance instance;
@@ -2129,6 +2149,7 @@ static void tp_object_execute( struct threadpool_object *object )
object->num_associated_callbacks++;
object->num_running_callbacks++;
RtlLeaveCriticalSection( &pool->cs );
+ if (wait_thread) RtlLeaveCriticalSection( &waitqueue.cs );
/* Initialize threadpool instance struct. */
callback_instance = (TP_CALLBACK_INSTANCE *)&instance;
@@ -2232,6 +2253,7 @@ static void tp_object_execute( struct threadpool_object *object )
}
skip_cleanup:
+ if (wait_thread) RtlEnterCriticalSection( &waitqueue.cs );
RtlEnterCriticalSection( &pool->cs );
/* Simple callbacks are automatically shutdown after execution. */
@@ -2278,7 +2300,7 @@ static void CALLBACK threadpool_worker_proc( void *param )
if (object->num_pending_callbacks > 1)
tp_object_prio_queue( object );
- tp_object_execute( object );
+ tp_object_execute( object, FALSE );
assert(pool->num_busy_workers);
pool->num_busy_workers--;
@@ -2418,18 +2440,13 @@ NTSTATUS WINAPI TpAllocTimer( TP_TIMER **out, PTP_TIMER_CALLBACK callback, PVOID
return STATUS_SUCCESS;
}
-/***********************************************************************
- * TpAllocWait (NTDLL.@)
- */
-NTSTATUS WINAPI TpAllocWait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID userdata,
- TP_CALLBACK_ENVIRON *environment )
+static NTSTATUS tp_alloc_wait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID userdata,
+ TP_CALLBACK_ENVIRON *environment, DWORD flags )
{
struct threadpool_object *object;
struct threadpool *pool;
NTSTATUS status;
- TRACE( "%p %p %p %p\n", out, callback, userdata, environment );
-
object = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*object) );
if (!object)
return STATUS_NO_MEMORY;
@@ -2443,6 +2460,7 @@ NTSTATUS WINAPI TpAllocWait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID us
object->type = TP_OBJECT_TYPE_WAIT;
object->u.wait.callback = callback;
+ object->u.wait.flags = flags;
status = tp_waitqueue_lock( object );
if (status)
@@ -2458,6 +2476,16 @@ NTSTATUS WINAPI TpAllocWait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID us
return STATUS_SUCCESS;
}
+/***********************************************************************
+ * TpAllocWait (NTDLL.@)
+ */
+NTSTATUS WINAPI TpAllocWait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID userdata,
+ TP_CALLBACK_ENVIRON *environment )
+{
+ TRACE( "%p %p %p %p\n", out, callback, userdata, environment );
+ return tp_alloc_wait( out, callback, userdata, environment, WT_EXECUTEONLYONCE );
+}
+
/***********************************************************************
* TpAllocWork (NTDLL.@)
*/
@@ -3143,71 +3171,10 @@ NTSTATUS WINAPI TpQueryPoolStackInformation( TP_POOL *pool, TP_POOL_STACK_INFORM
return STATUS_SUCCESS;
}
-static void delete_wait_work_item(struct wait_work_item *wait_work_item)
+static void CALLBACK rtl_wait_callback( TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result )
{
- NtClose( wait_work_item->CancelEvent );
- RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
-}
-
-static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
-{
- struct wait_work_item *wait_work_item = Arg;
- NTSTATUS status;
- BOOLEAN alertable = (wait_work_item->Flags & WT_EXECUTEINIOTHREAD) != 0;
- HANDLE handles[2] = { wait_work_item->Object, wait_work_item->CancelEvent };
- LARGE_INTEGER timeout;
- HANDLE completion_event;
-
- TRACE("\n");
-
- while (TRUE)
- {
- status = NtWaitForMultipleObjects( 2, handles, TRUE, alertable,
- get_nt_timeout( &timeout, wait_work_item->Milliseconds ) );
- if (status == STATUS_WAIT_0 || status == STATUS_TIMEOUT)
- {
- BOOLEAN TimerOrWaitFired;
-
- if (status == STATUS_WAIT_0)
- {
- TRACE( "object %p signaled, calling callback %p with context %p\n",
- wait_work_item->Object, wait_work_item->Callback,
- wait_work_item->Context );
- TimerOrWaitFired = FALSE;
- }
- else
- {
- TRACE( "wait for object %p timed out, calling callback %p with context %p\n",
- wait_work_item->Object, wait_work_item->Callback,
- wait_work_item->Context );
- TimerOrWaitFired = TRUE;
- }
- InterlockedExchange( &wait_work_item->CallbackInProgress, TRUE );
- if (wait_work_item->CompletionEvent)
- {
- TRACE( "Work has been canceled.\n" );
- break;
- }
- wait_work_item->Callback( wait_work_item->Context, TimerOrWaitFired );
- InterlockedExchange( &wait_work_item->CallbackInProgress, FALSE );
-
- if (wait_work_item->Flags & WT_EXECUTEONLYONCE)
- break;
- }
- else if (status != STATUS_USER_APC)
- break;
- }
-
-
- if (InterlockedIncrement( &wait_work_item->DeleteCount ) == 2 )
- {
- completion_event = wait_work_item->CompletionEvent;
- delete_wait_work_item( wait_work_item );
- if (completion_event && completion_event != INVALID_HANDLE_VALUE)
- NtSetEvent( completion_event, NULL );
- }
-
- return 0;
+ struct threadpool_object *object = impl_from_TP_WAIT(wait);
+ object->u.wait.rtl_callback( userdata, result != STATUS_WAIT_0 );
}
/***********************************************************************
@@ -3235,46 +3202,34 @@ static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
*|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
*|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
*/
-NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
- RTL_WAITORTIMERCALLBACKFUNC Callback,
- PVOID Context, ULONG Milliseconds, ULONG Flags)
+NTSTATUS WINAPI RtlRegisterWait( HANDLE *out, HANDLE handle, RTL_WAITORTIMERCALLBACKFUNC callback,
+ void *context, ULONG milliseconds, ULONG flags )
{
- struct wait_work_item *wait_work_item;
+ struct threadpool_object *object;
+ TP_CALLBACK_ENVIRON environment;
+ LARGE_INTEGER timeout;
NTSTATUS status;
+ TP_WAIT *wait;
- TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags );
-
- wait_work_item = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*wait_work_item) );
- if (!wait_work_item)
- return STATUS_NO_MEMORY;
+ TRACE( "out %p, handle %p, callback %p, context %p, milliseconds %u, flags %x\n",
+ out, handle, callback, context, milliseconds, flags );
- wait_work_item->Object = Object;
- wait_work_item->Callback = Callback;
- wait_work_item->Context = Context;
- wait_work_item->Milliseconds = Milliseconds;
- wait_work_item->Flags = Flags;
- wait_work_item->CallbackInProgress = FALSE;
- wait_work_item->DeleteCount = 0;
- wait_work_item->CompletionEvent = NULL;
+ memset( &environment, 0, sizeof(environment) );
+ environment.Version = 1;
+ environment.u.s.LongFunction = (flags & WT_EXECUTELONGFUNCTION) != 0;
+ environment.u.s.Persistent = (flags & WT_EXECUTEINPERSISTENTTHREAD) != 0;
- status = NtCreateEvent( &wait_work_item->CancelEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
- if (status != STATUS_SUCCESS)
- {
- RtlFreeHeap( GetProcessHeap(), 0, wait_work_item );
+ flags &= (WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD | WT_EXECUTEINIOTHREAD);
+ if ((status = tp_alloc_wait( &wait, rtl_wait_callback, context, &environment, flags )))
return status;
- }
- Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD |
- WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION);
- status = RtlQueueWorkItem( wait_thread_proc, wait_work_item, Flags );
- if (status != STATUS_SUCCESS)
- {
- delete_wait_work_item( wait_work_item );
- return status;
- }
+ object = impl_from_TP_WAIT(wait);
+ object->u.wait.rtl_callback = callback;
- *NewWaitObject = wait_work_item;
- return status;
+ TpSetWait( (TP_WAIT *)object, handle, get_nt_timeout( &timeout, milliseconds ) );
+
+ *out = object;
+ return STATUS_SUCCESS;
}
/***********************************************************************
@@ -3290,54 +3245,31 @@ NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
* Success: STATUS_SUCCESS.
* Failure: Any NTSTATUS code.
*/
-NTSTATUS WINAPI RtlDeregisterWaitEx(HANDLE WaitHandle, HANDLE CompletionEvent)
+NTSTATUS WINAPI RtlDeregisterWaitEx( HANDLE handle, HANDLE event )
{
- struct wait_work_item *wait_work_item = WaitHandle;
+ struct threadpool_object *object = handle;
NTSTATUS status;
- HANDLE LocalEvent = NULL;
- int CallbackInProgress;
- TRACE( "(%p %p)\n", WaitHandle, CompletionEvent );
+ TRACE( "handle %p, event %p\n", handle, event );
- if (WaitHandle == NULL)
- return STATUS_INVALID_HANDLE;
+ if (!object) return STATUS_INVALID_HANDLE;
- InterlockedExchangePointer( &wait_work_item->CompletionEvent, INVALID_HANDLE_VALUE );
- CallbackInProgress = wait_work_item->CallbackInProgress;
- TRACE( "callback in progress %u\n", CallbackInProgress );
- if (CompletionEvent == INVALID_HANDLE_VALUE || !CallbackInProgress)
- {
- status = NtCreateEvent( &LocalEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
- if (status != STATUS_SUCCESS)
- return status;
- InterlockedExchangePointer( &wait_work_item->CompletionEvent, LocalEvent );
- }
- else if (CompletionEvent != NULL)
- {
- InterlockedExchangePointer( &wait_work_item->CompletionEvent, CompletionEvent );
- }
+ TpSetWait( (TP_WAIT *)object, NULL, NULL );
- NtSetEvent( wait_work_item->CancelEvent, NULL );
-
- if (InterlockedIncrement( &wait_work_item->DeleteCount ) == 2 )
- {
- status = STATUS_SUCCESS;
- delete_wait_work_item( wait_work_item );
- }
- else if (LocalEvent)
- {
- TRACE( "Waiting for completion event\n" );
- NtWaitForSingleObject( LocalEvent, FALSE, NULL );
- status = STATUS_SUCCESS;
- }
+ if (event == INVALID_HANDLE_VALUE) TpWaitForWait( (TP_WAIT *)object, TRUE );
else
{
- status = STATUS_PENDING;
+ assert( object->completed_event == NULL );
+ object->completed_event = event;
}
- if (LocalEvent)
- NtClose( LocalEvent );
+ RtlEnterCriticalSection( &object->pool->cs );
+ if (object->num_pending_callbacks + object->num_running_callbacks
+ + object->num_associated_callbacks) status = STATUS_PENDING;
+ else status = STATUS_SUCCESS;
+ RtlLeaveCriticalSection( &object->pool->cs );
+ TpReleaseWait( (TP_WAIT *)object );
return status;
}
--
2.29.2
More information about the wine-devel
mailing list