reactos/reactos/dll/win32/kernel32/thread/thread.c
Alex Ionescu c39989df1b - Fix SleepEx.
- Put volatile statements in EX_RUNDOWN_REF, IRP, DEVICE_OBJECT, ERESOURCE, FILE_OBJECT, IO_REMOVE_LOCK, WORK_QUEUE_ITEM where required (thanks to Microsoft's changes in the WDK to mark the fields properly).
- Update FILE_OBJECT definition.
- Add some asserts to some I/O functions.
- Add stub support for File Objects created by XP+ Drivers which have File Object Extensions.
- Add some fixes to IopDeleteFile, including proper reference counting for the DO and VPB, as well as cleanup when the file is closed without a handle.
- Fix a bug in IopSecurityFile.
- Queue and unqueue IRPs in all I/O functions.
- Fully support IRP cancellation now.
- Fix critical bugs in NtDeviceIoControlFile and NtDeviceFsControlFile which were causing double queueing of IRPs and freeing of invalid memory, as well as invalid paramter checking for user-mode buffers.
- Add exhaustive validation checks to IoCreateFile, add more failure cases, and validate the EA buffer. Also support IO_ATTACH_DEVICE_API flag.
- Implement IoCreateStreamFileObjectEx and IoCreateStreamFileObjectLite and fix several bugs in the original implementation of IoCreateStreamFileObject.
- Fix a bug in RtlRaiseException.
- Update Io*ShareAccess routines to support XP+ style semantics related to special File Object flags which disable their use.
- Add validation to all Query/Set routines so that information clasess, lengths, buffers and alignment are properly checked.
- Also add an array for the proper acess rights that each query/set operation requires.
- Check backup/restore privileges during I/O File operations.
- Check traverse access during I/O File Operations.
- Check access privileges to the device during I/O file operations.
- Rename IopReferenceDeviceObject and also verify if an exclusive DO is trying to be invalidly opened.
- Support various extra security checks during I/O File/Device Parse Routine.
- Fix a bug during IopCleanupIrp so that we don't dereference the File OBject if this was a create operation.
- Fix some bogus asserts in IofCompleteRequest, and save the IRP Flags before signalling it's event, since the driver might've freed it behind our back.
- Fix a large bug in ObInsertObject which affected the insert of unnamed objects with forced security options (Such as process/threads).
- Fix the creation of the Process/Thread/Job Obejct Types to that security information is forced.
- Remove "Fix PS!!!" messages since the bug is now fixed and these objects now get proper security descriptors.
- Fix another bug in ObInsertObjet which wasn't properly validating user-mode objects and always assumed kernel mode.
- Silence multiple trace/checkpoint messages that have accumulated throughout time for various debugging purposes.

svn path=/trunk/; revision=25118
2006-12-10 18:40:30 +00:00

948 lines
21 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 "../include/debug.h"
/* FIXME: NDK */
#define HIGH_PRIORITY 31
/* FUNCTIONS *****************************************************************/
_SEH_FILTER(BaseThreadExceptionFilter)
{
EXCEPTION_POINTERS * ExceptionInfo = _SEH_GetExceptionPointers();
LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
if (GlobalTopLevelExceptionFilter != NULL)
{
_SEH_TRY
{
ExceptionDisposition = GlobalTopLevelExceptionFilter(ExceptionInfo);
}
_SEH_HANDLE
{
ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
}
_SEH_END;
}
return ExceptionDisposition;
}
__declspec(noreturn)
VOID
STDCALL
BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter)
{
volatile UINT uExitCode = 0;
/* Attempt to call the Thread Start Address */
_SEH_TRY
{
/* Get the exit code from the Thread Start */
uExitCode = (lpStartAddress)((PVOID)lpParameter);
}
_SEH_EXCEPT(BaseThreadExceptionFilter)
{
/* Get the Exit code from the SEH Handler */
uExitCode = _SEH_GetExceptionCode();
} _SEH_END;
/* Exit the Thread */
ExitThread(uExitCode);
}
/*
* @implemented
*/
HANDLE
STDCALL
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
STDCALL
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;
}
#ifdef SXS_SUPPORT_ENABLED
/* Are we in the same process? */
if (Process = NtCurrentProcess())
{
PTEB Teb;
PVOID ActivationContextStack;
PTHREAD_BASIC_INFORMATION ThreadBasicInfo;
PACTIVATION_CONTEXT_BASIC_INFORMATION ActivationCtxInfo;
ULONG_PTR Cookie;
/* Get the TEB */
Status = NtQueryInformationThread(hThread,
ThreadBasicIformation,
&ThreadBasicInfo,
sizeof(ThreadBasicInfo),
NULL);
/* Allocate the Activation Context Stack */
Status = RtlAllocateActivationContextStack(&ActivationContextStack);
Teb = ThreadBasicInfo.TebBaseAddress;
/* Save it */
Teb->ActivationContextStackPointer = ActivationContextStack;
/* Query the Context */
Status = RtlQueryInformationActivationContext(1,
0,
NULL,
ActivationContextBasicInformation,
&ActivationCtxInfo,
sizeof(ActivationCtxInfo),
NULL);
/* Does it need to be activated? */
if (!ActivationCtxInfo.hActCtx)
{
/* Activate it */
Status = RtlActivateActivationContextEx(1,
Teb,
ActivationCtxInfo.hActCtx,
&Cookie);
}
}
#endif
/* FIXME: Notify CSR */
/* Success */
if(lpThreadId) *lpThreadId = (DWORD)ClientId.UniqueThread;
/* Resume it if asked */
if (!(dwCreationFlags & CREATE_SUSPENDED))
{
NtResumeThread(hThread, &Dummy);
}
/* Return handle to thread */
return hThread;
}
/*
* @implemented
*/
VOID
STDCALL
ExitThread(DWORD uExitCode)
{
NTSTATUS Status;
BOOLEAN LastThread;
/*
* Terminate process if this is the last thread
* of the current process
*/
Status = NtQueryInformationThread(NtCurrentThread(),
ThreadAmILastThread,
&LastThread,
sizeof(BOOLEAN),
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 will never reach this place. This silences the compiler */
ExitThread(uExitCode);
}
/*
* @implemented
*/
HANDLE
STDCALL
OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadId
)
{
NTSTATUS errCode;
HANDLE ThreadHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientId ;
ClientId.UniqueProcess = 0;
ClientId.UniqueThread = (HANDLE)dwThreadId;
InitializeObjectAttributes (&ObjectAttributes,
NULL,
(bInheritHandle ? OBJ_INHERIT : 0),
NULL,
NULL);
errCode = NtOpenThread(&ThreadHandle,
dwDesiredAccess,
&ObjectAttributes,
&ClientId);
if (!NT_SUCCESS(errCode))
{
SetLastErrorByStatus (errCode);
return NULL;
}
return ThreadHandle;
}
/*
* @implemented
*/
PTEB
GetTeb(VOID)
{
return(NtCurrentTeb());
}
/*
* @implemented
*/
BOOL STDCALL
SwitchToThread(VOID)
{
NTSTATUS Status;
Status = NtYieldExecution();
return Status != STATUS_NO_YIELD_PERFORMED;
}
/*
* @implemented
*/
DWORD STDCALL
GetCurrentThreadId(VOID)
{
return((DWORD)(NtCurrentTeb()->Cid).UniqueThread);
}
/*
* @implemented
*/
BOOL STDCALL
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->dwLowDateTime = KernelUserTimes.CreateTime.u.LowPart;
lpCreationTime->dwHighDateTime = KernelUserTimes.CreateTime.u.HighPart;
lpExitTime->dwLowDateTime = KernelUserTimes.ExitTime.u.LowPart;
lpExitTime->dwHighDateTime = KernelUserTimes.ExitTime.u.HighPart;
lpKernelTime->dwLowDateTime = KernelUserTimes.KernelTime.u.LowPart;
lpKernelTime->dwHighDateTime = KernelUserTimes.KernelTime.u.HighPart;
lpUserTime->dwLowDateTime = KernelUserTimes.UserTime.u.LowPart;
lpUserTime->dwHighDateTime = KernelUserTimes.UserTime.u.HighPart;
return(TRUE);
}
/*
* @implemented
*/
BOOL STDCALL
GetThreadContext(HANDLE hThread,
LPCONTEXT lpContext)
{
NTSTATUS Status;
Status = NtGetContextThread(hThread,
lpContext);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(FALSE);
}
return(TRUE);
}
/*
* @implemented
*/
BOOL STDCALL
SetThreadContext(HANDLE hThread,
CONST CONTEXT *lpContext)
{
NTSTATUS Status;
Status = NtSetContextThread(hThread,
(void *)lpContext);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(FALSE);
}
return(TRUE);
}
/*
* @implemented
*/
BOOL STDCALL
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);
}
memcpy(lpExitCode, &ThreadBasic.ExitStatus, sizeof(DWORD));
return(TRUE);
}
/*
* @implemented
*/
DWORD STDCALL
ResumeThread(HANDLE hThread)
{
ULONG PreviousResumeCount;
NTSTATUS Status;
Status = NtResumeThread(hThread,
&PreviousResumeCount);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(-1);
}
return(PreviousResumeCount);
}
/*
* @implemented
*/
BOOL STDCALL
TerminateThread(HANDLE hThread,
DWORD dwExitCode)
{
NTSTATUS Status;
if (0 == hThread)
{
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
Status = NtTerminateThread(hThread,
dwExitCode);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(FALSE);
}
return(TRUE);
}
/*
* @implemented
*/
DWORD STDCALL
SuspendThread(HANDLE hThread)
{
ULONG PreviousSuspendCount;
NTSTATUS Status;
Status = NtSuspendThread(hThread,
&PreviousSuspendCount);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return(-1);
}
return(PreviousSuspendCount);
}
/*
* @implemented
*/
DWORD STDCALL
SetThreadAffinityMask(HANDLE hThread,
DWORD 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
STDCALL
SetThreadPriority(HANDLE hThread,
int nPriority)
{
ULONG 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(ULONG));
if (!NT_SUCCESS(Status))
{
/* Failure */
SetLastErrorByStatus(Status);
return FALSE;
}
/* Return */
return TRUE;
}
/*
* @implemented
*/
int
STDCALL
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 STDCALL
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 = !((BOOL)PriorityBoost);
return(TRUE);
}
/*
* @implemented
*/
BOOL STDCALL
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 STDCALL
GetThreadSelectorEntry(IN HANDLE hThread,
IN DWORD dwSelector,
OUT LPLDT_ENTRY lpSelectorEntry)
{
DESCRIPTOR_TABLE_ENTRY DescriptionTableEntry;
NTSTATUS Status;
DescriptionTableEntry.Selector = dwSelector;
Status = NtQueryInformationThread(hThread,
ThreadDescriptorTableEntry,
&DescriptionTableEntry,
sizeof(DESCRIPTOR_TABLE_ENTRY),
NULL);
if(!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
*lpSelectorEntry = DescriptionTableEntry.Descriptor;
return TRUE;
}
/*
* @implemented
*/
DWORD STDCALL
SetThreadIdealProcessor(HANDLE hThread,
DWORD dwIdealProcessor)
{
NTSTATUS Status;
Status = NtSetInformationThread(hThread,
ThreadIdealProcessor,
&dwIdealProcessor,
sizeof(ULONG));
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return -1;
}
return dwIdealProcessor;
}
/*
* @implemented
*/
DWORD STDCALL
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 (DWORD)ThreadBasic.ClientId.UniqueProcess;
}
/*
* @implemented
*/
DWORD STDCALL
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 (DWORD)ThreadBasic.ClientId.UniqueThread;
}
/*
* @unimplemented
*/
LANGID STDCALL
SetThreadUILanguage(WORD wReserved)
{
DPRINT1("SetThreadUILanguage(0x%4x) unimplemented!\n", wReserved);
return 0;
}
static void CALLBACK
IntCallUserApc(PVOID Function, PVOID dwData, PVOID Argument3)
{
PAPCFUNC pfnAPC = (PAPCFUNC)Function;
pfnAPC((ULONG_PTR)dwData);
}
/*
* @implemented
*/
DWORD STDCALL
QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData)
{
NTSTATUS Status;
Status = NtQueueApcThread(hThread, IntCallUserApc, pfnAPC,
(PVOID)dwData, NULL);
if (Status)
SetLastErrorByStatus(Status);
return NT_SUCCESS(Status);
}
/*
* @implemented
*/
BOOL STDCALL
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 STDCALL
Sleep(DWORD dwMilliseconds)
{
SleepEx(dwMilliseconds, FALSE);
return;
}
/*
* @implemented
*/
DWORD STDCALL
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 = -((ULONGLONG)dwMilliseconds * 10000);
}
else
{
/* Approximately 292000 years hence */
Interval.QuadPart = -0x7FFFFFFFFFFFFFFFLL;
}
dowait:
errCode = NtDelayExecution (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 != NULL);
/* 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
STDCALL
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;
}
/* EOF */