mirror of
https://github.com/reactos/reactos.git
synced 2025-04-21 04:37:15 +00:00

- 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
948 lines
21 KiB
C
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 */
|