2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/kill.c
5 * PURPOSE: Thread Termination and Reaping
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS *******************************************************************/
19 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
21 PETHREAD PspReaperList
= NULL
;
22 WORK_QUEUE_ITEM PspReaperWorkItem
;
23 BOOLEAN PspReaping
= FALSE
;
24 extern LIST_ENTRY PsActiveProcessHead
;
25 extern FAST_MUTEX PspActiveProcessMutex
;
27 /* FUNCTIONS *****************************************************************/
31 PspReapRoutine(PVOID Context
)
34 PETHREAD Thread
, NewThread
;
37 DPRINT("Evil reaper running!!\n");
38 OldIrql
= KeAcquireDispatcherDatabaseLock();
40 /* Get the first Thread Entry */
41 Thread
= PspReaperList
;
43 DPRINT("PspReaperList: %x\n", Thread
);
45 /* Check to see if the list is empty */
48 /* Unlock the Dispatcher */
49 KeReleaseDispatcherDatabaseLock(OldIrql
);
51 /* Is there a thread on the list? */
54 /* Get the next Thread */
55 DPRINT("Thread: %x\n", Thread
);
56 DPRINT("Thread: %x\n", Thread
->ReaperLink
);
57 NewThread
= Thread
->ReaperLink
;
59 /* Remove reference to current thread */
60 ObDereferenceObject(Thread
);
62 /* Move to next Thread */
66 /* No more linked threads... Reacquire the Lock */
67 OldIrql
= KeAcquireDispatcherDatabaseLock();
69 /* Now try to get a new thread from the list */
70 Thread
= PspReaperList
;
72 DPRINT("PspReaperList: %x\n", Thread
);
74 /* Loop again if there is a new thread */
78 DPRINT("Done reaping\n");
79 KeReleaseDispatcherDatabaseLock(OldIrql
);
84 PspKillMostProcesses(VOID
)
86 PLIST_ENTRY current_entry
;
89 /* Acquire the Active Process Lock */
90 ExAcquireFastMutex(&PspActiveProcessMutex
);
92 /* Loop all processes on the list */
93 current_entry
= PsActiveProcessHead
.Flink
;
94 while (current_entry
!= &PsActiveProcessHead
)
96 current
= CONTAINING_RECORD(current_entry
, EPROCESS
, ProcessListEntry
);
97 current_entry
= current_entry
->Flink
;
99 if (current
->UniqueProcessId
!= PsInitialSystemProcess
->UniqueProcessId
&&
100 current
->UniqueProcessId
!= PsGetCurrentProcessId())
102 /* Terminate all the Threads in this Process */
103 PspTerminateProcessThreads(current
, STATUS_SUCCESS
);
107 /* Release the lock */
108 ExReleaseFastMutex(&PspActiveProcessMutex
);
113 PspTerminateProcessThreads(PEPROCESS Process
,
116 PLIST_ENTRY CurrentEntry
;
117 PETHREAD Thread
, CurrentThread
= PsGetCurrentThread();
119 CurrentEntry
= Process
->ThreadListHead
.Flink
;
120 while (CurrentEntry
!= &Process
->ThreadListHead
) {
122 /* Get the Current Thread */
123 Thread
= CONTAINING_RECORD(CurrentEntry
, ETHREAD
, ThreadListEntry
);
125 /* Move to the Next Thread */
126 CurrentEntry
= CurrentEntry
->Flink
;
128 /* Make sure it's not the one we're in */
129 if (Thread
!= CurrentThread
) {
131 /* Make sure it didn't already terminate */
132 if (!Thread
->HasTerminated
) {
134 Thread
->HasTerminated
= TRUE
;
136 /* Terminate it by APC */
137 PspTerminateThreadByPointer(Thread
, ExitStatus
);
145 PspDeleteProcess(PVOID ObjectBody
)
147 PEPROCESS Process
= (PEPROCESS
)ObjectBody
;
149 DPRINT("PiDeleteProcess(ObjectBody %x)\n",Process
);
151 /* Delete the CID Handle */
152 if(Process
->UniqueProcessId
!= NULL
) {
154 PsDeleteCidHandle(Process
->UniqueProcessId
, PsProcessType
);
158 KDB_DELETEPROCESS_HOOK(Process
);
160 /* Dereference the Token and release Memory Information */
161 ObDereferenceObject(Process
->Token
);
162 MmReleaseMmInfo(Process
);
164 /* Delete the W32PROCESS structure if there's one associated */
165 if(Process
->Win32Process
!= NULL
) ExFreePool(Process
->Win32Process
);
170 PspDeleteThread(PVOID ObjectBody
)
172 PETHREAD Thread
= (PETHREAD
)ObjectBody
;
173 PEPROCESS Process
= Thread
->ThreadsProcess
;
175 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody
, Thread
->ThreadsProcess
);
177 /* Deassociate the Process */
178 Thread
->ThreadsProcess
= NULL
;
180 /* Delete the CID Handle */
181 if(Thread
->Cid
.UniqueThread
!= NULL
) {
183 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
186 /* Free the W32THREAD structure if present */
187 if(Thread
->Tcb
.Win32Thread
!= NULL
) ExFreePool (Thread
->Tcb
.Win32Thread
);
189 /* Release the Thread */
190 KeReleaseThread(ETHREAD_TO_KTHREAD(Thread
));
192 /* Dereference the Process */
193 ObDereferenceObject(Process
);
197 * FUNCTION: Terminates the current thread
198 * See "Windows Internals" - Chapter 13, Page 50-53
202 PspExitThread(NTSTATUS ExitStatus
)
204 PETHREAD CurrentThread
;
206 PEPROCESS CurrentProcess
;
207 SIZE_T Length
= PAGE_SIZE
;
209 PTERMINATION_PORT TerminationPort
;
211 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus
, PsGetCurrentThread());
213 /* Get the Current Thread and Process */
214 CurrentThread
= PsGetCurrentThread();
215 CurrentProcess
= CurrentThread
->ThreadsProcess
;
217 /* Set the Exit Status and Exit Time */
218 CurrentThread
->ExitStatus
= ExitStatus
;
219 KeQuerySystemTime(&CurrentThread
->ExitTime
);
221 /* Can't terminate a thread if it attached another process */
222 if (KeIsAttachedProcess()) {
224 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
225 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
226 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
227 (ULONG
) CurrentThread
);
230 /* Lower to Passive Level */
231 KeLowerIrql(PASSIVE_LEVEL
);
233 /* Lock the Process before we modify its thread entries */
234 PsLockProcess(CurrentProcess
, FALSE
);
236 /* wake up the thread so we don't deadlock on PsLockProcess */
237 KeForceResumeThread(&CurrentThread
->Tcb
);
239 /* Run Thread Notify Routines before we desintegrate the thread */
240 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
242 /* Remove the thread from the thread list of its process */
243 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
244 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
246 /* Set the last Thread Exit Status */
247 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
251 /* Save the Exit Time if not already done by NtTerminateProcess. This
252 happens when the last thread just terminates without explicitly
253 terminating the process. */
254 CurrentProcess
->ExitTime
= CurrentThread
->ExitTime
;
257 /* Check if the process has a debug port */
258 if (CurrentProcess
->DebugPort
) {
260 /* Notify the Debug API. TODO */
261 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
264 /* Process the Termination Ports */
265 TerminationPort
= CurrentThread
->TerminationPort
;
266 DPRINT("TerminationPort: %p\n", TerminationPort
);
267 while (TerminationPort
) {
269 /* Send the LPC Message */
270 LpcSendTerminationPort(TerminationPort
->Port
, CurrentThread
->CreateTime
);
273 ExFreePool(TerminationPort
);
275 /* Get the next one */
276 TerminationPort
= TerminationPort
->Next
;
277 DPRINT("TerminationPort: %p\n", TerminationPort
);
280 /* Rundown Win32 Structures */
281 PsTerminateWin32Thread(CurrentThread
);
282 if (Last
) PsTerminateWin32Process(CurrentProcess
);
284 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
285 //CmNotifyRunDown(CurrentThread);
288 if(CurrentThread
->Tcb
.Teb
) {
290 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
291 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
293 ZwFreeVirtualMemory(NtCurrentProcess(),
294 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
298 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
300 if (TebBlock
!= CurrentProcess
->TebBlock
||
301 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
) {
303 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
304 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
305 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
308 CurrentThread
->Tcb
.Teb
= NULL
;
311 /* The last Thread shuts down the Process */
312 if (Last
) PspExitProcess(CurrentProcess
);
314 /* Unlock the Process */
315 PsUnlockProcess(CurrentProcess
);
317 /* Cancel I/O for the thread. */
318 IoCancelThreadIo(CurrentThread
);
322 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
324 /* If the Processor Control Block's NpxThread points to the current thread
327 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread
,
329 (PKPROCESS
)CurrentThread
);
331 /* Rundown Mutexes */
334 /* Terminate the Thread from the Scheduler */
335 KeTerminateThread(0);
336 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
342 PsExitSpecialApc(PKAPC Apc
,
343 PKNORMAL_ROUTINE
* NormalRoutine
,
344 PVOID
* NormalContext
,
345 PVOID
* SystemArgument1
,
346 PVOID
* SystemArguemnt2
)
348 NTSTATUS ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
350 DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n", PsGetCurrentThread(), PsGetCurrentProcess());
355 /* Terminate the Thread */
356 PspExitThread(ExitStatus
);
358 /* we should never reach this point! */
364 PspExitNormalApc(PVOID NormalContext
,
365 PVOID SystemArgument1
,
366 PVOID SystemArgument2
)
368 /* Not fully supported yet... must work out some issues that
369 * I don't understand yet -- Alex
372 PspExitThread((NTSTATUS
)NormalContext
);
374 /* we should never reach this point! */
379 * See "Windows Internals" - Chapter 13, Page 49
383 PspTerminateThreadByPointer(PETHREAD Thread
,
388 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
391 /* Check if we are already in the right context */
392 if (PsGetCurrentThread() == Thread
) {
394 /* Directly terminate the thread */
395 PspExitThread(ExitStatus
);
397 /* we should never reach this point! */
401 /* Allocate the APC */
402 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
404 /* Initialize a Kernel Mode APC to Kill the Thread */
407 OriginalApcEnvironment
,
414 /* Insert it into the APC Queue */
415 KeInsertQueueApc(Apc
,
420 /* Forcefully resume the thread */
421 KeForceResumeThread(&Thread
->Tcb
);
426 PspExitProcess(PEPROCESS Process
)
428 DPRINT("PspExitProcess 0x%x\n", Process
);
430 PspRunCreateProcessNotifyRoutines(Process
, FALSE
);
432 /* Remove it from the Active List */
433 ExAcquireFastMutex(&PspActiveProcessMutex
);
434 RemoveEntryList(&Process
->ProcessListEntry
);
435 ExReleaseFastMutex(&PspActiveProcessMutex
);
437 /* close all handles associated with our process, this needs to be done
438 when the last thread still runs */
439 ObKillProcess(Process
);
441 KeSetProcess(&Process
->Pcb
, IO_NO_INCREMENT
);
443 return(STATUS_SUCCESS
);
448 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
449 IN NTSTATUS ExitStatus
)
453 PETHREAD CurrentThread
;
454 BOOLEAN KillByHandle
;
458 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
459 ProcessHandle
, ExitStatus
);
461 KillByHandle
= (ProcessHandle
!= NULL
);
463 /* Get the Process Object */
464 Status
= ObReferenceObjectByHandle((KillByHandle
? ProcessHandle
: NtCurrentProcess()),
470 if (!NT_SUCCESS(Status
)) {
472 DPRINT1("Invalid handle to Process\n");
476 CurrentThread
= PsGetCurrentThread();
478 PsLockProcess(Process
, FALSE
);
480 if(Process
->ExitTime
.QuadPart
!= 0)
482 PsUnlockProcess(Process
);
483 return STATUS_PROCESS_IS_TERMINATING
;
486 /* Terminate all the Process's Threads */
487 PspTerminateProcessThreads(Process
, ExitStatus
);
489 /* only kill the calling thread if it either passed a process handle or
490 NtCurrentProcess() */
493 /* set the exit time as we're about to release the process lock before
494 we kill ourselves to prevent threads outside of our process trying
496 KeQuerySystemTime(&Process
->ExitTime
);
498 /* Only master thread remains... kill it off */
499 if (CurrentThread
->ThreadsProcess
== Process
) {
501 /* mark our thread as terminating so attempts to terminate it, when
502 unlocking the process, fail */
503 CurrentThread
->HasTerminated
= TRUE
;
505 PsUnlockProcess(Process
);
507 /* we can safely dereference the process because the current thread
508 holds a reference to it until it gets reaped */
509 ObDereferenceObject(Process
);
511 /* now the other threads get a chance to terminate, we don't wait but
512 just kill ourselves right now. The process will be run down when the
513 last thread terminates */
515 PspExitThread(ExitStatus
);
517 /* we should never reach this point! */
522 /* unlock and dereference the process so the threads can kill themselves */
523 PsUnlockProcess(Process
);
524 ObDereferenceObject(Process
);
526 return(STATUS_SUCCESS
);
531 NtTerminateThread(IN HANDLE ThreadHandle
,
532 IN NTSTATUS ExitStatus
)
539 /* Get the Thread Object */
540 Status
= ObReferenceObjectByHandle(ThreadHandle
,
546 if (!NT_SUCCESS(Status
)) {
548 DPRINT1("Could not reference thread object\n");
552 /* Make sure this is not a system thread */
553 if (PsIsSystemThread(Thread
)) {
555 DPRINT1("Trying to Terminate a system thread!\n");
556 ObDereferenceObject(Thread
);
557 return STATUS_INVALID_PARAMETER
;
560 /* Check to see if we're running in the same thread */
561 if (Thread
!= PsGetCurrentThread()) {
563 /* we need to lock the process to make sure it's not already terminating */
564 PsLockProcess(Thread
->ThreadsProcess
, FALSE
);
566 /* This isn't our thread, terminate it if not already done */
567 if (!Thread
->HasTerminated
) {
569 Thread
->HasTerminated
= TRUE
;
572 PspTerminateThreadByPointer(Thread
, ExitStatus
);
575 PsUnlockProcess(Thread
->ThreadsProcess
);
577 /* Dereference the Thread and return */
578 ObDereferenceObject(Thread
);
582 Thread
->HasTerminated
= TRUE
;
584 /* it's safe to dereference thread, there's at least the keep-alive
585 reference which will be removed by the thread reaper causing the
586 thread to be finally destroyed */
587 ObDereferenceObject(Thread
);
589 /* Terminate him, he's ours */
590 PspExitThread(ExitStatus
);
592 /* We do never reach this point */
596 return(STATUS_SUCCESS
);
604 PsTerminateSystemThread(NTSTATUS ExitStatus
)
606 PETHREAD Thread
= PsGetCurrentThread();
608 /* Make sure this is a system thread */
609 if (!PsIsSystemThread(Thread
)) {
611 DPRINT1("Trying to Terminate a non-system thread!\n");
612 return STATUS_INVALID_PARAMETER
;
615 /* Terminate it for real */
616 PspExitThread(ExitStatus
);
618 /* we should never reach this point! */
621 return(STATUS_SUCCESS
);
626 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
629 PTERMINATION_PORT TerminationPort
;
630 PVOID TerminationLpcPort
;
636 Status
= ObReferenceObjectByHandle(PortHandle
,
642 if (!NT_SUCCESS(Status
)) {
644 DPRINT1("Failed to reference Port\n");
648 /* Allocate the Port and make sure it suceeded */
649 if((TerminationPort
= ExAllocatePoolWithTag(NonPagedPool
,
650 sizeof(PTERMINATION_PORT
),
651 TAG('P', 's', 'T', '=')))) {
653 /* Associate the Port */
654 Thread
= PsGetCurrentThread();
655 TerminationPort
->Port
= TerminationLpcPort
;
656 DPRINT("TerminationPort: %p\n", TerminationPort
);
657 TerminationPort
->Next
= Thread
->TerminationPort
;
658 Thread
->TerminationPort
= TerminationPort
;
659 DPRINT("TerminationPort: %p\n", Thread
->TerminationPort
);
662 return(STATUS_SUCCESS
);
666 /* Dereference and Fail */
667 ObDereferenceObject(TerminationPort
);
668 return(STATUS_INSUFFICIENT_RESOURCES
);