reactos/dll/win32/kernel32/thread/thread.c
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

1074 lines
25 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: lib/kernel32/thread/thread.c
* PURPOSE: Thread functions
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* Ariadne ( ariadne@xs4all.nl)
*
*/
/* INCLUDES ******************************************************************/
#include <k32.h>
#define NDEBUG
#include <debug.h>
/* FIXME: NDK */
#define HIGH_PRIORITY 31
#define SXS_SUPPORT_FIXME
NTSTATUS
WINAPI
BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
IN PCLIENT_ID ClientId);
/* FUNCTIONS *****************************************************************/
static
LONG BaseThreadExceptionFilter(EXCEPTION_POINTERS * ExceptionInfo)
{
LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
if (GlobalTopLevelExceptionFilter != NULL)
{
_SEH2_TRY
{
ExceptionDisposition = GlobalTopLevelExceptionFilter(ExceptionInfo);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
}
_SEH2_END;
}
return ExceptionDisposition;
}
__declspec(noreturn)
VOID
WINAPI
BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter)
{
volatile UINT uExitCode = 0;
/* Attempt to call the Thread Start Address */
_SEH2_TRY
{
/* Get the exit code from the Thread Start */
uExitCode = (lpStartAddress)((PVOID)lpParameter);
}
_SEH2_EXCEPT(BaseThreadExceptionFilter(_SEH2_GetExceptionInformation()))
{
/* Get the Exit code from the SEH Handler */
uExitCode = _SEH2_GetExceptionCode();
} _SEH2_END;
/* Exit the Thread */
ExitThread(uExitCode);
}
/*
* @implemented
*/
HANDLE
WINAPI
CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId)
{
/* Act as if we're going to create a remote thread in ourselves */
return CreateRemoteThread(NtCurrentProcess(),
lpThreadAttributes,
dwStackSize,
lpStartAddress,
lpParameter,
dwCreationFlags,
lpThreadId);
}
/*
* @implemented
*/
HANDLE
WINAPI
CreateRemoteThread(HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId)
{
NTSTATUS Status;
INITIAL_TEB InitialTeb;
CONTEXT Context;
CLIENT_ID ClientId;
OBJECT_ATTRIBUTES LocalObjectAttributes;
POBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hThread;
ULONG Dummy;
DPRINT("CreateRemoteThread: hProcess: %ld dwStackSize: %ld lpStartAddress"
": %p lpParameter: %lx, dwCreationFlags: %lx\n", hProcess,
dwStackSize, lpStartAddress, lpParameter, dwCreationFlags);
/* Clear the Context */
RtlZeroMemory(&Context, sizeof(CONTEXT));
/* Write PID */
ClientId.UniqueProcess = hProcess;
/* Create the Stack */
Status = BasepCreateStack(hProcess,
dwStackSize,
dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ?
dwStackSize : 0,
&InitialTeb);
if(!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return NULL;
}
/* Create Initial Context */
BasepInitializeContext(&Context,
lpParameter,
lpStartAddress,
InitialTeb.StackBase,
1);
/* initialize the attributes for the thread object */
ObjectAttributes = BasepConvertObjectAttributes(&LocalObjectAttributes,
lpThreadAttributes,
NULL);
/* Create the Kernel Thread Object */
Status = NtCreateThread(&hThread,
THREAD_ALL_ACCESS,
ObjectAttributes,
hProcess,
&ClientId,
&Context,
&InitialTeb,
TRUE);
if(!NT_SUCCESS(Status))
{
BasepFreeStack(hProcess, &InitialTeb);
SetLastErrorByStatus(Status);
return NULL;
}
/* Are we in the same process? */
if (hProcess == NtCurrentProcess())
{
PTEB Teb;
PVOID ActivationContextStack;
THREAD_BASIC_INFORMATION ThreadBasicInfo;
#ifndef SXS_SUPPORT_FIXME
ACTIVATION_CONTEXT_BASIC_INFORMATION ActivationCtxInfo;
ULONG_PTR Cookie;
#endif
ULONG retLen;
/* Get the TEB */
Status = NtQueryInformationThread(hThread,
ThreadBasicInformation,
&ThreadBasicInfo,
sizeof(ThreadBasicInfo),
&retLen);
if (NT_SUCCESS(Status))
{
/* Allocate the Activation Context Stack */
Status = RtlAllocateActivationContextStack(&ActivationContextStack);
}
if (NT_SUCCESS(Status))
{
Teb = ThreadBasicInfo.TebBaseAddress;
/* Save it */
Teb->ActivationContextStackPointer = ActivationContextStack;
#ifndef SXS_SUPPORT_FIXME
/* Query the Context */
Status = RtlQueryInformationActivationContext(1,
0,
NULL,
ActivationContextBasicInformation,
&ActivationCtxInfo,
sizeof(ActivationCtxInfo),
&retLen);
if (NT_SUCCESS(Status))
{
/* Does it need to be activated? */
if (!ActivationCtxInfo.hActCtx)
{
/* Activate it */
Status = RtlActivateActivationContext(1,
ActivationCtxInfo.hActCtx,
&Cookie);
if (!NT_SUCCESS(Status))
DPRINT1("RtlActivateActivationContext failed %x\n", Status);
}
}
else
DPRINT1("RtlQueryInformationActivationContext failed %x\n", Status);
#endif
}
else
DPRINT1("RtlAllocateActivationContextStack failed %x\n", Status);
}
/* Notify CSR */
Status = BasepNotifyCsrOfThread(hThread, &ClientId);
if (!NT_SUCCESS(Status))
{
ASSERT(FALSE);
}
/* Success */
if(lpThreadId) *lpThreadId = HandleToUlong(ClientId.UniqueThread);
/* Resume it if asked */
if (!(dwCreationFlags & CREATE_SUSPENDED))
{
NtResumeThread(hThread, &Dummy);
}
/* Return handle to thread */
return hThread;
}
/*
* @implemented
*/
VOID
WINAPI
ExitThread(DWORD uExitCode)
{
NTSTATUS Status;
ULONG LastThread;
/*
* Terminate process if this is the last thread
* of the current process
*/
Status = NtQueryInformationThread(NtCurrentThread(),
ThreadAmILastThread,
&LastThread,
sizeof(LastThread),
NULL);
if (NT_SUCCESS(Status) && LastThread)
{
/* Exit the Process */
ExitProcess(uExitCode);
}
/* Notify DLLs and TLS Callbacks of termination */
LdrShutdownThread();
/* Tell the Kernel to free the Stack */
NtCurrentTeb()->FreeStackOnTermination = TRUE;
NtTerminateThread(NULL, uExitCode);
/* We should never reach this place */
DPRINT1("It should not happen\n");
while (TRUE) ;
}
/*
* @implemented
*/
HANDLE
WINAPI
OpenThread(DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadId)
{
NTSTATUS Status;
HANDLE ThreadHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientId ;
ClientId.UniqueProcess = 0;
ClientId.UniqueThread = ULongToHandle(dwThreadId);
InitializeObjectAttributes(&ObjectAttributes,
NULL,
(bInheritHandle ? OBJ_INHERIT : 0),
NULL,
NULL);
Status = NtOpenThread(&ThreadHandle,
dwDesiredAccess,
&ObjectAttributes,
&ClientId);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return NULL;
}
return ThreadHandle;
}
/*
* @implemented
*/
PTEB
GetTeb(VOID)
{
return NtCurrentTeb();
}
/*
* @implemented
*/
BOOL
WINAPI
SwitchToThread(VOID)
{
return NtYieldExecution() != STATUS_NO_YIELD_PERFORMED;
}
/*
* @implemented
*/
DWORD
WINAPI
GetCurrentThreadId(VOID)
{
return HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
}
/*
* @implemented
*/
BOOL
NTAPI
GetThreadTimes(HANDLE hThread,
LPFILETIME lpCreationTime,
LPFILETIME lpExitTime,
LPFILETIME lpKernelTime,
LPFILETIME lpUserTime)
{
KERNEL_USER_TIMES KernelUserTimes;
NTSTATUS Status;
Status = NtQueryInformationThread(hThread,
ThreadTimes,
&KernelUserTimes,
sizeof(KERNEL_USER_TIMES),
NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(FALSE);
}
*lpCreationTime = *(LPFILETIME)&KernelUserTimes.CreateTime;
*lpExitTime = *(LPFILETIME)&KernelUserTimes.ExitTime;
*lpKernelTime = *(LPFILETIME)&KernelUserTimes.KernelTime;
*lpUserTime = *(LPFILETIME)&KernelUserTimes.UserTime;
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
GetThreadContext(HANDLE hThread,
LPCONTEXT lpContext)
{
NTSTATUS Status;
Status = NtGetContextThread(hThread, lpContext);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
SetThreadContext(HANDLE hThread,
CONST CONTEXT *lpContext)
{
NTSTATUS Status;
Status = NtSetContextThread(hThread, (PCONTEXT)lpContext);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
GetExitCodeThread(HANDLE hThread,
LPDWORD lpExitCode)
{
THREAD_BASIC_INFORMATION ThreadBasic;
NTSTATUS Status;
Status = NtQueryInformationThread(hThread,
ThreadBasicInformation,
&ThreadBasic,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(FALSE);
}
*lpExitCode = ThreadBasic.ExitStatus;
return TRUE;
}
/*
* @implemented
*/
DWORD
WINAPI
ResumeThread(HANDLE hThread)
{
ULONG PreviousResumeCount;
NTSTATUS Status;
Status = NtResumeThread(hThread, &PreviousResumeCount);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return -1;
}
return PreviousResumeCount;
}
/*
* @implemented
*/
BOOL
WINAPI
TerminateThread(HANDLE hThread,
DWORD dwExitCode)
{
NTSTATUS Status;
if (!hThread)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Status = NtTerminateThread(hThread, dwExitCode);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
DWORD
WINAPI
SuspendThread(HANDLE hThread)
{
ULONG PreviousSuspendCount;
NTSTATUS Status;
Status = NtSuspendThread(hThread, &PreviousSuspendCount);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return -1;
}
return PreviousSuspendCount;
}
/*
* @implemented
*/
DWORD_PTR
WINAPI
SetThreadAffinityMask(HANDLE hThread,
DWORD_PTR dwThreadAffinityMask)
{
THREAD_BASIC_INFORMATION ThreadBasic;
KAFFINITY AffinityMask;
NTSTATUS Status;
AffinityMask = (KAFFINITY)dwThreadAffinityMask;
Status = NtQueryInformationThread(hThread,
ThreadBasicInformation,
&ThreadBasic,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return 0;
}
Status = NtSetInformationThread(hThread,
ThreadAffinityMask,
&AffinityMask,
sizeof(KAFFINITY));
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
ThreadBasic.AffinityMask = 0;
}
return ThreadBasic.AffinityMask;
}
/*
* @implemented
*/
BOOL
WINAPI
SetThreadPriority(HANDLE hThread,
int nPriority)
{
LONG Prio = nPriority;
NTSTATUS Status;
/* Check if values forcing saturation should be used */
if (Prio == THREAD_PRIORITY_TIME_CRITICAL)
{
Prio = (HIGH_PRIORITY + 1) / 2;
}
else if (Prio == THREAD_PRIORITY_IDLE)
{
Prio = -((HIGH_PRIORITY + 1) / 2);
}
/* Set the Base Priority */
Status = NtSetInformationThread(hThread,
ThreadBasePriority,
&Prio,
sizeof(LONG));
if (!NT_SUCCESS(Status))
{
/* Failure */
SetLastErrorByStatus(Status);
return FALSE;
}
/* Return */
return TRUE;
}
/*
* @implemented
*/
int
WINAPI
GetThreadPriority(HANDLE hThread)
{
THREAD_BASIC_INFORMATION ThreadBasic;
NTSTATUS Status;
/* Query the Base Priority Increment */
Status = NtQueryInformationThread(hThread,
ThreadBasicInformation,
&ThreadBasic,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
if (!NT_SUCCESS(Status))
{
/* Failure */
SetLastErrorByStatus(Status);
return THREAD_PRIORITY_ERROR_RETURN;
}
/* Do some conversions for out of boundary values */
if (ThreadBasic.BasePriority > THREAD_BASE_PRIORITY_MAX)
{
ThreadBasic.BasePriority = THREAD_PRIORITY_TIME_CRITICAL;
}
else if (ThreadBasic.BasePriority < THREAD_BASE_PRIORITY_MIN)
{
ThreadBasic.BasePriority = THREAD_PRIORITY_IDLE;
}
/* Return the final result */
return ThreadBasic.BasePriority;
}
/*
* @implemented
*/
BOOL
WINAPI
GetThreadPriorityBoost(IN HANDLE hThread,
OUT PBOOL pDisablePriorityBoost)
{
ULONG PriorityBoost;
NTSTATUS Status;
Status = NtQueryInformationThread(hThread,
ThreadPriorityBoost,
&PriorityBoost,
sizeof(ULONG),
NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
*pDisablePriorityBoost = PriorityBoost;
return TRUE;
}
/*
* @implemented
*/
BOOL
NTAPI
SetThreadPriorityBoost(IN HANDLE hThread,
IN BOOL bDisablePriorityBoost)
{
ULONG PriorityBoost;
NTSTATUS Status;
PriorityBoost = (ULONG)bDisablePriorityBoost;
Status = NtSetInformationThread(hThread,
ThreadPriorityBoost,
&PriorityBoost,
sizeof(ULONG));
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
GetThreadSelectorEntry(IN HANDLE hThread,
IN DWORD dwSelector,
OUT LPLDT_ENTRY lpSelectorEntry)
{
#ifdef _M_IX86
DESCRIPTOR_TABLE_ENTRY DescriptionTableEntry;
NTSTATUS Status;
/* Set the selector and do the query */
DescriptionTableEntry.Selector = dwSelector;
Status = NtQueryInformationThread(hThread,
ThreadDescriptorTableEntry,
&DescriptionTableEntry,
sizeof(DESCRIPTOR_TABLE_ENTRY),
NULL);
if (!NT_SUCCESS(Status))
{
/* Fail */
SetLastErrorByStatus(Status);
return FALSE;
}
/* Success, return the selector */
*lpSelectorEntry = DescriptionTableEntry.Descriptor;
return TRUE;
#else
DPRINT1("Calling GetThreadSelectorEntry!\n");
return FALSE;
#endif
}
/*
* @implemented
*/
DWORD
WINAPI
SetThreadIdealProcessor(HANDLE hThread,
DWORD dwIdealProcessor)
{
NTSTATUS Status;
Status = NtSetInformationThread(hThread,
ThreadIdealProcessor,
&dwIdealProcessor,
sizeof(ULONG));
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return -1;
}
return (DWORD)Status;
}
/*
* @implemented
*/
DWORD WINAPI
GetProcessIdOfThread(HANDLE Thread)
{
THREAD_BASIC_INFORMATION ThreadBasic;
NTSTATUS Status;
Status = NtQueryInformationThread(Thread,
ThreadBasicInformation,
&ThreadBasic,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
if(!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return 0;
}
return HandleToUlong(ThreadBasic.ClientId.UniqueProcess);
}
/*
* @implemented
*/
DWORD WINAPI
GetThreadId(HANDLE Thread)
{
THREAD_BASIC_INFORMATION ThreadBasic;
NTSTATUS Status;
Status = NtQueryInformationThread(Thread,
ThreadBasicInformation,
&ThreadBasic,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
if(!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return 0;
}
return HandleToUlong(ThreadBasic.ClientId.UniqueThread);
}
/*
* @unimplemented
*/
LANGID WINAPI
SetThreadUILanguage(LANGID LangId)
{
DPRINT1("SetThreadUILanguage(0x%4x) unimplemented!\n", LangId);
return LangId;
}
static void CALLBACK
IntCallUserApc(PVOID Function, PVOID dwData, PVOID Argument3)
{
PAPCFUNC pfnAPC = (PAPCFUNC)Function;
pfnAPC((ULONG_PTR)dwData);
}
/*
* @implemented
*/
DWORD WINAPI
QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
{
NTSTATUS Status;
Status = NtQueueApcThread(hThread, IntCallUserApc, pfnAPC,
(PVOID)dwData, NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return 0;
}
return 1;
}
/*
* @implemented
*/
BOOL WINAPI
GetThreadIOPendingFlag(HANDLE hThread,
PBOOL lpIOIsPending)
{
ULONG IoPending;
NTSTATUS Status;
if(lpIOIsPending == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
Status = NtQueryInformationThread(hThread,
ThreadIsIoPending,
(PVOID)&IoPending,
sizeof(IoPending),
NULL);
if(NT_SUCCESS(Status))
{
*lpIOIsPending = ((IoPending != 0) ? TRUE : FALSE);
return TRUE;
}
SetLastErrorByStatus(Status);
return FALSE;
}
/*
* @implemented
*/
VOID WINAPI
Sleep(DWORD dwMilliseconds)
{
SleepEx(dwMilliseconds, FALSE);
return;
}
/*
* @implemented
*/
DWORD WINAPI
SleepEx(DWORD dwMilliseconds,
BOOL bAlertable)
{
LARGE_INTEGER Interval;
NTSTATUS errCode;
if (dwMilliseconds != INFINITE)
{
/*
* System time units are 100 nanoseconds (a nanosecond is a billionth of
* a second).
*/
Interval.QuadPart = -((LONGLONG)dwMilliseconds * 10000);
}
else
{
/* Approximately 292000 years hence */
Interval.QuadPart = -0x7FFFFFFFFFFFFFFFLL;
}
dowait:
errCode = NtDelayExecution ((BOOLEAN)bAlertable, &Interval);
if ((bAlertable) && (errCode == STATUS_ALERTED)) goto dowait;
return (errCode == STATUS_USER_APC) ? WAIT_IO_COMPLETION : 0;
}
typedef struct _QUEUE_USER_WORKITEM_CONTEXT
{
LPTHREAD_START_ROUTINE Function;
PVOID Context;
} QUEUE_USER_WORKITEM_CONTEXT, *PQUEUE_USER_WORKITEM_CONTEXT;
static VOID
NTAPI
InternalWorkItemTrampoline(PVOID Context)
{
QUEUE_USER_WORKITEM_CONTEXT Info;
ASSERT(Context);
/* Save the context to the stack */
Info = *(volatile QUEUE_USER_WORKITEM_CONTEXT *)Context;
/* Free the context before calling the callback. This avoids
a memory leak in case the thread dies... */
RtlFreeHeap(RtlGetProcessHeap(),
0,
Context);
/* Call the real callback */
Info.Function(Info.Context);
}
/*
* @implemented
*/
BOOL
WINAPI
QueueUserWorkItem(
LPTHREAD_START_ROUTINE Function,
PVOID Context,
ULONG Flags
)
{
PQUEUE_USER_WORKITEM_CONTEXT WorkItemContext;
NTSTATUS Status;
/* Save the context for the trampoline function */
WorkItemContext = RtlAllocateHeap(RtlGetProcessHeap(),
0,
sizeof(*WorkItemContext));
if (WorkItemContext == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
WorkItemContext->Function = Function;
WorkItemContext->Context = Context;
/* NOTE: Don't use Function directly since the callback signature
differs. This might cause problems on certain platforms... */
Status = RtlQueueWorkItem(InternalWorkItemTrampoline,
WorkItemContext,
Flags);
if (!NT_SUCCESS(Status))
{
/* Free the allocated context in case of failure */
RtlFreeHeap(RtlGetProcessHeap(),
0,
WorkItemContext);
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
RegisterWaitForSingleObject(
PHANDLE phNewWaitObject,
HANDLE hObject,
WAITORTIMERCALLBACK Callback,
PVOID Context,
ULONG dwMilliseconds,
ULONG dwFlags
)
{
NTSTATUS Status = RtlRegisterWait(phNewWaitObject,
hObject,
Callback,
Context,
dwMilliseconds,
dwFlags);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
HANDLE
WINAPI
RegisterWaitForSingleObjectEx(
HANDLE hObject,
WAITORTIMERCALLBACK Callback,
PVOID Context,
ULONG dwMilliseconds,
ULONG dwFlags
)
{
NTSTATUS Status;
HANDLE hNewWaitObject;
Status = RtlRegisterWait(&hNewWaitObject,
hObject,
Callback,
Context,
dwMilliseconds,
dwFlags);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return NULL;
}
return hNewWaitObject;
}
/*
* @implemented
*/
BOOL
WINAPI
UnregisterWait(
HANDLE WaitHandle
)
{
NTSTATUS Status = RtlDeregisterWaitEx(WaitHandle, NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
UnregisterWaitEx(
HANDLE WaitHandle,
HANDLE CompletionEvent
)
{
NTSTATUS Status = RtlDeregisterWaitEx(WaitHandle, CompletionEvent);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}
/* EOF */