/* * 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; if (GlobalTopLevelExceptionFilter != NULL) { _SEH2_TRY { ExceptionDisposition = GlobalTopLevelExceptionFilter(ExceptionInfo); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo); } _SEH2_END; } return ExceptionDisposition; } __declspec(noreturn) VOID WINAPI BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter) { volatile UINT uExitCode = 0; /* Attempt to call the Thread Start Address */ _SEH2_TRY { /* Get the exit code from the Thread Start */ uExitCode = (lpStartAddress)((PVOID)lpParameter); } _SEH2_EXCEPT(BaseThreadExceptionFilter(_SEH2_GetExceptionInformation())) { /* Get the Exit code from the SEH Handler */ uExitCode = _SEH2_GetExceptionCode(); } _SEH2_END; /* Exit the Thread */ ExitThread(uExitCode); } /* * @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 = 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; } /* Are we in the same process? */ if (hProcess == NtCurrentProcess()) { PTEB Teb; PVOID ActivationContextStack; 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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(Status); return FALSE; } return TRUE; } /* * @implemented */ DWORD WINAPI SuspendThread(HANDLE hThread) { ULONG PreviousSuspendCount; NTSTATUS Status; Status = NtSuspendThread(hThread, &PreviousSuspendCount); if (!NT_SUCCESS(Status)) { SetLastErrorByStatus(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)) { 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 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 */ SetLastErrorByStatus(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 */ 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 WINAPI 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 = 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)) { SetLastErrorByStatus(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 */ SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(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)) { SetLastErrorByStatus(Status); return 0; } return 1; } /* * @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; } SetLastErrorByStatus(Status); return FALSE; } /* * @implemented */ VOID WINAPI Sleep(DWORD dwMilliseconds) { SleepEx(dwMilliseconds, FALSE); return; } /* * @implemented */ DWORD WINAPI 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 = -((LONGLONG)dwMilliseconds * 10000); } else { /* Approximately 292000 years hence */ Interval.QuadPart = -0x7FFFFFFFFFFFFFFFLL; } dowait: errCode = NtDelayExecution ((BOOLEAN)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); /* 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); SetLastErrorByStatus(Status); return FALSE; } return TRUE; } /* * @implemented */ BOOL WINAPI RegisterWaitForSingleObject( PHANDLE phNewWaitObject, HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID Context, ULONG dwMilliseconds, ULONG dwFlags ) { NTSTATUS Status = RtlRegisterWait(phNewWaitObject, hObject, Callback, Context, dwMilliseconds, dwFlags); if (!NT_SUCCESS(Status)) { SetLastErrorByStatus(Status); return FALSE; } return TRUE; } /* * @implemented */ HANDLE WINAPI RegisterWaitForSingleObjectEx( HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID Context, ULONG dwMilliseconds, ULONG dwFlags ) { NTSTATUS Status; HANDLE hNewWaitObject; Status = RtlRegisterWait(&hNewWaitObject, hObject, Callback, Context, dwMilliseconds, dwFlags); if (!NT_SUCCESS(Status)) { SetLastErrorByStatus(Status); return NULL; } return hNewWaitObject; } /* * @implemented */ BOOL WINAPI UnregisterWait( HANDLE WaitHandle ) { NTSTATUS Status = RtlDeregisterWaitEx(WaitHandle, NULL); if (!NT_SUCCESS(Status)) { SetLastErrorByStatus(Status); return FALSE; } return TRUE; } /* * @implemented */ BOOL WINAPI UnregisterWaitEx( HANDLE WaitHandle, HANDLE CompletionEvent ) { NTSTATUS Status = RtlDeregisterWaitEx(WaitHandle, CompletionEvent); if (!NT_SUCCESS(Status)) { SetLastErrorByStatus(Status); return FALSE; } return TRUE; } /* EOF */