reactos/dll/win32/kernel32/client/thread.c
Cameron Gutman c2d0d784c7 [USB-BRINGUP-TRUNK]
- Create a branch to do a proper merge of USB work from a trunk base instead of from cmake-bringup
- In the future, DO NOT under any circumstances branch another branch. This leads to merge problems!

svn path=/branches/usb-bringup-trunk/; revision=55018
2012-01-20 20:58:46 +00:00

1188 lines
28 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;
LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
if (RealFilter != NULL)
{
_SEH2_TRY
{
ExceptionDisposition = RealFilter(ExceptionInfo);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
}
_SEH2_END;
}
return ExceptionDisposition;
}
__declspec(noreturn)
VOID
WINAPI
BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter)
{
/* Attempt to call the Thread Start Address */
_SEH2_TRY
{
/* Legacy check which is still used today for Win32 threads */
if (NtCurrentTeb()->NtTib.Version == (30 << 8)) // OS/2 V3.0 ("Cruiser")
{
/* This registers the termination port with CSRSS */
if (!BaseRunningInServerProcess) CsrNewThread();
}
/* Get the exit code from the Thread Start */
ExitThread((lpStartAddress)((PVOID)lpParameter));
}
_SEH2_EXCEPT(BaseThreadExceptionFilter(_SEH2_GetExceptionInformation()))
{
/* Get the Exit code from the SEH Handler */
if (!BaseRunningInServerProcess)
{
/* Kill the whole process, usually */
ExitProcess(_SEH2_GetExceptionCode());
}
else
{
/* If running inside CSRSS, kill just this thread */
ExitThread(_SEH2_GetExceptionCode());
}
}
_SEH2_END;
}
/*
* @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 = BaseCreateStack(hProcess,
dwStackSize,
dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ?
dwStackSize : 0,
&InitialTeb);
if(!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return NULL;
}
/* Create Initial Context */
BaseInitializeContext(&Context,
lpParameter,
lpStartAddress,
InitialTeb.StackBase,
1);
/* initialize the attributes for the thread object */
ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
lpThreadAttributes,
NULL);
/* Create the Kernel Thread Object */
Status = NtCreateThread(&hThread,
THREAD_ALL_ACCESS,
ObjectAttributes,
hProcess,
&ClientId,
&Context,
&InitialTeb,
TRUE);
if(!NT_SUCCESS(Status))
{
BaseFreeThreadStack(hProcess, &InitialTeb);
BaseSetLastNTError(Status);
return NULL;
}
/* Are we in the same process? */
if (hProcess == NtCurrentProcess())
{
PTEB Teb;
PVOID ActivationContextStack = NULL;
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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
DWORD
WINAPI
SuspendThread(HANDLE hThread)
{
ULONG PreviousSuspendCount;
NTSTATUS Status;
Status = NtSuspendThread(hThread, &PreviousSuspendCount);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(Status);
return 0;
}
Status = NtSetInformationThread(hThread,
ThreadAffinityMask,
&AffinityMask,
sizeof(KAFFINITY));
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(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 */
BaseSetLastNTError(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 */
BaseSetLastNTError(Status);
return THREAD_PRIORITY_ERROR_RETURN;
}
/* Do some conversions for saturation values */
if (ThreadBasic.BasePriority == ((HIGH_PRIORITY + 1) / 2))
{
/* Win32 calls this "time critical" */
ThreadBasic.BasePriority = THREAD_PRIORITY_TIME_CRITICAL;
}
else if (ThreadBasic.BasePriority == -((HIGH_PRIORITY + 1) / 2))
{
/* Win32 calls this "idle" */
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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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 */
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(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))
{
BaseSetLastNTError(Status);
return 0;
}
return 1;
}
BOOL
WINAPI
SetThreadStackGuarantee(IN OUT PULONG StackSizeInBytes)
{
STUB;
return FALSE;
}
/*
* @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;
}
BaseSetLastNTError(Status);
return FALSE;
}
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);
BaseSetLastNTError(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
DWORD
WINAPI
TlsAlloc(VOID)
{
ULONG Index;
PTEB Teb;
PPEB Peb;
/* Get the PEB and TEB, lock the PEB */
Teb = NtCurrentTeb();
Peb = Teb->ProcessEnvironmentBlock;
RtlAcquirePebLock();
/* Try to get regular TEB slot */
Index = RtlFindClearBitsAndSet(Peb->TlsBitmap, 1, 0);
if (Index != 0xFFFFFFFF)
{
/* Clear the value. */
Teb->TlsSlots[Index] = 0;
RtlReleasePebLock();
return Index;
}
/* If it fails, try to find expansion TEB slot. */
Index = RtlFindClearBitsAndSet(Peb->TlsExpansionBitmap, 1, 0);
if (Index != 0xFFFFFFFF)
{
/* Is there no expansion slot yet? */
if (!Teb->TlsExpansionSlots)
{
/* Allocate an array */
Teb->TlsExpansionSlots = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TLS_EXPANSION_SLOTS *
sizeof(PVOID));
}
/* Did we get an array? */
if (!Teb->TlsExpansionSlots)
{
/* Fail */
RtlClearBits(Peb->TlsExpansionBitmap, Index, 1);
Index = 0xFFFFFFFF;
BaseSetLastNTError(STATUS_NO_MEMORY);
}
else
{
/* Clear the value. */
Teb->TlsExpansionSlots[Index] = 0;
Index += TLS_MINIMUM_AVAILABLE;
}
}
else
{
/* Fail */
BaseSetLastNTError(STATUS_NO_MEMORY);
}
/* Release the lock and return */
RtlReleasePebLock();
return Index;
}
/*
* @implemented
*/
BOOL
WINAPI
TlsFree(IN DWORD Index)
{
BOOL BitSet;
PPEB Peb;
ULONG TlsIndex;
PVOID TlsBitmap;
NTSTATUS Status;
/* Acquire the PEB lock and grab the PEB */
Peb = NtCurrentPeb();
RtlAcquirePebLock();
/* Check if the index is too high */
if (Index >= TLS_MINIMUM_AVAILABLE)
{
/* Check if it can fit in the expansion slots */
TlsIndex = Index - TLS_MINIMUM_AVAILABLE;
if (TlsIndex >= TLS_EXPANSION_SLOTS)
{
/* It's invalid */
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
RtlReleasePebLock();
return FALSE;
}
else
{
/* Use the expansion bitmap */
TlsBitmap = Peb->TlsExpansionBitmap;
Index = TlsIndex;
}
}
else
{
/* Use the normal bitmap */
TlsBitmap = Peb->TlsBitmap;
}
/* Check if the index was set */
BitSet = RtlAreBitsSet(TlsBitmap, Index, 1);
if (BitSet)
{
/* Tell the kernel to free the TLS cells */
Status = NtSetInformationThread(NtCurrentThread(),
ThreadZeroTlsCell,
&Index,
sizeof(DWORD));
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
RtlReleasePebLock();
return FALSE;
}
/* Clear the bit */
RtlClearBits(TlsBitmap, Index, 1);
}
else
{
/* Fail */
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
RtlReleasePebLock();
return FALSE;
}
/* Done! */
RtlReleasePebLock();
return TRUE;
}
/*
* @implemented
*/
LPVOID
WINAPI
TlsGetValue(IN DWORD Index)
{
PTEB Teb;
/* Get the TEB and clear the last error */
Teb = NtCurrentTeb();
Teb->LastErrorValue = 0;
/* Check for simple TLS index */
if (Index < TLS_MINIMUM_AVAILABLE)
{
/* Return it */
return Teb->TlsSlots[Index];
}
/* Check for valid index */
if (Index >= TLS_EXPANSION_SLOTS + TLS_MINIMUM_AVAILABLE)
{
/* Fail */
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
/* The expansion slots are allocated on demand, so check for it. */
Teb->LastErrorValue = 0;
if (!Teb->TlsExpansionSlots) return NULL;
/* Return the value from the expansion slots */
return Teb->TlsExpansionSlots[Index - TLS_MINIMUM_AVAILABLE];
}
/*
* @implemented
*/
BOOL
WINAPI
TlsSetValue(IN DWORD Index,
IN LPVOID Value)
{
DWORD TlsIndex;
PTEB Teb = NtCurrentTeb();
/* Check for simple TLS index */
if (Index < TLS_MINIMUM_AVAILABLE)
{
/* Return it */
Teb->TlsSlots[Index] = Value;
return TRUE;
}
/* Check if this is an expansion slot */
TlsIndex = Index - TLS_MINIMUM_AVAILABLE;
if (TlsIndex >= TLS_EXPANSION_SLOTS)
{
/* Fail */
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return FALSE;
}
/* Do we not have expansion slots? */
if (!Teb->TlsExpansionSlots)
{
/* Get the PEB lock to see if we still need them */
RtlAcquirePebLock();
if (!Teb->TlsExpansionSlots)
{
/* Allocate them */
Teb->TlsExpansionSlots = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TLS_EXPANSION_SLOTS *
sizeof(PVOID));
if (!Teb->TlsExpansionSlots)
{
/* Fail */
RtlReleasePebLock();
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
}
/* Release the lock */
RtlReleasePebLock();
}
/* Write the value */
Teb->TlsExpansionSlots[TlsIndex] = Value;
/* Success */
return TRUE;
}
/* EOF */