/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: dll/win32/kernel32/client/thread.c * PURPOSE: Thread functions * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Ariadne (ariadne@xs4all.nl) * */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include #define SXS_SUPPORT_FIXME typedef NTSTATUS (NTAPI *PCSR_CREATE_REMOTE_THREAD)(IN HANDLE ThreadHandle, IN PCLIENT_ID ClientId); 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(IN LPTHREAD_START_ROUTINE lpStartAddress, IN 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; } VOID NTAPI BaseDispatchApc(IN PAPCFUNC ApcRoutine, IN PVOID Data, IN PACTIVATION_CONTEXT ActivationContext) { RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame; /* Setup the activation context */ ActivationFrame.Size = sizeof(ActivationFrame); ActivationFrame.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER; /* Check if caller wanted one */ if (ActivationContext == INVALID_ACTIVATION_CONTEXT) { /* Do the APC directly */ ApcRoutine((ULONG_PTR)Data); return; } /* Then activate it */ RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext); /* Call the routine under SEH */ _SEH2_TRY { ApcRoutine((ULONG_PTR)Data); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; /* Now de-activate and release the activation context */ RtlDeactivateActivationContextUnsafeFast(&ActivationFrame); RtlReleaseActivationContext(ActivationContext); } /* PUBLIC FUNCTIONS ***********************************************************/ /* * @implemented */ HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT 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(IN HANDLE hProcess, IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId) { NTSTATUS Status; INITIAL_TEB InitialTeb; CONTEXT Context; CLIENT_ID ClientId; OBJECT_ATTRIBUTES LocalObjectAttributes; POBJECT_ATTRIBUTES ObjectAttributes; HANDLE hThread; ULONG Dummy; PTEB Teb; THREAD_BASIC_INFORMATION ThreadBasicInfo; PACTIVATION_CONTEXT_STACK ActivationContextStack = NULL; ACTIVATION_CONTEXT_BASIC_INFORMATION ActCtxInfo; ULONG_PTR Cookie; ULONG ReturnLength; SIZE_T ReturnSize; DPRINT("CreateRemoteThread: hProcess: %p dwStackSize: %lu lpStartAddress" ": %p lpParameter: %p, 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)) { /* Fail the kernel create */ BaseFreeThreadStack(hProcess, &InitialTeb); BaseSetLastNTError(Status); return NULL; } /* Are we in the same process? */ if (hProcess == NtCurrentProcess()) { /* Get the TEB */ Status = NtQueryInformationThread(hThread, ThreadBasicInformation, &ThreadBasicInfo, sizeof(ThreadBasicInfo), &ReturnLength); if (!NT_SUCCESS(Status)) { /* Fail */ ERROR_DBGBREAK("SXS: %s - Failing thread create because " "NtQueryInformationThread() failed with status %08lx\n", __FUNCTION__, Status); return NULL; } /* Allocate the Activation Context Stack */ Status = RtlAllocateActivationContextStack(&ActivationContextStack); if (!NT_SUCCESS(Status)) { /* Fail */ ERROR_DBGBREAK("SXS: %s - Failing thread create because " "RtlAllocateActivationContextStack() failed with status %08lx\n", __FUNCTION__, Status); return NULL; } /* Save it */ Teb = ThreadBasicInfo.TebBaseAddress; Teb->ActivationContextStackPointer = ActivationContextStack; /* Query the Context */ Status = RtlQueryInformationActivationContext(RTL_QUERY_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT, NULL, 0, ActivationContextBasicInformation, &ActCtxInfo, sizeof(ActCtxInfo), &ReturnSize); if (!NT_SUCCESS(Status)) { /* Fail */ ERROR_DBGBREAK("SXS: %s - Failing thread create because " "RtlQueryInformationActivationContext() failed with status %08lx\n", __FUNCTION__, Status); /* Free the activation context stack */ // RtlFreeThreadActivationContextStack(); RtlFreeActivationContextStack(Teb->ActivationContextStackPointer); return NULL; } /* Does it need to be activated? */ if ((ActCtxInfo.hActCtx) && !(ActCtxInfo.dwFlags & 1)) { /* Activate it */ Status = RtlActivateActivationContextEx(RTL_ACTIVATE_ACTIVATION_CONTEXT_EX_FLAG_RELEASE_ON_STACK_DEALLOCATION, Teb, ActCtxInfo.hActCtx, &Cookie); if (!NT_SUCCESS(Status)) { /* Fail */ ERROR_DBGBREAK("SXS: %s - Failing thread create because " "RtlActivateActivationContextEx() failed with status %08lx\n", __FUNCTION__, Status); /* Free the activation context stack */ // RtlFreeThreadActivationContextStack(); RtlFreeActivationContextStack(Teb->ActivationContextStackPointer); return NULL; } } } /* Notify CSR */ if (!BaseRunningInServerProcess) { Status = BasepNotifyCsrOfThread(hThread, &ClientId); ASSERT(NT_SUCCESS(Status)); } else { if (hProcess != NtCurrentProcess()) { PCSR_CREATE_REMOTE_THREAD CsrCreateRemoteThread; /* Get the direct CSRSRV export */ CsrCreateRemoteThread = (PCSR_CREATE_REMOTE_THREAD) GetProcAddress(GetModuleHandleA("csrsrv"), "CsrCreateRemoteThread"); if (CsrCreateRemoteThread) { /* Call it instead of going through LPC */ Status = CsrCreateRemoteThread(hThread, &ClientId); ASSERT(NT_SUCCESS(Status)); } } } /* 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(IN DWORD uExitCode) { NTSTATUS Status; ULONG LastThread; PRTL_CRITICAL_SECTION LoaderLock; /* Make sure loader lock isn't held */ LoaderLock = NtCurrentPeb()->LoaderLock; if (LoaderLock) ASSERT(NtCurrentTeb()->ClientId.UniqueThread != LoaderLock->OwningThread); /* * 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)) 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 */ ERROR_FATAL("It should not happen\n"); while (TRUE); /* 'noreturn' function */ } /* * @implemented */ HANDLE WINAPI OpenThread(IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN 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(IN HANDLE hThread, OUT LPFILETIME lpCreationTime, OUT LPFILETIME lpExitTime, OUT LPFILETIME lpKernelTime, OUT 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(IN HANDLE hThread, OUT LPCONTEXT lpContext) { NTSTATUS Status; Status = NtGetContextThread(hThread, lpContext); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return FALSE; } return TRUE; } /* * @implemented */ BOOL WINAPI SetThreadContext(IN HANDLE hThread, IN CONST CONTEXT *lpContext) { NTSTATUS Status; Status = NtSetContextThread(hThread, (PCONTEXT)lpContext); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return FALSE; } return TRUE; } /* * @implemented */ BOOL WINAPI GetExitCodeThread(IN HANDLE hThread, OUT 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(IN HANDLE hThread) { ULONG PreviousResumeCount; NTSTATUS Status; Status = NtResumeThread(hThread, &PreviousResumeCount); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return -1; } return PreviousResumeCount; } /* * @implemented */ BOOL WINAPI TerminateThread(IN HANDLE hThread, IN DWORD dwExitCode) { NTSTATUS Status; #if DBG PRTL_CRITICAL_SECTION LoaderLock; THREAD_BASIC_INFORMATION ThreadInfo; #endif /* DBG */ /* Check for invalid thread handle */ if (!hThread) { /* Fail if one was passed */ SetLastError(ERROR_INVALID_HANDLE); return FALSE; } #if DBG /* Get the loader lock */ LoaderLock = NtCurrentPeb()->LoaderLock; if (LoaderLock) { /* Get our TID */ Status = NtQueryInformationThread(hThread, ThreadBasicInformation, &ThreadInfo, sizeof(ThreadInfo), NULL); if (NT_SUCCESS(Status)) { /* If terminating the current thread, we must not hold the loader lock */ if (NtCurrentTeb()->ClientId.UniqueThread == ThreadInfo.ClientId.UniqueThread) ASSERT(NtCurrentTeb()->ClientId.UniqueThread != LoaderLock->OwningThread); } } #endif /* DBG */ /* Now terminate the thread */ Status = NtTerminateThread(hThread, dwExitCode); if (!NT_SUCCESS(Status)) { /* Fail */ BaseSetLastNTError(Status); return FALSE; } /* All done */ return TRUE; } /* * @implemented */ DWORD WINAPI SuspendThread(IN 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(IN HANDLE hThread, IN 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(IN HANDLE hThread, IN int nPriority) { LONG Prio = nPriority; NTSTATUS Status; /* Check if values forcing saturation should be used */ if (Prio == THREAD_PRIORITY_TIME_CRITICAL) { /* This is 16 */ Prio = (HIGH_PRIORITY + 1) / 2; } else if (Prio == THREAD_PRIORITY_IDLE) { /* This is -16 */ 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(IN 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 = bDisablePriorityBoost != FALSE; 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(IN HANDLE hThread, IN 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(IN 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(IN 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(IN LANGID LangId) { UNIMPLEMENTED; return (LANGID)NtCurrentTeb()->CurrentLocale; } /* * @implemented */ DWORD WINAPI QueueUserAPC(IN PAPCFUNC pfnAPC, IN HANDLE hThread, IN ULONG_PTR dwData) { NTSTATUS Status; ACTIVATION_CONTEXT_BASIC_INFORMATION ActCtxInfo; /* Zero the activation context and query information on it */ RtlZeroMemory(&ActCtxInfo, sizeof(ActCtxInfo)); Status = RtlQueryInformationActivationContext(RTL_QUERY_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT, NULL, 0, ActivationContextBasicInformation, &ActCtxInfo, sizeof(ActCtxInfo), NULL); if (!NT_SUCCESS(Status)) { /* Fail due to SxS */ DbgPrint("SXS: %s failing because RtlQueryInformationActivationContext()" "returned status %08lx\n", __FUNCTION__, Status); BaseSetLastNTError(Status); return FALSE; } /* Queue the APC */ Status = NtQueueApcThread(hThread, (PKNORMAL_ROUTINE)BaseDispatchApc, pfnAPC, (PVOID)dwData, (ActCtxInfo.dwFlags & 1) ? INVALID_ACTIVATION_CONTEXT : ActCtxInfo.hActCtx); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return FALSE; } /* All good */ return TRUE; } /* * @implemented */ BOOL WINAPI SetThreadStackGuarantee(IN OUT PULONG StackSizeInBytes) { static int once; if (once++ == 0) DPRINT1("SetThreadStackGuarantee(%p): stub\n", StackSizeInBytes); return TRUE; } /* * @implemented */ BOOL WINAPI GetThreadIOPendingFlag(IN HANDLE hThread, OUT PBOOL lpIOIsPending) { ULONG IoPending; NTSTATUS Status; /* Query the flag */ Status = NtQueryInformationThread(hThread, ThreadIsIoPending, &IoPending, sizeof(IoPending), NULL); if (NT_SUCCESS(Status)) { /* Return the flag */ *lpIOIsPending = IoPending ? TRUE : FALSE; return TRUE; } /* Fail */ BaseSetLastNTError(Status); return FALSE; } /* * @implemented */ BOOL WINAPI QueueUserWorkItem(IN LPTHREAD_START_ROUTINE Function, IN PVOID Context, IN ULONG Flags) { NTSTATUS Status; /* NOTE: Rtl needs to safely call the function using a trampoline */ Status = RtlQueueWorkItem((WORKERCALLBACKFUNC)Function, Context, Flags); if (!NT_SUCCESS(Status)) { /* Failed */ BaseSetLastNTError(Status); return FALSE; } /* All good */ 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 */