/* * 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 #define NDEBUG #include /* 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 */