1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152 |
- /*++
- Copyright (c) 2012 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- thread.c
- Abstract:
- This module implements support for threads in the kernel.
- Author:
- Evan Green 6-Aug-2012
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include "psp.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the number of times to try and get the thread list.
- //
- #define THREAD_LIST_TRIES 100
- //
- // Define the fudge factor to add to the reported allocation to account for
- // new threads sneaking in between calls.
- //
- #define THREAD_LIST_FUDGE_FACTOR 2
- //
- // Define the number of threads the reaper attempts to clean up in one pass.
- //
- #define THREAD_DEFAULT_REAP_COUNT 16
- //
- // Define the number of dead threads that are allowed to sit on the dead
- // threads list before thread creation starts to kick in helping to destroy
- // threads.
- //
- #define THREAD_CREATE_DEAD_THREAD_THRESHOLD 50
- //
- // Define the number of threads that thread creation will reap if the number of
- // dead threads has exceeded the threshold.
- //
- #define THREAD_CREATE_REAP_COUNT 2
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- PKTHREAD
- PspCreateThread (
- PKPROCESS OwningProcess,
- PVOID KernelStack,
- ULONG KernelStackSize,
- PTHREAD_ENTRY_ROUTINE ThreadRoutine,
- PVOID ThreadParameter,
- PCSTR Name,
- ULONG Flags
- );
- VOID
- PspReaperThread (
- PVOID Parameter
- );
- VOID
- PspReapThreads (
- UINTN ReapCount
- );
- VOID
- PspDestroyThread (
- PVOID ThreadObject
- );
- KSTATUS
- PspGetThreadList (
- PROCESS_ID ProcessId,
- PVOID Buffer,
- PULONG BufferSize
- );
- KSTATUS
- PspGetThreadInformation (
- PKTHREAD Thread,
- PTHREAD_INFORMATION Buffer,
- PULONG BufferSize
- );
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Globals related to thread manipulation.
- //
- //
- // Stores the ID for the next thread to be created.
- //
- volatile THREAD_ID PsNextThreadId = 0;
- //
- // Stores the list of exited threads waiting to be cleaned up.
- //
- KSPIN_LOCK PsDeadThreadsLock;
- LIST_ENTRY PsDeadThreadsListHead;
- UINTN PsDeadThreadsCount;
- PKEVENT PsDeadThreadsEvent;
- //
- // ------------------------------------------------------------------ Functions
- //
- KERNEL_API
- KSTATUS
- PsCreateKernelThread (
- PTHREAD_ENTRY_ROUTINE ThreadRoutine,
- PVOID ThreadParameter,
- PCSTR Name
- )
- /*++
- Routine Description:
- This routine creates and launches a new kernel thread with default
- parameters.
- Arguments:
- ThreadRoutine - Supplies the entry point to the thread.
- ThreadParameter - Supplies the parameter to pass to the entry point
- routine.
- Name - Supplies an optional name to identify the thread.
- Return Value:
- Returns a pointer to the new thread on success, or NULL on failure.
- --*/
- {
- THREAD_CREATION_PARAMETERS Parameters;
- RtlZeroMemory(&Parameters, sizeof(THREAD_CREATION_PARAMETERS));
- Parameters.Name = Name;
- if (Name != NULL) {
- Parameters.NameSize = RtlStringLength(Name) + 1;
- }
- Parameters.ThreadRoutine = ThreadRoutine;
- Parameters.Parameter = ThreadParameter;
- return PsCreateThread(&Parameters);
- }
- KERNEL_API
- KSTATUS
- PsCreateThread (
- PTHREAD_CREATION_PARAMETERS Parameters
- )
- /*++
- Routine Description:
- This routine creates and initializes a new thread, and adds it to the
- ready list for execution.
- Arguments:
- Parameters - Supplies a pointer to the thread creation parameters.
- Return Value:
- Status code.
- --*/
- {
- PKTHREAD CurrentThread;
- UINTN KernelStackSize;
- PKTHREAD NewThread;
- BOOL ParameterIsStack;
- KSTATUS Status;
- PPROCESS_ENVIRONMENT UserEnvironment;
- KernelStackSize = 0;
- if ((Parameters->Flags & THREAD_FLAG_USER_MODE) == 0) {
- KernelStackSize = Parameters->StackSize;
- }
- NewThread = PspCreateThread(Parameters->Process,
- NULL,
- KernelStackSize,
- Parameters->ThreadRoutine,
- Parameters->Parameter,
- Parameters->Name,
- Parameters->Flags);
- if (NewThread == NULL) {
- Status = STATUS_UNSUCCESSFUL;
- goto CreateThreadEnd;
- }
- if (Parameters->ThreadIdPointer != NULL) {
- if ((PVOID)(Parameters->ThreadIdPointer) >= KERNEL_VA_START) {
- *(Parameters->ThreadIdPointer) = NewThread->ThreadId;
- } else {
- //
- // Save the new ID to user mode, and remember this as the thread
- // ID pointer.
- //
- MmUserWrite32(Parameters->ThreadIdPointer, NewThread->ThreadId);
- NewThread->ThreadIdPointer = Parameters->ThreadIdPointer;
- }
- }
- ArSetThreadPointer(NewThread, Parameters->ThreadPointer);
- //
- // Copy the thread permissions and identity from the current thread.
- //
- CurrentThread = KeGetCurrentThread();
- Status = PspCopyThreadCredentials(NewThread, CurrentThread);
- if (!KSUCCESS(Status)) {
- goto CreateThreadEnd;
- }
- //
- // Create the user mode stack if needed.
- //
- ParameterIsStack = FALSE;
- if ((Parameters->Flags & THREAD_FLAG_USER_MODE) != 0) {
- if (Parameters->UserStack == NULL) {
- NewThread->Flags |= THREAD_FLAG_FREE_USER_STACK;
- if (Parameters->StackSize == 0) {
- Parameters->StackSize =
- NewThread->Limits[ResourceLimitStack].Current;
- }
- Status = PspSetThreadUserStackSize(NewThread,
- Parameters->StackSize);
- if (!KSUCCESS(Status)) {
- goto CreateThreadEnd;
- }
- Parameters->UserStack = NewThread->UserStack;
- } else {
- NewThread->UserStack = Parameters->UserStack;
- NewThread->UserStackSize = Parameters->StackSize;
- }
- //
- // Copy the signal mask from the current thread.
- //
- NewThread->BlockedSignals = CurrentThread->BlockedSignals;
- //
- // Set up the environment if there is one.
- //
- if (Parameters->Environment != NULL) {
- ParameterIsStack = TRUE;
- Parameters->Environment->StartData->StackBase =
- NewThread->UserStack;
- Status = PsCopyEnvironment(Parameters->Environment,
- &UserEnvironment,
- FALSE,
- NewThread,
- NULL,
- 0);
- if (!KSUCCESS(Status)) {
- goto CreateThreadEnd;
- }
- }
- }
- PspPrepareThreadForFirstRun(NewThread, NULL, ParameterIsStack);
- //
- // Insert the thread onto the ready list.
- //
- KeSetThreadReady(NewThread);
- Status = STATUS_SUCCESS;
- CreateThreadEnd:
- if (!KSUCCESS(Status)) {
- if (NewThread != NULL) {
- PspSetThreadUserStackSize(NewThread, 0);
- PspDestroyCredentials(NewThread);
- KeAcquireQueuedLock(NewThread->OwningProcess->QueuedLock);
- LIST_REMOVE(&(NewThread->ProcessEntry));
- NewThread->ProcessEntry.Next = NULL;
- NewThread->OwningProcess->ThreadCount -= 1;
- ASSERT(NewThread->OwningProcess->ThreadCount != 0);
- KeReleaseQueuedLock(NewThread->OwningProcess->QueuedLock);
- ObReleaseReference(NewThread);
- }
- }
- return Status;
- }
- KSTATUS
- PsGetThreadList (
- PROCESS_ID ProcessId,
- ULONG AllocationTag,
- PVOID *Buffer,
- PULONG BufferSize
- )
- /*++
- Routine Description:
- This routine returns information about the active processes in the system.
- Arguments:
- ProcessId - Supplies the identifier of the process to get thread
- information for.
- AllocationTag - Supplies the allocation tag to use for the allocation
- this routine will make on behalf of the caller.
- Buffer - Supplies a pointer where a non-paged pool buffer will be returned
- containing the array of thread information. The caller is responsible
- for freeing this memory from non-paged pool. The type returned here
- will be an array (where each element may be a different size) of
- THREAD_INFORMATION structures.
- BufferSize - Supplies a pointer where the size of the buffer in bytes
- will be returned on success.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_INSUFFICIENT_RESOURCES if memory could not be allocated for the
- information buffer.
- STATUS_BUFFER_TOO_SMALL if the thread list is so volatile that it cannot
- be sized. This is only returned in extremely rare cases, as this routine
- makes multiple attempts.
- --*/
- {
- PVOID Allocation;
- ULONG Size;
- KSTATUS Status;
- ULONG Try;
- Allocation = NULL;
- Size = 0;
- Status = STATUS_BUFFER_TOO_SMALL;
- for (Try = 0; Try < THREAD_LIST_TRIES; Try += 1) {
- Status = PspGetThreadList(ProcessId, NULL, &Size);
- if (!KSUCCESS(Status)) {
- goto GetThreadListEnd;
- }
- ASSERT(Size != 0);
- Size = Size * THREAD_LIST_FUDGE_FACTOR;
- Allocation = MmAllocateNonPagedPool(Size, AllocationTag);
- if (Allocation == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto GetThreadListEnd;
- }
- Status = PspGetThreadList(ProcessId, Allocation, &Size);
- if (KSUCCESS(Status)) {
- break;
- }
- MmFreeNonPagedPool(Allocation);
- Allocation = NULL;
- }
- GetThreadListEnd:
- if (!KSUCCESS(Status)) {
- if (Allocation != NULL) {
- MmFreeNonPagedPool(Allocation);
- Allocation = NULL;
- }
- Size = 0;
- }
- *Buffer = Allocation;
- *BufferSize = Size;
- return Status;
- }
- KSTATUS
- PsGetThreadInformation (
- PROCESS_ID ProcessId,
- THREAD_ID ThreadId,
- PTHREAD_INFORMATION Buffer,
- PULONG BufferSize
- )
- /*++
- Routine Description:
- This routine returns information about a given thread.
- Arguments:
- ProcessId - Supplies the process ID owning the thread.
- ThreadId - Supplies the ID of the thread to get information about.
- Buffer - Supplies an optional pointer to a buffer to write the data into.
- This must be non-paged memory if the thread requested belongs to the
- kernel process.
- BufferSize - Supplies a pointer that on input contains the size of the
- input buffer. On output, returns the size needed to contain the data.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_NO_SUCH_PROCESS if no process with the given identifier exists.
- STATUS_NO_SUCH_THREAD if the no thread with the given identifier exists.
- STATUS_BUFFER_TOO_SMALL if a buffer was supplied but was not big enough to
- contain all the information.
- --*/
- {
- PKPROCESS Process;
- KSTATUS Status;
- PKTHREAD Thread;
- Thread = NULL;
- Process = PspGetProcessById(ProcessId);
- if (Process == NULL) {
- Status = STATUS_NO_SUCH_PROCESS;
- goto GetThreadInformationEnd;
- }
- Thread = PspGetThreadById(Process, ThreadId);
- if (Thread == NULL) {
- Status = STATUS_NO_SUCH_THREAD;
- goto GetThreadInformationEnd;
- }
- Status = PspGetThreadInformation(Thread, Buffer, BufferSize);
- if (!KSUCCESS(Status)) {
- goto GetThreadInformationEnd;
- }
- GetThreadInformationEnd:
- if (Process != NULL) {
- ObReleaseReference(Process);
- }
- if (Thread != NULL) {
- ObReleaseReference(Thread);
- }
- return Status;
- }
- INTN
- PsSysCreateThread (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine creates a new thread for the current process.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- PKPROCESS CurrentProcess;
- PSTR Name;
- PSYSTEM_CALL_CREATE_THREAD Parameters;
- KSTATUS Status;
- THREAD_CREATION_PARAMETERS ThreadParameters;
- CurrentProcess = PsGetCurrentProcess();
- ASSERT(CurrentProcess != PsGetKernelProcess());
- Name = NULL;
- Parameters = (PSYSTEM_CALL_CREATE_THREAD)SystemCallParameter;
- if ((Parameters->Name != NULL) && (Parameters->NameBufferLength != 0)) {
- Status = MmCreateCopyOfUserModeString(Parameters->Name,
- Parameters->NameBufferLength,
- PS_ALLOCATION_TAG,
- &Name);
- if (!KSUCCESS(Status)) {
- goto SysCreateThreadEnd;
- }
- }
- //
- // Enable locking on the handle table, which will exist for the remainder
- // of the process lifetime.
- //
- Status = ObEnableHandleTableLocking(CurrentProcess->HandleTable);
- if (!KSUCCESS(Status)) {
- goto SysCreateThreadEnd;
- }
- //
- // Create and launch the thread.
- //
- RtlZeroMemory(&ThreadParameters, sizeof(THREAD_CREATION_PARAMETERS));
- ThreadParameters.Name = Parameters->Name;
- ThreadParameters.NameSize = Parameters->NameBufferLength;
- ThreadParameters.ThreadRoutine = Parameters->ThreadRoutine;
- ThreadParameters.Parameter = Parameters->Parameter;
- ThreadParameters.UserStack = Parameters->StackBase;
- ThreadParameters.StackSize = Parameters->StackSize;
- ThreadParameters.Flags = THREAD_FLAG_USER_MODE;
- ThreadParameters.ThreadPointer = Parameters->ThreadPointer;
- ThreadParameters.ThreadIdPointer = Parameters->ThreadId;
- if ((PVOID)(ThreadParameters.ThreadIdPointer) >= KERNEL_VA_START) {
- Status = STATUS_ACCESS_VIOLATION;
- goto SysCreateThreadEnd;
- }
- Status = PsCreateThread(&ThreadParameters);
- if (!KSUCCESS(Status)) {
- goto SysCreateThreadEnd;
- }
- //
- // Update the stack base and size on output.
- //
- Parameters->StackBase = ThreadParameters.UserStack;
- Parameters->StackSize = ThreadParameters.StackSize;
- //
- // Null out the name parameters as that memory is now owned by the object
- // manager.
- //
- Name = NULL;
- Status = STATUS_SUCCESS;
- SysCreateThreadEnd:
- if (Name != NULL) {
- MmFreePagedPool(Name);
- }
- return Status;
- }
- INTN
- PsSysExitThread (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine terminates the current thread.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- Return Value:
- Does not return. Eventually exits by killing the thread.
- --*/
- {
- PSYSTEM_CALL_EXIT_THREAD Parameters;
- PKTHREAD Thread;
- PVOID ThreadIdPointer;
- Parameters = SystemCallParameter;
- //
- // Before killing the thread, unmap a region if requested. This is used by
- // the user-modes thread library to clean up the stack for the thread that
- // just exited.
- //
- if ((Parameters->UnmapSize != 0) && (Parameters->UnmapAddress != NULL)) {
- //
- // Clear the thread ID pointer if it's in the unmap region. This saves
- // the C library a system call.
- //
- Thread = KeGetCurrentThread();
- ThreadIdPointer = Thread->ThreadIdPointer;
- if ((ThreadIdPointer >= Parameters->UnmapAddress) &&
- (ThreadIdPointer <
- Parameters->UnmapAddress + Parameters->UnmapSize)) {
- Thread->ThreadIdPointer = NULL;
- }
- MmUnmapFileSection(PsGetCurrentProcess(),
- Parameters->UnmapAddress,
- Parameters->UnmapSize,
- NULL);
- }
- PspThreadTermination();
- //
- // Execution should never get here.
- //
- ASSERT(FALSE);
- return STATUS_UNSUCCESSFUL;
- }
- INTN
- PsSysSetThreadPointer (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine sets the thread pointer for the current thread.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This is supplies the thread pointer directly, which is
- passed from user mode via a register.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- ArSetThreadPointer(KeGetCurrentThread(), SystemCallParameter);
- return STATUS_SUCCESS;
- }
- INTN
- PsSysSetThreadIdPointer (
- PVOID SystemCallParameter
- )
- /*++
- Routine Description:
- This routine sets the thread ID pointer for the current thread.
- Arguments:
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This is supplies the thread ID pointer directly, which
- is passed from user mode via a register.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- {
- PVOID Pointer;
- PKTHREAD Thread;
- Pointer = SystemCallParameter;
- Thread = KeGetCurrentThread();
- if (Pointer < USER_VA_END) {
- Thread->ThreadIdPointer = Pointer;
- //
- // As a convenience, also set the thread ID if the pointer is being set
- // to a new value. This is useful when the executable becomes
- // multithreaded and the main thread needs to catch up setting up a
- // thread structure.
- //
- if (Pointer != NULL) {
- MmUserWrite32(Pointer, Thread->ThreadId);
- }
- }
- return STATUS_SUCCESS;
- }
- VOID
- PsQueueThreadCleanup (
- PKTHREAD Thread
- )
- /*++
- Routine Description:
- This routine queues the work item that cleans up a dead thread. This routine
- must not be executed by the thread being destroyed! This routine must be
- called at dispatch level.
- Arguments:
- Thread - Supplies a pointer to the thread to clean up.
- Return Value:
- None.
- --*/
- {
- RUNLEVEL OldRunLevel;
- ASSERT(KeGetCurrentThread() != Thread);
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- KeAcquireSpinLock(&PsDeadThreadsLock);
- ASSERT(Thread->SchedulerEntry.ListEntry.Next == NULL);
- INSERT_AFTER(&(Thread->SchedulerEntry.ListEntry), &PsDeadThreadsListHead);
- PsDeadThreadsCount += 1;
- KeSignalEvent(PsDeadThreadsEvent, SignalOptionSignalAll);
- KeReleaseSpinLock(&PsDeadThreadsLock);
- KeLowerRunLevel(OldRunLevel);
- return;
- }
- KSTATUS
- PspSetThreadUserStackSize (
- PKTHREAD Thread,
- UINTN NewStackSize
- )
- /*++
- Routine Description:
- This routine changes the given thread's user mode stack size.
- Arguments:
- Thread - Supplies a pointer to the thread whose stack size should be
- changed.
- NewStackSize - Supplies the new stack size to set. If 0 is supplied, the
- user mode stack will be destroyed.
- Return Value:
- Status code.
- --*/
- {
- ULONG Flags;
- PVOID MaxMapRegion;
- ULONG PageSize;
- KSTATUS Status;
- VM_ALLOCATION_PARAMETERS VaRequest;
- PageSize = MmPageSize();
- if (NewStackSize > USER_STACK_MAX) {
- NewStackSize = USER_STACK_MAX;
- }
- NewStackSize = ALIGN_RANGE_UP(NewStackSize, PageSize);
- //
- // Shrink or destroy the stack if requested. This whole routine assumes the
- // stack grows down.
- //
- if (NewStackSize <= Thread->UserStackSize) {
- Status = STATUS_SUCCESS;
- if ((Thread->UserStackSize != 0) &&
- (NewStackSize != Thread->UserStackSize)) {
- Status = MmUnmapFileSection(Thread->OwningProcess,
- Thread->UserStack,
- Thread->UserStackSize - NewStackSize,
- NULL);
- if (NewStackSize != 0) {
- Thread->UserStack += Thread->UserStackSize - NewStackSize;
- }
- Thread->UserStackSize = NewStackSize;
- }
- if (NewStackSize == 0) {
- Thread->UserStack = NULL;
- Thread->UserStackSize = 0;
- }
- //
- // Create or grow the stack.
- //
- } else {
- VaRequest.Address = NULL;
- VaRequest.Size = NewStackSize;
- VaRequest.Strategy = AllocationStrategyHighestAddress;
- if (Thread->UserStackSize != 0) {
- VaRequest.Strategy = AllocationStrategyFixedAddress;
- VaRequest.Address = Thread->UserStack + Thread->UserStackSize -
- NewStackSize;
- if (VaRequest.Address > Thread->UserStack) {
- Status = STATUS_INTEGER_OVERFLOW;
- goto SetThreadUserStackSizeEnd;
- }
- VaRequest.Size = NewStackSize - Thread->UserStackSize;
- }
- ASSERT(Thread->UserStackSize == 0);
- ASSERT(Thread->UserStack == NULL);
- //
- // Check against the current resource limit.
- //
- if (NewStackSize > Thread->Limits[ResourceLimitStack].Current) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto SetThreadUserStackSizeEnd;
- }
- Flags = IMAGE_SECTION_READABLE | IMAGE_SECTION_WRITABLE;
- VaRequest.Alignment = 0;
- VaRequest.Min = 0;
- VaRequest.Max = MAX_ADDRESS;
- VaRequest.MemoryType = MemoryTypeReserved;
- Status = MmMapFileSection(INVALID_HANDLE,
- 0,
- &VaRequest,
- Flags,
- FALSE,
- NULL);
- if (!KSUCCESS(Status)) {
- goto SetThreadUserStackSizeEnd;
- }
- //
- // If this was the first time the stack size has been set, save the
- // upper limit for memory mapped regions. This gives the stack some
- // breathing room to grow.
- //
- if ((Thread->UserStackSize == 0) &&
- (Thread->OwningProcess->AddressSpace->MaxMemoryMap == 0)) {
- MaxMapRegion = VaRequest.Address;
- if (NewStackSize < USER_STACK_HEADROOM) {
- MaxMapRegion = VaRequest.Address + NewStackSize -
- USER_STACK_HEADROOM;
- }
- Thread->OwningProcess->AddressSpace->MaxMemoryMap = MaxMapRegion;
- }
- //
- // Don't free the stack for the first thread, as it contains the
- // environment and arguments. Do free it for all the other threads
- // that used the kernel to allocate a stack.
- //
- if (Thread->OwningProcess->ThreadCount > 1) {
- Thread->Flags |= THREAD_FLAG_FREE_USER_STACK;
- }
- Thread->UserStack = VaRequest.Address;
- Thread->UserStackSize = NewStackSize;
- Status = STATUS_SUCCESS;
- }
- SetThreadUserStackSizeEnd:
- return Status;
- }
- VOID
- PspKernelThreadStart (
- VOID
- )
- /*++
- Routine Description:
- This routine performs common initialization for all kernel mode threads, and
- executes the primary thread routine.
- Arguments:
- None.
- Return Value:
- Does not return. Eventually exits by killing the thread.
- --*/
- {
- PTHREAD_ENTRY_ROUTINE Entry;
- PKTHREAD Thread;
- //
- // Run the thread.
- //
- Thread = KeGetCurrentThread();
- Entry = Thread->ThreadRoutine;
- Entry(Thread->ThreadParameter);
- //
- // The thread returned, so exit.
- //
- PspThreadTermination();
- return;
- }
- KSTATUS
- PspInitializeThreadSupport (
- VOID
- )
- /*++
- Routine Description:
- This routine performs one-time system initialization for thread support.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- ASSERT(KeGetCurrentProcessorNumber() == 0);
- ASSERT(PsDeadThreadsCount == 0);
- KeInitializeSpinLock(&PsDeadThreadsLock);
- INITIALIZE_LIST_HEAD(&PsDeadThreadsListHead);
- PsDeadThreadsEvent = KeCreateEvent(NULL);
- if (PsDeadThreadsEvent == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeThreadSupportEnd;
- }
- //
- // Create the reaper thread.
- //
- Status = PsCreateKernelThread(PspReaperThread, NULL, "PspReaperThread");
- if (!KSUCCESS(Status)) {
- goto InitializeThreadSupportEnd;
- }
- Status = STATUS_SUCCESS;
- InitializeThreadSupportEnd:
- return Status;
- }
- PKTHREAD
- PspCloneThread (
- PKPROCESS DestinationProcess,
- PKTHREAD Thread,
- PVOID KernelStack,
- UINTN KernelStackSize,
- PTRAP_FRAME TrapFrame
- )
- /*++
- Routine Description:
- This routine clones a user mode thread from another process into the
- destination thread. This routine is designed to support the fork process
- system call.
- Arguments:
- DestinationProcess - Supplies a pointer to the process the new thread
- should be created under.
- Thread - Supplies a pointer to the thread to clone.
- KernelStack - Supplies a pointer to the kernel stack to use.
- KernelStackSize - Supplies the size of the supplied kernel stack in bytes.
- TrapFrame - Supplies a pointer to the trap frame to set initial thread
- state to. A copy of this trap frame will be made.
- Return Value:
- Returns a pointer to the new thread on success, or NULL on failure.
- --*/
- {
- PKTHREAD NewThread;
- KSTATUS Status;
- NewThread = PspCreateThread(DestinationProcess,
- KernelStack,
- KernelStackSize,
- Thread->ThreadRoutine,
- Thread->ThreadParameter,
- Thread->Header.Name,
- Thread->Flags & THREAD_FLAG_CREATION_MASK);
- if (NewThread == NULL) {
- Status = STATUS_UNSUCCESSFUL;
- goto CloneThreadEnd;
- }
- //
- // Copy the existing thread's credentials to the new thread.
- //
- Status = PspCopyThreadCredentials(NewThread, Thread);
- if (!KSUCCESS(Status)) {
- goto CloneThreadEnd;
- }
- Status = PspArchCloneThread(Thread, NewThread);
- if (!KSUCCESS(Status)) {
- goto CloneThreadEnd;
- }
- //
- // The user stack is presumed to be set up in the new process at the same
- // place.
- //
- NewThread->BlockedSignals = Thread->BlockedSignals;
- NewThread->UserStack = Thread->UserStack;
- NewThread->UserStackSize = Thread->UserStackSize;
- PspPrepareThreadForFirstRun(NewThread, TrapFrame, FALSE);
- NewThread->ThreadPointer = Thread->ThreadPointer;
- NewThread->ThreadIdPointer = Thread->ThreadIdPointer;
- //
- // Insert the thread onto the ready list.
- //
- KeSetThreadReady(NewThread);
- Status = STATUS_SUCCESS;
- CloneThreadEnd:
- if (!KSUCCESS(Status)) {
- if (NewThread != NULL) {
- ASSERT(NewThread->SupplementaryGroups == NULL);
- ObReleaseReference(NewThread);
- NewThread = NULL;
- }
- }
- return NewThread;
- }
- KSTATUS
- PspResetThread (
- PKTHREAD Thread,
- PTRAP_FRAME TrapFrame,
- PINTN ReturnValue
- )
- /*++
- Routine Description:
- This routine resets a user mode thread. It assumes that the user mode stack
- was freed out from under it, and sets up a new stack.
- Arguments:
- Thread - Supplies a pointer to the thread to reset. The thread must be a
- user mode thread. A new user mode stack will be allocated for it, the
- old one will not be freed. Commonly, this parameter will be a pointer
- to the currently running thread.
- TrapFrame - Supplies a pointer to the initial trap frame to reset the thread
- to.
- ReturnValue - Supplies a pointer that receives the value that the reset
- user mode thread should return when exiting back to user mode.
- Return Value:
- Status code.
- --*/
- {
- PPROCESS_ENVIRONMENT Environment;
- KSTATUS Status;
- PPROCESS_ENVIRONMENT UserEnvironment;
- //
- // Create the user mode stack.
- //
- ASSERT((Thread->Flags & THREAD_FLAG_USER_MODE) != 0);
- Thread->ThreadIdPointer = NULL;
- Status = PspSetThreadUserStackSize(
- Thread,
- Thread->Limits[ResourceLimitStack].Current);
- if (!KSUCCESS(Status)) {
- goto CreateThreadEnd;
- }
- Thread->ThreadParameter = NULL;
- Environment = Thread->OwningProcess->Environment;
- Environment->StartData->StackBase = Thread->UserStack;
- Status = PsCopyEnvironment(Environment,
- &UserEnvironment,
- FALSE,
- Thread,
- NULL,
- 0);
- if (!KSUCCESS(Status)) {
- goto CreateThreadEnd;
- }
- *ReturnValue = PspArchResetThreadContext(Thread, TrapFrame);
- Status = STATUS_SUCCESS;
- CreateThreadEnd:
- if (!KSUCCESS(Status)) {
- PspSetThreadUserStackSize(Thread, 0);
- }
- return Status;
- }
- PKTHREAD
- PspGetThreadById (
- PKPROCESS Process,
- THREAD_ID ThreadId
- )
- /*++
- Routine Description:
- This routine returns the thread with the given thread ID under the given
- process. This routine also increases the reference count of the returned
- thread.
- Arguments:
- Process - Supplies a pointer to the process to search under.
- ThreadId - Supplies the thread ID to search for.
- Return Value:
- Returns a pointer to the thread with the corresponding ID. The reference
- count will be increased by one.
- NULL if no such thread could be found.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PKTHREAD FoundThread;
- PKTHREAD Thread;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- FoundThread = NULL;
- KeAcquireQueuedLock(Process->QueuedLock);
- CurrentEntry = Process->ThreadListHead.Next;
- while (CurrentEntry != &(Process->ThreadListHead)) {
- Thread = LIST_VALUE(CurrentEntry, KTHREAD, ProcessEntry);
- if (Thread->ThreadId == ThreadId) {
- FoundThread = Thread;
- ObAddReference(FoundThread);
- break;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- KeReleaseQueuedLock(Process->QueuedLock);
- return FoundThread;
- }
- VOID
- PspThreadTermination (
- VOID
- )
- /*++
- Routine Description:
- This routine is called when a thread finishes execution, it performs some
- cleanup and calls the scheduler to exit the thread. This routine runs in
- the context of the thread itself.
- Arguments:
- None.
- Return Value:
- Does not return. Eventually exits by killing the thread.
- --*/
- {
- PFPU_CONTEXT FpuContext;
- BOOL LastThread;
- PKPROCESS Process;
- PKTHREAD Thread;
- SYSTEM_CALL_USER_LOCK WakeOperation;
- LastThread = FALSE;
- Thread = KeGetCurrentThread();
- Process = Thread->OwningProcess;
- //
- // Mark that the thread is exiting so that it does not get chosen for any
- // new signals.
- //
- Thread->Flags |= THREAD_FLAG_EXITING;
- //
- // Free the user mode stack before decrementing the thread count.
- //
- PspSetThreadUserStackSize(Thread, 0);
- //
- // Decrement the thread count. If this is the last thread, unload all
- // images in the process.
- //
- KeAcquireQueuedLock(Process->QueuedLock);
- ASSERT((Process->ThreadCount != 0) && (Process->ThreadCount < 0x10000000));
- Process->ThreadCount -= 1;
- if (Process->ThreadCount == 0) {
- //
- // The last thread shouldn't be exiting without having first set the
- // exit flags.
- //
- ASSERT((Process == PsGetKernelProcess()) || (Process->ExitReason != 0));
- LastThread = TRUE;
- }
- //
- // If a stop was requested and this thread happened to be the last one
- // being waited for, signal the all stopped event.
- //
- if ((Process->DebugData != NULL) &&
- (Process->ThreadCount != 0) &&
- (Process->StoppedThreadCount == Process->ThreadCount)) {
- KeSignalEvent(Process->DebugData->AllStoppedEvent,
- SignalOptionSignalAll);
- }
- //
- // The thread may have been responsible for dispatching some signals. Pass
- // those on to other threads.
- //
- PspCleanupThreadSignals();
- KeReleaseQueuedLock(Process->QueuedLock);
- //
- // Wake any threads waiting on the thread ID address.
- //
- if ((LastThread == FALSE) && (Thread->ThreadIdPointer != NULL)) {
- ASSERT((PVOID)(Thread->ThreadIdPointer) < USER_VA_END);
- ASSERT(sizeof(THREAD_ID) == sizeof(ULONG));
- MmUserWrite32(Thread->ThreadIdPointer, 0);
- WakeOperation.Address = (PULONG)(Thread->ThreadIdPointer);
- WakeOperation.Value = 1;
- WakeOperation.Operation = UserLockWake;
- WakeOperation.TimeoutInMilliseconds = 0;
- PspUserLockWake(&WakeOperation);
- }
- PspDestroyCredentials(Thread);
- //
- // Free up the FPU context. The thread could still get context swapped here,
- // which is why it's NULLed and then freed. The context swap code watches
- // out for this case where the using FPU flag is set but the context is
- // gone.
- //
- FpuContext = Thread->FpuContext;
- if (FpuContext != NULL) {
- Thread->FpuContext = NULL;
- ArDestroyFpuContext(FpuContext);
- }
- //
- // If this was the last thread in the process, clean up the dying process.
- //
- if (LastThread != FALSE) {
- PspProcessTermination(Process);
- }
- KeRaiseRunLevel(RunLevelDispatch);
- KeSchedulerEntry(SchedulerReasonThreadExiting);
- //
- // Execution should never get here.
- //
- KeCrashSystem(CRASH_THREAD_ERROR,
- (UINTN)Thread,
- Thread->State,
- 0,
- 0);
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- PKTHREAD
- PspCreateThread (
- PKPROCESS OwningProcess,
- PVOID KernelStack,
- ULONG KernelStackSize,
- PTHREAD_ENTRY_ROUTINE ThreadRoutine,
- PVOID ThreadParameter,
- PCSTR Name,
- ULONG Flags
- )
- /*++
- Routine Description:
- This routine creates and initializes a new thread. It will not create a
- user mode stack.
- Arguments:
- OwningProcess - Supplies a pointer to the process responsible for creating
- this thread.
- KernelStack - Supplies an optional pointer to a kernel stack. If NULL is
- supplied, then one will be created.
- KernelStackSize - Supplies the initial size of the kernel mode stack, in
- bytes. Supply 0 to use a default size.
- ThreadRoutine - Supplies the entry point to thread.
- ThreadParameter - Supplies the parameter to pass to the entry point routine.
- Name - Supplies an optional name to identify the thread.
- Flags - Supplies a set of flags governing the behavior and characteristics
- of the thread. See THREAD_FLAG_* definitions.
- Return Value:
- Returns a pointer to the new thread on success, or NULL on failure.
- --*/
- {
- PKTHREAD CurrentThread;
- ULONG NameLength;
- PKTHREAD NewThread;
- ULONG ObjectFlags;
- KSTATUS Status;
- BOOL UserMode;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT((Flags & ~THREAD_FLAG_CREATION_MASK) == 0);
- //
- // Before creating a new thread, make sure there aren't too many dead
- // threads hanging around. If there are dead threads, attempt to help the
- // system out be reaping some before creating a new thread.
- //
- if (PsDeadThreadsCount > THREAD_CREATE_DEAD_THREAD_THRESHOLD) {
- PspReapThreads(THREAD_CREATE_REAP_COUNT);
- }
- CurrentThread = KeGetCurrentThread();
- UserMode = FALSE;
- if ((Flags & THREAD_FLAG_USER_MODE) != 0) {
- UserMode = TRUE;
- }
- if (KernelStackSize == 0) {
- ASSERT(KernelStack == NULL);
- KernelStackSize = DEFAULT_KERNEL_STACK_SIZE;
- }
- if (OwningProcess == NULL) {
- OwningProcess = CurrentThread->OwningProcess;
- if (UserMode == FALSE) {
- OwningProcess = PsKernelProcess;
- }
- }
- ASSERT((ThreadRoutine == NULL) ||
- ((UserMode == FALSE) && ((PVOID)ThreadRoutine >= KERNEL_VA_START)) ||
- ((UserMode != FALSE) && ((PVOID)ThreadRoutine < USER_VA_END)));
- NameLength = 0;
- if (Name != NULL) {
- NameLength = RtlStringLength(Name) + 1;
- }
- //
- // Allocate the new thread's structure.
- //
- ObjectFlags = OBJECT_FLAG_USE_NAME_DIRECTLY;
- NewThread = ObCreateObject(ObjectThread,
- OwningProcess,
- Name,
- NameLength,
- sizeof(KTHREAD),
- PspDestroyThread,
- ObjectFlags,
- PS_ALLOCATION_TAG);
- if (NewThread == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateThreadEnd;
- }
- INITIALIZE_LIST_HEAD(&(NewThread->SignalListHead));
- NewThread->OwningProcess = OwningProcess;
- NewThread->State = ThreadStateFirstTime;
- NewThread->KernelStackSize = KernelStackSize;
- NewThread->ThreadRoutine = ThreadRoutine;
- NewThread->ThreadParameter = ThreadParameter;
- NewThread->Flags = Flags;
- NewThread->SignalPending = ThreadNoSignalPending;
- NewThread->SchedulerEntry.Type = SchedulerEntryThread;
- NewThread->SchedulerEntry.Parent = CurrentThread->SchedulerEntry.Parent;
- NewThread->ThreadPointer = PsInitialThreadPointer;
- //
- // Allocate a kernel stack unless one was provided. Touch the top level
- // page directory to ensure that the stack can be switched onto when it's
- // time to run this thread.
- //
- NewThread->KernelStack = KernelStack;
- if (KernelStack == NULL) {
- NewThread->KernelStack = MmAllocateKernelStack(KernelStackSize);
- if (NewThread->KernelStack == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateThreadEnd;
- }
- //
- // Touch the top level table even if this is the owning process and
- // current process are the same. The allocated stack may have been
- // cached, but still created after this process was started.
- //
- MmUpdatePageDirectory(OwningProcess->AddressSpace,
- NewThread->KernelStack,
- KernelStackSize);
- }
- //
- // Create a timer to be used for most operations that can time out.
- //
- NewThread->BuiltinTimer = KeCreateTimer(PS_ALLOCATION_TAG);
- if (NewThread->BuiltinTimer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateThreadEnd;
- }
- //
- // Create a built in wait block for the thread.
- //
- NewThread->BuiltinWaitBlock = ObCreateWaitBlock(0);
- if (NewThread->BuiltinWaitBlock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateThreadEnd;
- }
- //
- // Give the thread a unique ID.
- //
- NewThread->ThreadId = RtlAtomicAdd32((PULONG)&PsNextThreadId, 1);
- //
- // Add the thread to the process.
- //
- KeAcquireQueuedLock(OwningProcess->QueuedLock);
- INSERT_BEFORE(&(NewThread->ProcessEntry), &(OwningProcess->ThreadListHead));
- OwningProcess->ThreadCount += 1;
- KeReleaseQueuedLock(OwningProcess->QueuedLock);
- SpProcessNewThread(OwningProcess->Identifiers.ProcessId,
- NewThread->ThreadId);
- Status = STATUS_SUCCESS;
- CreateThreadEnd:
- if (!KSUCCESS(Status)) {
- if (NewThread != NULL) {
- if (KernelStack != NULL) {
- NewThread->KernelStack = NULL;
- NewThread->KernelStackSize = 0;
- }
- ObReleaseReference(NewThread);
- NewThread = NULL;
- }
- }
- return NewThread;
- }
- VOID
- PspReaperThread (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine waits on the dead thread event and when the event is signaled
- it attempts to reap the default number of threads until the event is no
- longer signaled.
- Arguments:
- Parameter - Supplies a pointer supplied by the creator of the thread. This
- parameter is not used.
- Return Value:
- None. This thread never exits.
- --*/
- {
- while (TRUE) {
- KeWaitForEvent(PsDeadThreadsEvent, FALSE, WAIT_TIME_INDEFINITE);
- PspReapThreads(THREAD_DEFAULT_REAP_COUNT);
- }
- return;
- }
- VOID
- PspReapThreads (
- UINTN TargetReapCount
- )
- /*++
- Routine Description:
- This routine checks for any threads that need to be cleaned up, dequeues
- them, and then frees the threads and all associated memory. This routine
- runs at low level.
- Arguments:
- TargetReapCount - Supplies the maximum number of threads to reap.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- LIST_ENTRY ListHead;
- RUNLEVEL OldRunLevel;
- UINTN ReapCount;
- PKTHREAD Thread;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- //
- // Acquire the lock and move up to the requested number of threads to the
- // local list.
- //
- ReapCount = 0;
- INITIALIZE_LIST_HEAD(&ListHead);
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- KeAcquireSpinLock(&PsDeadThreadsLock);
- while ((LIST_EMPTY(&PsDeadThreadsListHead) == FALSE) &&
- (ReapCount < TargetReapCount)) {
- CurrentEntry = PsDeadThreadsListHead.Next;
- LIST_REMOVE(CurrentEntry);
- INSERT_BEFORE(CurrentEntry, &ListHead);
- ReapCount += 1;
- }
- PsDeadThreadsCount -= ReapCount;
- //
- // Only unsignal the event if there are no threads left to reap.
- //
- if (LIST_EMPTY(&PsDeadThreadsListHead) != FALSE) {
- KeSignalEvent(PsDeadThreadsEvent, SignalOptionUnsignal);
- }
- KeReleaseSpinLock(&PsDeadThreadsLock);
- KeLowerRunLevel(OldRunLevel);
- //
- // Now that execution is running back at passive, calmly walk the local
- // list, signal anyone waiting on the thread exiting, and destroy the
- // threads.
- //
- while (LIST_EMPTY(&ListHead) == FALSE) {
- Thread = LIST_VALUE(ListHead.Next,
- KTHREAD,
- SchedulerEntry.ListEntry);
- LIST_REMOVE(&(Thread->SchedulerEntry.ListEntry));
- Thread->SchedulerEntry.ListEntry.Next = NULL;
- //
- // Remove the thread from the process before the reference count
- // drops to zero so that acquiring the process lock and adding
- // a reference synchronizes against the thread destroying itself
- // during or after that process lock is released.
- //
- KeAcquireQueuedLock(Thread->OwningProcess->QueuedLock);
- LIST_REMOVE(&(Thread->ProcessEntry));
- Thread->ProcessEntry.Next = NULL;
- //
- // The thread has been removed from the process's thread list. Add
- // its resource usage to the process' counts. This is where a process'
- // max resident set is updated (by setting it in the thread and then
- // updating to the parent).
- //
- Thread->ResourceUsage.MaxResidentSet =
- Thread->OwningProcess->AddressSpace->MaxResidentSet;
- PspAddResourceUsages(&(Thread->OwningProcess->ResourceUsage),
- &(Thread->ResourceUsage));
- KeReleaseQueuedLock(Thread->OwningProcess->QueuedLock);
- //
- // Signal everyone waiting on the thread to die.
- //
- ObSignalObject(Thread, SignalOptionSignalAll);
- ObReleaseReference(Thread);
- }
- return;
- }
- VOID
- PspDestroyThread (
- PVOID ThreadObject
- )
- /*++
- Routine Description:
- This routine frees all memory associated with a thread. It is assumed that
- the thread has already been unlinked from any queues or ready lists.
- Arguments:
- ThreadObject - Supplies the thread to free.
- Return Value:
- None.
- --*/
- {
- BOOL DestroyProcess;
- BOOL LastThread;
- PKPROCESS Process;
- BOOL SignalQueued;
- PSIGNAL_QUEUE_ENTRY SignalQueueEntry;
- PKTHREAD Thread;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeGetCurrentThread() != ThreadObject);
- Thread = (PKTHREAD)ThreadObject;
- ASSERT((Thread->State == ThreadStateExited) ||
- (Thread->State == ThreadStateFirstTime));
- ASSERT(Thread->Header.ReferenceCount == 0);
- ASSERT(Thread->ProcessEntry.Next == NULL);
- ASSERT(Thread->SupplementaryGroups == NULL);
- //
- // Clean up any queued signals that snuck on while the thread was dying.
- //
- while (LIST_EMPTY(&(Thread->SignalListHead)) == FALSE) {
- SignalQueueEntry = LIST_VALUE(Thread->SignalListHead.Next,
- SIGNAL_QUEUE_ENTRY,
- ListEntry);
- LIST_REMOVE(&(SignalQueueEntry->ListEntry));
- SignalQueueEntry->ListEntry.Next = NULL;
- if (SignalQueueEntry->CompletionRoutine != NULL) {
- SignalQueueEntry->CompletionRoutine(SignalQueueEntry);
- }
- }
- DestroyProcess = FALSE;
- //
- // Destroy the built in timer.
- //
- if (Thread->BuiltinTimer != NULL) {
- KeDestroyTimer(Thread->BuiltinTimer);
- }
- //
- // Destroy the built in wait block.
- //
- if (Thread->BuiltinWaitBlock != NULL) {
- ObDestroyWaitBlock(Thread->BuiltinWaitBlock);
- }
- Process = Thread->OwningProcess;
- //
- // If the thread never got a chance to run, remove it from the owning
- // process's list and if this is the last thread, make sure the process has
- // an exit status before proceeding.
- //
- if (Thread->State == ThreadStateFirstTime) {
- LastThread = FALSE;
- if (Thread->ProcessEntry.Next != NULL) {
- KeAcquireQueuedLock(Process->QueuedLock);
- LIST_REMOVE(&(Thread->ProcessEntry));
- Process->ThreadCount -= 1;
- if (Process->ThreadCount == 0) {
- LastThread = TRUE;
- }
- KeReleaseQueuedLock(Process->QueuedLock);
- Thread->ProcessEntry.Next = NULL;
- } else if (Process->ThreadCount == 0) {
- LastThread = TRUE;
- }
- if ((LastThread != FALSE) && (Process->ExitReason == 0)) {
- PspSetProcessExitStatus(Process,
- CHILD_SIGNAL_REASON_KILLED,
- SIGNAL_ABORT);
- }
- }
- if (LIST_EMPTY(&(Process->ThreadListHead)) != FALSE) {
- DestroyProcess = TRUE;
- }
- //
- // Free the kernel stack.
- //
- if (Thread->KernelStack != NULL) {
- MmFreeKernelStack(Thread->KernelStack, Thread->KernelStackSize);
- Thread->KernelStack = NULL;
- }
- //
- // Remove the thread from its scheduling group.
- //
- if (Thread->State != ThreadStateFirstTime) {
- KeUnlinkSchedulerEntry(&(Thread->SchedulerEntry));
- }
- //
- // Potentially clean up the process if the last thread just exited. This
- // will clean up all blocked signals.
- //
- if (DestroyProcess != FALSE) {
- //
- // Send the child signal to the parent.
- //
- SignalQueued = PspQueueChildSignalToParent(Process,
- Process->ExitStatus,
- Process->ExitReason);
- ObSignalObject(Process, SignalOptionSignalAll);
- //
- // If the parent was not signaled, then just remove the process from
- // the global list.
- //
- if (SignalQueued == FALSE) {
- PspRemoveProcessFromLists(Process);
- }
- //
- // Clean up any queued signals that snuck on while the process was
- // dying.
- //
- while (LIST_EMPTY(&(Process->SignalListHead)) == FALSE) {
- SignalQueueEntry = LIST_VALUE(Process->SignalListHead.Next,
- SIGNAL_QUEUE_ENTRY,
- ListEntry);
- LIST_REMOVE(&(SignalQueueEntry->ListEntry));
- SignalQueueEntry->ListEntry.Next = NULL;
- if (SignalQueueEntry->CompletionRoutine != NULL) {
- SignalQueueEntry->CompletionRoutine(SignalQueueEntry);
- }
- }
- //
- // Also clean up any blocked signals.
- //
- while (LIST_EMPTY(&(Process->UnreapedChildList)) == FALSE) {
- SignalQueueEntry = LIST_VALUE(Process->UnreapedChildList.Next,
- SIGNAL_QUEUE_ENTRY,
- ListEntry);
- ASSERT(IS_CHILD_SIGNAL(SignalQueueEntry));
- LIST_REMOVE(&(SignalQueueEntry->ListEntry));
- SignalQueueEntry->ListEntry.Next = NULL;
- if (SignalQueueEntry->CompletionRoutine != NULL) {
- SignalQueueEntry->CompletionRoutine(SignalQueueEntry);
- }
- }
- }
- return;
- }
- KSTATUS
- PspGetThreadList (
- PROCESS_ID ProcessId,
- PVOID Buffer,
- PULONG BufferSize
- )
- /*++
- Routine Description:
- This routine returns information about the threads in a given process.
- Arguments:
- ProcessId - Supplies the ID of the process to get thread information for.
- Buffer - Supplies an optional pointer to a buffer to write the data into.
- BufferSize - Supplies a pointer that on input contains the size of the
- input buffer. On output, returns the size needed to contain the data.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_NO_SUCH_PROCESS if the supplied process ID does not correspond to
- any active process.
- STATUS_BUFFER_TOO_SMALL if a buffer was supplied but was not big enough to
- contain all the information.
- --*/
- {
- PKPROCESS Process;
- ULONG RemainingSize;
- ULONG Size;
- KSTATUS Status;
- PKTHREAD Thread;
- PLIST_ENTRY ThreadEntry;
- ULONG ThreadSize;
- KSTATUS ThreadStatus;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Process = PspGetProcessById(ProcessId);
- if (Process == NULL) {
- return STATUS_NO_SUCH_PROCESS;
- }
- RemainingSize = *BufferSize;
- Size = 0;
- Status = STATUS_SUCCESS;
- KeAcquireQueuedLock(Process->QueuedLock);
- ThreadEntry = Process->ThreadListHead.Next;
- while (ThreadEntry != &(Process->ThreadListHead)) {
- Thread = LIST_VALUE(ThreadEntry, KTHREAD, ProcessEntry);
- ThreadEntry = ThreadEntry->Next;
- ThreadSize = RemainingSize;
- ThreadStatus = PspGetThreadInformation(Thread, Buffer, &ThreadSize);
- if (!KSUCCESS(ThreadStatus)) {
- Status = ThreadStatus;
- } else if (RemainingSize >= ThreadSize) {
- Buffer += ThreadSize;
- RemainingSize -= ThreadSize;
- }
- Size += ThreadSize;
- }
- KeReleaseQueuedLock(Process->QueuedLock);
- ObReleaseReference(Process);
- *BufferSize = Size;
- return Status;
- }
- KSTATUS
- PspGetThreadInformation (
- PKTHREAD Thread,
- PTHREAD_INFORMATION Buffer,
- PULONG BufferSize
- )
- /*++
- Routine Description:
- This routine returns information about a given thread.
- Arguments:
- Thread - Supplies a pointer to the thread.
- Buffer - Supplies an optional pointer to a buffer to write the data into.
- BufferSize - Supplies a pointer that on input contains the size of the
- input buffer. On output, returns the size needed to contain the data.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_BUFFER_TOO_SMALL if a buffer was supplied but was not big enough to
- contain all the information.
- --*/
- {
- KSTATUS Status;
- ULONG ThreadSize;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- Status = STATUS_SUCCESS;
- ThreadSize = sizeof(THREAD_INFORMATION);
- if (Thread->Header.NameLength != 0) {
- ThreadSize += Thread->Header.NameLength -
- (ANYSIZE_ARRAY * sizeof(CHAR));
- }
- if (*BufferSize >= ThreadSize) {
- Buffer->StructureSize = ThreadSize;
- Buffer->ThreadId = Thread->ThreadId;
- PspGetThreadResourceUsage(Thread, &(Buffer->ResourceUsage));
- Buffer->Name[0] = '\0';
- if (Thread->Header.NameLength != 0) {
- RtlStringCopy(Buffer->Name,
- Thread->Header.Name,
- Thread->Header.NameLength);
- }
- } else if (Buffer != NULL) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- *BufferSize = ThreadSize;
- return Status;
- }
|