/* * 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 */