mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 15:18:33 +00:00
276 lines
7.9 KiB
C
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 */
|