diff --git a/reactos/lib/rtl/rtl.rbuild b/reactos/lib/rtl/rtl.rbuild index 51dca8e4ba7..fdd2ddbebec 100644 --- a/reactos/lib/rtl/rtl.rbuild +++ b/reactos/lib/rtl/rtl.rbuild @@ -91,6 +91,7 @@ unicodeprefix.c vectoreh.c version.c + wait.c workitem.c rtl.h diff --git a/reactos/lib/rtl/thread.c b/reactos/lib/rtl/thread.c index 9cadffaa158..e862e85323e 100644 --- a/reactos/lib/rtl/thread.c +++ b/reactos/lib/rtl/thread.c @@ -275,82 +275,4 @@ _NtCurrentTeb(VOID) } -/*********************************************************************** - * 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) -{ - return STATUS_SUCCESS; -} - -/*********************************************************************** - * 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) -{ - return STATUS_SUCCESS; -} - -/*********************************************************************** - * 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 */ diff --git a/reactos/lib/rtl/wait.c b/reactos/lib/rtl/wait.c new file mode 100644 index 00000000000..914d7e79843 --- /dev/null +++ b/reactos/lib/rtl/wait.c @@ -0,0 +1,272 @@ +/* + * 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) ? TRUE : FALSE; + HANDLE handles[2] = { Wait->Object, Wait->CancelEvent }; + LARGE_INTEGER timeout; + HANDLE completion_event; + +// TRACE("\n"); + + while (TRUE) + { + Status = NtWaitForMultipleObjects( 2, + handles, + FALSE, + alertable, + get_nt_timeout( &timeout, Wait->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->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 + 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, + TRUE, + FALSE ); + + if (Status != STATUS_SUCCESS) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, Wait ); + return Status; + } + + Status = RtlQueueWorkItem( Wait_thread_proc, + Wait, + Flags & ~WT_EXECUTEONLYONCE ); + + 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, + TRUE, + 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 */