From 02343c22f662ee1df29f6b999ef790d4f6627f32 Mon Sep 17 00:00:00 2001 From: Justin Miller Date: Tue, 8 Jul 2025 12:05:53 -0700 Subject: [PATCH] [RTL] Partial revert of Wine timer code in thread pooling (#8229) * [SDK] Partial Revert of 0bf42067d28ea488e03c9955632d2a614c4262c2 It was found that the timer code from WINE doesn't work as well as ours, This is a revert from that part of the PR as it doesn't change any of the real threadpool code in all reality. Thanks to the investigation of Julenuri and Simone JIRA issue: CORE-20245 --- sdk/lib/rtl/CMakeLists.txt | 1 + sdk/lib/rtl/threadpool.c | 6 +- sdk/lib/rtl/timerqueue.c | 493 +++++++++++++++++++++++++++++++++++++ sdk/lib/rtl/wait.c | 275 +++++++++++++++++++++ 4 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 sdk/lib/rtl/wait.c diff --git a/sdk/lib/rtl/CMakeLists.txt b/sdk/lib/rtl/CMakeLists.txt index db9a3c66f84..0b4438f16cf 100644 --- a/sdk/lib/rtl/CMakeLists.txt +++ b/sdk/lib/rtl/CMakeLists.txt @@ -89,6 +89,7 @@ list(APPEND SOURCE unicodeprefix.c vectoreh.c version.c + wait.c workitem.c rtl.h) diff --git a/sdk/lib/rtl/threadpool.c b/sdk/lib/rtl/threadpool.c index efdfa454618..52aa7da5115 100644 --- a/sdk/lib/rtl/threadpool.c +++ b/sdk/lib/rtl/threadpool.c @@ -837,6 +837,8 @@ static void WINAPI timer_queue_thread_proc(LPVOID p) #endif } +#ifndef __REACTOS__ + static void queue_destroy_timer(struct queue_timer *t) { /* We MUST hold the queue cs while calling this function. */ @@ -1137,7 +1139,7 @@ NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer, return status; } - +#endif /*********************************************************************** * timerqueue_thread_proc (internal) */ @@ -3380,6 +3382,7 @@ NTSTATUS WINAPI TpQueryPoolStackInformation( TP_POOL *pool, TP_POOL_STACK_INFORM return STATUS_SUCCESS; } +#ifndef __REACTOS__ static void CALLBACK rtl_wait_callback( TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result ) { struct threadpool_object *object = impl_from_TP_WAIT(wait); @@ -3502,6 +3505,7 @@ NTSTATUS WINAPI RtlDeregisterWait(HANDLE WaitHandle) { return RtlDeregisterWaitEx(WaitHandle, NULL); } +#endif #ifdef __REACTOS__ VOID diff --git a/sdk/lib/rtl/timerqueue.c b/sdk/lib/rtl/timerqueue.c index 195059ae1ea..596588bdafd 100644 --- a/sdk/lib/rtl/timerqueue.c +++ b/sdk/lib/rtl/timerqueue.c @@ -64,6 +64,405 @@ struct timer_queue #define EXPIRE_NEVER (~(ULONGLONG) 0) #define TIMER_QUEUE_MAGIC 0x516d6954 /* TimQ */ +static void queue_remove_timer(struct queue_timer *t) +{ + /* We MUST hold the queue cs while calling this function. This ensures + that we cannot queue another callback for this timer. The runcount + being zero makes sure we don't have any already queued. */ + struct timer_queue *q = t->q; + + assert(t->runcount == 0); + assert(t->destroy); + + list_remove(&t->entry); + if (t->event) + NtSetEvent(t->event, NULL); + RtlFreeHeap(RtlGetProcessHeap(), 0, t); + + if (q->quit && list_empty(&q->timers)) + NtSetEvent(q->event, NULL); +} + +static void timer_cleanup_callback(struct queue_timer *t) +{ + struct timer_queue *q = t->q; + RtlEnterCriticalSection(&q->cs); + + assert(0 < t->runcount); + --t->runcount; + + if (t->destroy && t->runcount == 0) + queue_remove_timer(t); + + RtlLeaveCriticalSection(&q->cs); +} + +static VOID WINAPI timer_callback_wrapper(LPVOID p) +{ + struct queue_timer *t = p; + t->callback(t->param, TRUE); + timer_cleanup_callback(t); +} + +static inline ULONGLONG queue_current_time(void) +{ + LARGE_INTEGER now, freq; + NtQueryPerformanceCounter(&now, &freq); + return now.QuadPart * 1000 / freq.QuadPart; +} + +static void queue_add_timer(struct queue_timer *t, ULONGLONG time, + BOOL set_event) +{ + /* We MUST hold the queue cs while calling this function. */ + struct timer_queue *q = t->q; + struct list *ptr = &q->timers; + + assert(!q->quit || (t->destroy && time == EXPIRE_NEVER)); + + if (time != EXPIRE_NEVER) + LIST_FOR_EACH(ptr, &q->timers) + { + struct queue_timer *cur = LIST_ENTRY(ptr, struct queue_timer, entry); + if (time < cur->expire) + break; + } + list_add_before(ptr, &t->entry); + + t->expire = time; + + /* If we insert at the head of the list, we need to expire sooner + than expected. */ + if (set_event && &t->entry == list_head(&q->timers)) + NtSetEvent(q->event, NULL); +} + +static inline void queue_move_timer(struct queue_timer *t, ULONGLONG time, + BOOL set_event) +{ + /* We MUST hold the queue cs while calling this function. */ + list_remove(&t->entry); + queue_add_timer(t, time, set_event); +} + +static void queue_timer_expire(struct timer_queue *q) +{ + struct queue_timer *t = NULL; + + RtlEnterCriticalSection(&q->cs); + if (list_head(&q->timers)) + { + ULONGLONG now, next; + t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry); + if (!t->destroy && t->expire <= ((now = queue_current_time()))) + { + ++t->runcount; + if (t->period) + { + next = t->expire + t->period; + /* avoid trigger cascade if overloaded / hibernated */ + if (next < now) + next = now + t->period; + } + else + next = EXPIRE_NEVER; + queue_move_timer(t, next, FALSE); + } + else + t = NULL; + } + RtlLeaveCriticalSection(&q->cs); + + if (t) + { + if (t->flags & WT_EXECUTEINTIMERTHREAD) + timer_callback_wrapper(t); + else + { + ULONG flags + = (t->flags + & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD + | WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION)); + NTSTATUS status = RtlQueueWorkItem(timer_callback_wrapper, t, flags); + if (status != STATUS_SUCCESS) + timer_cleanup_callback(t); + } + } +} + +static ULONG queue_get_timeout(struct timer_queue *q) +{ + struct queue_timer *t; + ULONG timeout = INFINITE; + + RtlEnterCriticalSection(&q->cs); + if (list_head(&q->timers)) + { + t = LIST_ENTRY(list_head(&q->timers), struct queue_timer, entry); + assert(!t->destroy || t->expire == EXPIRE_NEVER); + + if (t->expire != EXPIRE_NEVER) + { + ULONGLONG time = queue_current_time(); + timeout = t->expire < time ? 0 : (ULONG)(t->expire - time); + } + } + RtlLeaveCriticalSection(&q->cs); + + return timeout; +} + +static DWORD WINAPI timer_queue_thread_proc(LPVOID p) +{ + struct timer_queue *q = p; + ULONG timeout_ms; + + timeout_ms = INFINITE; + for (;;) + { + LARGE_INTEGER timeout; + NTSTATUS status; + BOOL done = FALSE; + + status = NtWaitForSingleObject( + q->event, FALSE, get_nt_timeout(&timeout, timeout_ms)); + + if (status == STATUS_WAIT_0) + { + /* There are two possible ways to trigger the event. Either + we are quitting and the last timer got removed, or a new + timer got put at the head of the list so we need to adjust + our timeout. */ + RtlEnterCriticalSection(&q->cs); + if (q->quit && list_empty(&q->timers)) + done = TRUE; + RtlLeaveCriticalSection(&q->cs); + } + else if (status == STATUS_TIMEOUT) + queue_timer_expire(q); + + if (done) + break; + + timeout_ms = queue_get_timeout(q); + } + + NtClose(q->event); + RtlDeleteCriticalSection(&q->cs); + q->magic = 0; + RtlFreeHeap(RtlGetProcessHeap(), 0, q); + RtlpExitThreadFunc(STATUS_SUCCESS); + return 0; +} + +static void queue_destroy_timer(struct queue_timer *t) +{ + /* We MUST hold the queue cs while calling this function. */ + t->destroy = TRUE; + if (t->runcount == 0) + /* Ensure a timer is promptly removed. If callbacks are pending, + it will be removed after the last one finishes by the callback + cleanup wrapper. */ + queue_remove_timer(t); + else + /* Make sure no destroyed timer masks an active timer at the head + of the sorted list. */ + queue_move_timer(t, EXPIRE_NEVER, FALSE); +} + +/*********************************************************************** + * RtlCreateTimerQueue (NTDLL.@) + * + * Creates a timer queue object and returns a handle to it. + * + * PARAMS + * NewTimerQueue [O] The newly created queue. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + */ +NTSTATUS WINAPI RtlCreateTimerQueue(PHANDLE NewTimerQueue) +{ + NTSTATUS status; + struct timer_queue *q = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *q); + if (!q) + return STATUS_NO_MEMORY; + + RtlInitializeCriticalSection(&q->cs); + list_init(&q->timers); + q->quit = FALSE; + q->magic = TIMER_QUEUE_MAGIC; + status = NtCreateEvent(&q->event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE); + if (status != STATUS_SUCCESS) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, q); + return status; + } + status = RtlpStartThreadFunc(timer_queue_thread_proc, q, &q->thread); + if (status != STATUS_SUCCESS) + { + NtClose(q->event); + RtlFreeHeap(RtlGetProcessHeap(), 0, q); + return status; + } + + NtResumeThread(q->thread, NULL); + *NewTimerQueue = q; + return STATUS_SUCCESS; +} + +/*********************************************************************** + * RtlDeleteTimerQueueEx (NTDLL.@) + * + * Deletes a timer queue object. + * + * PARAMS + * TimerQueue [I] The timer queue to destroy. + * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE, + * wait until all timers are finished firing before + * returning. Otherwise, return immediately and set the + * event when all timers are done. + * + * RETURNS + * Success: STATUS_SUCCESS if synchronous, STATUS_PENDING if not. + * Failure: Any NTSTATUS code. + */ +NTSTATUS WINAPI RtlDeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) +{ + struct timer_queue *q = TimerQueue; + struct queue_timer *t, *temp; + HANDLE thread; + NTSTATUS status; + + if (!q || q->magic != TIMER_QUEUE_MAGIC) + return STATUS_INVALID_HANDLE; + + thread = q->thread; + + RtlEnterCriticalSection(&q->cs); + q->quit = TRUE; + if (list_head(&q->timers)) + /* When the last timer is removed, it will signal the timer thread to + exit... */ + LIST_FOR_EACH_ENTRY_SAFE(t, temp, &q->timers, struct queue_timer, entry) + queue_destroy_timer(t); + else + /* However if we have none, we must do it ourselves. */ + NtSetEvent(q->event, NULL); + RtlLeaveCriticalSection(&q->cs); + + if (CompletionEvent == INVALID_HANDLE_VALUE) + { + NtWaitForSingleObject(thread, FALSE, NULL); + status = STATUS_SUCCESS; + } + else + { + if (CompletionEvent) + { + DPRINT1("asynchronous return on completion event unimplemented\n"); + NtWaitForSingleObject(thread, FALSE, NULL); + NtSetEvent(CompletionEvent, NULL); + } + status = STATUS_PENDING; + } + + NtClose(thread); + return status; +} + +static struct timer_queue *get_timer_queue(HANDLE TimerQueue) +{ + static struct timer_queue *default_timer_queue; + + if (TimerQueue) + return TimerQueue; + else + { + if (!default_timer_queue) + { + HANDLE q; + NTSTATUS status = RtlCreateTimerQueue(&q); + if (status == STATUS_SUCCESS) + { + PVOID p = InterlockedCompareExchangePointer( + (void **) &default_timer_queue, q, NULL); + if (p) + /* Got beat to the punch. */ + RtlDeleteTimerQueueEx(q, NULL); + } + } + return default_timer_queue; + } +} + +/*********************************************************************** + * RtlCreateTimer (NTDLL.@) + * + * Creates a new timer associated with the given queue. + * + * PARAMS + * NewTimer [O] The newly created timer. + * TimerQueue [I] The queue to hold the timer. + * Callback [I] The callback to fire. + * Parameter [I] The argument for the callback. + * DueTime [I] The delay, in milliseconds, before first firing the + * timer. + * Period [I] The period, in milliseconds, at which to fire the timer + * after the first callback. If zero, the timer will only + * fire once. It still needs to be deleted with + * RtlDeleteTimer. + * Flags [I] Flags controlling the execution of the callback. In + * addition to the WT_* thread pool flags (see + * RtlQueueWorkItem), WT_EXECUTEINTIMERTHREAD and + * WT_EXECUTEONLYONCE are supported. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + */ +NTSTATUS WINAPI RtlCreateTimer(HANDLE TimerQueue, PHANDLE NewTimer, + WAITORTIMERCALLBACKFUNC Callback, + PVOID Parameter, DWORD DueTime, DWORD Period, + ULONG Flags) +{ + NTSTATUS status; + struct queue_timer *t; + struct timer_queue *q = get_timer_queue(TimerQueue); + + if (!q) return STATUS_NO_MEMORY; + if (q->magic != TIMER_QUEUE_MAGIC) return STATUS_INVALID_HANDLE; + + t = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof *t); + if (!t) + return STATUS_NO_MEMORY; + + t->q = q; + t->runcount = 0; + t->callback = Callback; + t->param = Parameter; + t->period = Period; + t->flags = Flags; + t->destroy = FALSE; + t->event = NULL; + + status = STATUS_SUCCESS; + RtlEnterCriticalSection(&q->cs); + if (q->quit) + status = STATUS_INVALID_HANDLE; + else + queue_add_timer(t, queue_current_time() + DueTime, TRUE); + RtlLeaveCriticalSection(&q->cs); + + if (status == STATUS_SUCCESS) + *NewTimer = t; + else + RtlFreeHeap(RtlGetProcessHeap(), 0, t); + + return status; +} + NTSTATUS WINAPI RtlSetTimer( @@ -84,6 +483,100 @@ RtlSetTimer( Flags); } +/*********************************************************************** + * RtlUpdateTimer (NTDLL.@) + * + * Changes the time at which a timer expires. + * + * PARAMS + * TimerQueue [I] The queue that holds the timer. + * Timer [I] The timer to update. + * DueTime [I] The delay, in milliseconds, before next firing the timer. + * Period [I] The period, in milliseconds, at which to fire the timer + * after the first callback. If zero, the timer will not + * refire once. It still needs to be deleted with + * RtlDeleteTimer. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + */ +NTSTATUS WINAPI RtlUpdateTimer(HANDLE TimerQueue, HANDLE Timer, + DWORD DueTime, DWORD Period) +{ + struct queue_timer *t = Timer; + struct timer_queue *q = t->q; + + RtlEnterCriticalSection(&q->cs); + /* Can't change a timer if it was once-only or destroyed. */ + if (t->expire != EXPIRE_NEVER) + { + t->period = Period; + queue_move_timer(t, queue_current_time() + DueTime, TRUE); + } + RtlLeaveCriticalSection(&q->cs); + + return STATUS_SUCCESS; +} + +/*********************************************************************** + * RtlDeleteTimer (NTDLL.@) + * + * Cancels a timer-queue timer. + * + * PARAMS + * TimerQueue [I] The queue that holds the timer. + * Timer [I] The timer to update. + * CompletionEvent [I] If NULL, return immediately. If INVALID_HANDLE_VALUE, + * wait until the timer is finished firing all pending + * callbacks before returning. Otherwise, return + * immediately and set the timer is done. + * + * RETURNS + * Success: STATUS_SUCCESS if the timer is done, STATUS_PENDING if not, + or if the completion event is NULL. + * Failure: Any NTSTATUS code. + */ +NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer, + HANDLE CompletionEvent) +{ + struct queue_timer *t = Timer; + struct timer_queue *q; + NTSTATUS status = STATUS_PENDING; + HANDLE event = NULL; + + if (!Timer) + return STATUS_INVALID_PARAMETER_1; + q = t->q; + if (CompletionEvent == INVALID_HANDLE_VALUE) + { + status = NtCreateEvent(&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE); + if (status == STATUS_SUCCESS) + status = STATUS_PENDING; + } + else if (CompletionEvent) + event = CompletionEvent; + + RtlEnterCriticalSection(&q->cs); + t->event = event; + if (t->runcount == 0 && event) + status = STATUS_SUCCESS; + queue_destroy_timer(t); + RtlLeaveCriticalSection(&q->cs); + + if (CompletionEvent == INVALID_HANDLE_VALUE && event) + { + if (status == STATUS_PENDING) + { + NtWaitForSingleObject(event, FALSE, NULL); + status = STATUS_SUCCESS; + } + NtClose(event); + } + + return status; +} + /* * @implemented */ diff --git a/sdk/lib/rtl/wait.c b/sdk/lib/rtl/wait.c new file mode 100644 index 00000000000..bab26365d5c --- /dev/null +++ b/sdk/lib/rtl/wait.c @@ -0,0 +1,275 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * PURPOSE: Rtl user wait functions + * FILE: lib/rtl/wait.c + * PROGRAMERS: + * Alex Ionescu (alex@relsoft.net) + * Eric Kohl + * KJK::Hyperion + */ + +/* INCLUDES *****************************************************************/ + +#include + +#define NDEBUG +#include + +typedef struct _RTLP_WAIT +{ + HANDLE Object; + BOOLEAN CallbackInProgress; + HANDLE CancelEvent; + LONG DeleteCount; + HANDLE CompletionEvent; + ULONG Flags; + WAITORTIMERCALLBACKFUNC Callback; + PVOID Context; + ULONG Milliseconds; +} RTLP_WAIT, *PRTLP_WAIT; + +/* PRIVATE FUNCTIONS *******************************************************/ + +static inline PLARGE_INTEGER get_nt_timeout( PLARGE_INTEGER pTime, ULONG timeout ) +{ + if (timeout == INFINITE) return NULL; + pTime->QuadPart = (ULONGLONG)timeout * -10000; + return pTime; +} + +static VOID +NTAPI +Wait_thread_proc(LPVOID Arg) +{ + PRTLP_WAIT Wait = (PRTLP_WAIT) Arg; + NTSTATUS Status; + BOOLEAN alertable = (Wait->Flags & WT_EXECUTEINIOTHREAD) != 0; + HANDLE handles[2] = { Wait->CancelEvent, Wait->Object }; + LARGE_INTEGER timeout; + HANDLE completion_event; + +// TRACE("\n"); + + while (TRUE) + { + Status = NtWaitForMultipleObjects( 2, + handles, + WaitAny, + alertable, + get_nt_timeout( &timeout, Wait->Milliseconds ) ); + + if (Status == STATUS_WAIT_1 || Status == STATUS_TIMEOUT) + { + BOOLEAN TimerOrWaitFired; + + if (Status == STATUS_WAIT_1) + { + // TRACE( "object %p signaled, calling callback %p with context %p\n", + // Wait->Object, Wait->Callback, + // Wait->Context ); + TimerOrWaitFired = FALSE; + } + else + { + // TRACE( "wait for object %p timed out, calling callback %p with context %p\n", + // Wait->Object, Wait->Callback, + // Wait->Context ); + TimerOrWaitFired = TRUE; + } + Wait->CallbackInProgress = TRUE; + Wait->Callback( Wait->Context, TimerOrWaitFired ); + Wait->CallbackInProgress = FALSE; + + if (Wait->Flags & WT_EXECUTEONLYONCE) + break; + } + else if (Status != STATUS_USER_APC) + break; + } + + completion_event = Wait->CompletionEvent; + if (completion_event) NtSetEvent( completion_event, NULL ); + + if (InterlockedIncrement( &Wait->DeleteCount ) == 2 ) + { + NtClose( Wait->CancelEvent ); + RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); + } +} + + +/* FUNCTIONS ***************************************************************/ + + +/*********************************************************************** + * RtlRegisterWait + * + * Registers a wait for a handle to become signaled. + * + * PARAMS + * NewWaitObject [I] Handle to the new wait object. Use RtlDeregisterWait() to free it. + * Object [I] Object to wait to become signaled. + * Callback [I] Callback function to execute when the wait times out or the handle is signaled. + * Context [I] Context to pass to the callback function when it is executed. + * Milliseconds [I] Number of milliseconds to wait before timing out. + * Flags [I] Flags. See notes. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + * + * NOTES + * Flags can be one or more of the following: + *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread. + *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread. + *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent. + *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time. + *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token. + */ +NTSTATUS +NTAPI +RtlRegisterWait(PHANDLE NewWaitObject, + HANDLE Object, + WAITORTIMERCALLBACKFUNC Callback, + PVOID Context, + ULONG Milliseconds, + ULONG Flags) +{ + PRTLP_WAIT Wait; + NTSTATUS Status; + + //TRACE( "(%p, %p, %p, %p, %d, 0x%x)\n", NewWaitObject, Object, Callback, Context, Milliseconds, Flags ); + + Wait = RtlAllocateHeap( RtlGetProcessHeap(), 0, sizeof(RTLP_WAIT) ); + if (!Wait) + return STATUS_NO_MEMORY; + + Wait->Object = Object; + Wait->Callback = Callback; + Wait->Context = Context; + Wait->Milliseconds = Milliseconds; + Wait->Flags = Flags; + Wait->CallbackInProgress = FALSE; + Wait->DeleteCount = 0; + Wait->CompletionEvent = NULL; + + Status = NtCreateEvent( &Wait->CancelEvent, + EVENT_ALL_ACCESS, + NULL, + NotificationEvent, + FALSE ); + + if (Status != STATUS_SUCCESS) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); + return Status; + } + + Flags = Flags & (WT_EXECUTEINIOTHREAD | WT_EXECUTEINPERSISTENTTHREAD | + WT_EXECUTELONGFUNCTION | WT_TRANSFER_IMPERSONATION); + + Status = RtlQueueWorkItem( Wait_thread_proc, + Wait, + Flags ); + + if (Status != STATUS_SUCCESS) + { + NtClose( Wait->CancelEvent ); + RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); + return Status; + } + + *NewWaitObject = Wait; + return Status; +} + +/*********************************************************************** + * RtlDeregisterWaitEx + * + * Cancels a wait operation and frees the resources associated with calling + * RtlRegisterWait(). + * + * PARAMS + * WaitObject [I] Handle to the wait object to free. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + */ +NTSTATUS +NTAPI +RtlDeregisterWaitEx(HANDLE WaitHandle, + HANDLE CompletionEvent) +{ + PRTLP_WAIT Wait = (PRTLP_WAIT) WaitHandle; + NTSTATUS Status = STATUS_SUCCESS; + + //TRACE( "(%p)\n", WaitHandle ); + + NtSetEvent( Wait->CancelEvent, NULL ); + if (Wait->CallbackInProgress) + { + if (CompletionEvent != NULL) + { + if (CompletionEvent == INVALID_HANDLE_VALUE) + { + Status = NtCreateEvent( &CompletionEvent, + EVENT_ALL_ACCESS, + NULL, + NotificationEvent, + FALSE ); + + if (Status != STATUS_SUCCESS) + return Status; + + (void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent ); + + if (Wait->CallbackInProgress) + NtWaitForSingleObject( CompletionEvent, FALSE, NULL ); + + NtClose( CompletionEvent ); + } + else + { + (void)InterlockedExchangePointer( &Wait->CompletionEvent, CompletionEvent ); + + if (Wait->CallbackInProgress) + Status = STATUS_PENDING; + } + } + else + Status = STATUS_PENDING; + } + + if (InterlockedIncrement( &Wait->DeleteCount ) == 2 ) + { + Status = STATUS_SUCCESS; + NtClose( Wait->CancelEvent ); + RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); + } + + return Status; +} + +/*********************************************************************** + * RtlDeregisterWait + * + * Cancels a wait operation and frees the resources associated with calling + * RtlRegisterWait(). + * + * PARAMS + * WaitObject [I] Handle to the wait object to free. + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any NTSTATUS code. + */ +NTSTATUS +NTAPI +RtlDeregisterWait(HANDLE WaitHandle) +{ + return RtlDeregisterWaitEx(WaitHandle, NULL); +} + +/* EOF */