reactos/sdk/lib/rtl/thread.c

339 lines
10 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* PURPOSE: Rtl user thread functions
* FILE: lib/rtl/thread.c
* PROGRAMERS:
* Alex Ionescu (alex@relsoft.net)
* Eric Kohl
* KJK::Hyperion
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
#define NDEBUG
#include <debug.h>
/* PRIVATE FUNCTIONS *******************************************************/
NTSTATUS
NTAPI
RtlpCreateUserStack(IN HANDLE hProcess,
IN SIZE_T StackReserve OPTIONAL,
IN SIZE_T StackCommit OPTIONAL,
IN ULONG StackZeroBits OPTIONAL,
OUT PINITIAL_TEB InitialTeb)
{
NTSTATUS Status;
SYSTEM_BASIC_INFORMATION SystemBasicInfo;
PIMAGE_NT_HEADERS Headers;
ULONG_PTR Stack = 0;
BOOLEAN UseGuard = FALSE;
ULONG Dummy;
SIZE_T GuardPageSize;
/* Get some memory information */
Status = ZwQuerySystemInformation(SystemBasicInformation,
&SystemBasicInfo,
sizeof(SYSTEM_BASIC_INFORMATION),
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* Use the Image Settings if we are dealing with the current Process */
if (hProcess == NtCurrentProcess())
{
/* Get the Image Headers */
Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
if (!Headers) return STATUS_INVALID_IMAGE_FORMAT;
/* If we didn't get the parameters, find them ourselves */
if (!StackReserve) StackReserve = Headers->OptionalHeader.
SizeOfStackReserve;
if (!StackCommit) StackCommit = Headers->OptionalHeader.
SizeOfStackCommit;
}
else
{
/* Use the System Settings if needed */
if (!StackReserve) StackReserve = SystemBasicInfo.AllocationGranularity;
if (!StackCommit) StackCommit = SystemBasicInfo.PageSize;
}
/* Check if the commit is higher than the reserve*/
if (StackCommit >= StackReserve)
{
/* Grow the reserve beyond the commit, up to 1MB alignment */
StackReserve = ROUND_UP(StackCommit, 1024 * 1024);
}
/* Align everything to Page Size */
StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
/* Reserve memory for the stack */
Status = ZwAllocateVirtualMemory(hProcess,
(PVOID*)&Stack,
StackZeroBits,
&StackReserve,
MEM_RESERVE,
PAGE_READWRITE);
if (!NT_SUCCESS(Status)) return Status;
/* Now set up some basic Initial TEB Parameters */
InitialTeb->PreviousStackBase = NULL;
InitialTeb->PreviousStackLimit = NULL;
InitialTeb->AllocatedStackBase = (PVOID)Stack;
InitialTeb->StackBase = (PVOID)(Stack + StackReserve);
/* Update the Stack Position */
Stack += StackReserve - StackCommit;
/* Check if we will need a guard page */
if (StackReserve > StackCommit)
{
/* Remove a page to set as guard page */
Stack -= SystemBasicInfo.PageSize;
StackCommit += SystemBasicInfo.PageSize;
UseGuard = TRUE;
}
/* Allocate memory for the stack */
Status = ZwAllocateVirtualMemory(hProcess,
(PVOID*)&Stack,
0,
&StackCommit,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status)) return Status;
/* Now set the current Stack Limit */
InitialTeb->StackLimit = (PVOID)Stack;
/* Create a guard page */
if (UseGuard)
{
/* Attempt maximum space possible */
GuardPageSize = SystemBasicInfo.PageSize;
Status = ZwProtectVirtualMemory(hProcess,
(PVOID*)&Stack,
&GuardPageSize,
PAGE_GUARD | PAGE_READWRITE,
&Dummy);
if (!NT_SUCCESS(Status)) return Status;
/* Update the Stack Limit keeping in mind the Guard Page */
InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit +
GuardPageSize);
}
/* We are done! */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlpFreeUserStack(IN HANDLE Process,
IN PINITIAL_TEB InitialTeb)
{
SIZE_T Dummy = 0;
NTSTATUS Status;
/* Free the Stack */
Status = ZwFreeVirtualMemory(Process,
&InitialTeb->AllocatedStackBase,
&Dummy,
MEM_RELEASE);
/* Clear the initial TEB */
RtlZeroMemory(InitialTeb, sizeof(INITIAL_TEB));
return Status;
}
/* FUNCTIONS ***************************************************************/
/*
* @implemented
*/
NTSTATUS
__cdecl
RtlSetThreadIsCritical(IN BOOLEAN NewValue,
OUT PBOOLEAN OldValue OPTIONAL,
IN BOOLEAN NeedBreaks)
{
ULONG BreakOnTermination;
/* Initialize to FALSE */
if (OldValue) *OldValue = FALSE;
/* Fail, if the critical breaks flag is required but is not set */
if ((NeedBreaks) &&
!(NtCurrentPeb()->NtGlobalFlag & FLG_ENABLE_SYSTEM_CRIT_BREAKS))
{
return STATUS_UNSUCCESSFUL;
}
/* Check if the caller wants the old value */
if (OldValue)
{
/* Query and return the old break on termination flag for the process */
ZwQueryInformationThread(NtCurrentThread(),
ThreadBreakOnTermination,
&BreakOnTermination,
sizeof(ULONG),
NULL);
*OldValue = (BOOLEAN)BreakOnTermination;
}
/* Set the break on termination flag for the process */
BreakOnTermination = NewValue;
return ZwSetInformationThread(NtCurrentThread(),
ThreadBreakOnTermination,
&BreakOnTermination,
sizeof(ULONG));
}
/*
@implemented
*/
NTSTATUS
NTAPI
RtlCreateUserThread(IN HANDLE ProcessHandle,
IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
IN BOOLEAN CreateSuspended,
IN ULONG StackZeroBits OPTIONAL,
IN SIZE_T StackReserve OPTIONAL,
IN SIZE_T StackCommit OPTIONAL,
IN PTHREAD_START_ROUTINE StartAddress,
IN PVOID Parameter OPTIONAL,
OUT PHANDLE ThreadHandle OPTIONAL,
OUT PCLIENT_ID ClientId OPTIONAL)
{
NTSTATUS Status;
HANDLE Handle;
CLIENT_ID ThreadCid;
INITIAL_TEB InitialTeb;
OBJECT_ATTRIBUTES ObjectAttributes;
CONTEXT Context;
/* First, we'll create the Stack */
Status = RtlpCreateUserStack(ProcessHandle,
StackReserve,
StackCommit,
StackZeroBits,
&InitialTeb);
if (!NT_SUCCESS(Status)) return Status;
/* Next, we'll set up the Initial Context */
RtlInitializeContext(ProcessHandle,
&Context,
Parameter,
StartAddress,
InitialTeb.StackBase);
/* We are now ready to create the Kernel Thread Object */
InitializeObjectAttributes(&ObjectAttributes,
NULL,
0,
NULL,
SecurityDescriptor);
Status = ZwCreateThread(&Handle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
ProcessHandle,
&ThreadCid,
&Context,
&InitialTeb,
CreateSuspended);
if (!NT_SUCCESS(Status))
{
/* Free the stack */
RtlpFreeUserStack(ProcessHandle, &InitialTeb);
}
else
{
/* Return thread data */
if (ThreadHandle)
*ThreadHandle = Handle;
else
NtClose(Handle);
if (ClientId) *ClientId = ThreadCid;
}
/* Return success or the previous failure */
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
RtlExitUserThread(NTSTATUS Status)
{
/* Call the Loader and tell him to notify the DLLs */
LdrShutdownThread();
/* Shut us down */
NtCurrentTeb()->FreeStackOnTermination = TRUE;
NtTerminateThread(NtCurrentThread(), Status);
}
/*
@implemented
*/
VOID
NTAPI
RtlFreeUserThreadStack(HANDLE ProcessHandle,
HANDLE ThreadHandle)
{
NTSTATUS Status;
THREAD_BASIC_INFORMATION ThreadBasicInfo;
SIZE_T Dummy, Size = 0;
PVOID StackLocation;
/* Query the Basic Info */
Status = NtQueryInformationThread(ThreadHandle,
ThreadBasicInformation,
&ThreadBasicInfo,
sizeof(THREAD_BASIC_INFORMATION),
NULL);
if (!NT_SUCCESS(Status) || !ThreadBasicInfo.TebBaseAddress) return;
/* Get the deallocation stack */
Status = NtReadVirtualMemory(ProcessHandle,
&((PTEB)ThreadBasicInfo.TebBaseAddress)->
DeallocationStack,
&StackLocation,
sizeof(PVOID),
&Dummy);
if (!NT_SUCCESS(Status) || !StackLocation) return;
/* Free it */
NtFreeVirtualMemory(ProcessHandle, &StackLocation, &Size, MEM_RELEASE);
}
PTEB
NTAPI
_NtCurrentTeb(VOID)
{
/* Return the TEB */
return NtCurrentTeb();
}
NTSTATUS
NTAPI
RtlRemoteCall(IN HANDLE Process,
IN HANDLE Thread,
IN PVOID CallSite,
IN ULONG ArgumentCount,
IN PULONG Arguments,
IN BOOLEAN PassContext,
IN BOOLEAN AlreadySuspended)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}