reactos/sdk/lib/rtl/wait.c
2021-06-11 15:33:08 +03:00

276 lines
7.9 KiB
C

/*
* 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 <rtl.h>
#define NDEBUG
#include <debug.h>
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 */