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 PETHREAD PspReaperList
= NULL
;
20 WORK_QUEUE_ITEM PspReaperWorkItem
;
21 BOOLEAN PspReaping
= FALSE
;
22 extern LIST_ENTRY PsActiveProcessHead
;
23 extern FAST_MUTEX PspActiveProcessMutex
;
25 /* FUNCTIONS *****************************************************************/
29 PspReapRoutine(PVOID Context
)
32 PETHREAD Thread
, NewThread
;
35 DPRINT("Evil reaper running!!\n");
36 OldIrql
= KeAcquireDispatcherDatabaseLock();
38 /* Get the first Thread Entry */
39 Thread
= PspReaperList
;
41 DPRINT("PspReaperList: %x\n", Thread
);
43 /* Check to see if the list is empty */
46 /* Unlock the Dispatcher */
47 KeReleaseDispatcherDatabaseLock(OldIrql
);
49 /* Is there a thread on the list? */
52 /* Get the next Thread */
53 DPRINT("Thread: %x\n", Thread
);
54 DPRINT("Thread: %x\n", Thread
->ReaperLink
);
55 NewThread
= Thread
->ReaperLink
;
57 /* Remove reference to current thread */
58 ObDereferenceObject(Thread
);
60 /* Move to next Thread */
64 /* No more linked threads... Reacquire the Lock */
65 OldIrql
= KeAcquireDispatcherDatabaseLock();
67 /* Now try to get a new thread from the list */
68 Thread
= PspReaperList
;
70 DPRINT("PspReaperList: %x\n", Thread
);
72 /* Loop again if there is a new thread */
76 DPRINT("Done reaping\n");
77 KeReleaseDispatcherDatabaseLock(OldIrql
);
82 PspKillMostProcesses(VOID
)
84 PLIST_ENTRY current_entry
;
87 ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess
->UniqueProcessId
);
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
, ActiveProcessLinks
);
97 current_entry
= current_entry
->Flink
;
99 if (current
->UniqueProcessId
!= PsInitialSystemProcess
->UniqueProcessId
)
101 /* Terminate all the Threads in this Process */
102 PspTerminateProcessThreads(current
, STATUS_SUCCESS
);
106 /* Release the lock */
107 ExReleaseFastMutex(&PspActiveProcessMutex
);
112 PspTerminateProcessThreads(PEPROCESS Process
,
115 PLIST_ENTRY CurrentEntry
;
116 PETHREAD Thread
, CurrentThread
= PsGetCurrentThread();
118 CurrentEntry
= Process
->ThreadListHead
.Flink
;
119 while (CurrentEntry
!= &Process
->ThreadListHead
) {
121 /* Get the Current Thread */
122 Thread
= CONTAINING_RECORD(CurrentEntry
, ETHREAD
, ThreadListEntry
);
124 /* Move to the Next Thread */
125 CurrentEntry
= CurrentEntry
->Flink
;
127 /* Make sure it's not the one we're in */
128 if (Thread
!= CurrentThread
) {
130 /* Make sure it didn't already terminate */
131 if (!Thread
->Terminated
) {
133 Thread
->Terminated
= TRUE
;
135 /* Terminate it by APC */
136 PspTerminateThreadByPointer(Thread
, ExitStatus
);
144 PspDeleteProcess(PVOID ObjectBody
)
146 PEPROCESS Process
= (PEPROCESS
)ObjectBody
;
148 DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody
);
150 /* Remove it from the Active List */
151 ExAcquireFastMutex(&PspActiveProcessMutex
);
152 RemoveEntryList(&Process
->ActiveProcessLinks
);
153 ExReleaseFastMutex(&PspActiveProcessMutex
);
155 /* Delete the CID Handle */
156 if(Process
->UniqueProcessId
!= NULL
) {
158 PsDeleteCidHandle(Process
->UniqueProcessId
, PsProcessType
);
162 KDB_DELETEPROCESS_HOOK(Process
);
164 /* Dereference the Token */
165 SeDeassignPrimaryToken(Process
);
167 /* Release Memory Information */
168 MmReleaseMmInfo(Process
);
170 /* Delete the W32PROCESS structure if there's one associated */
171 if(Process
->Win32Process
!= NULL
) ExFreePool(Process
->Win32Process
);
176 PspDeleteThread(PVOID ObjectBody
)
178 PETHREAD Thread
= (PETHREAD
)ObjectBody
;
179 PEPROCESS Process
= Thread
->ThreadsProcess
;
181 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody
, Thread
->ThreadsProcess
);
183 /* Deassociate the Process */
184 Thread
->ThreadsProcess
= NULL
;
186 /* Delete the CID Handle */
187 if(Thread
->Cid
.UniqueThread
!= NULL
) {
189 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
192 /* Free the W32THREAD structure if present */
193 if(Thread
->Tcb
.Win32Thread
!= NULL
) ExFreePool (Thread
->Tcb
.Win32Thread
);
195 /* Release the Kernel Stack */
196 MmDeleteKernelStack((PVOID
)Thread
->Tcb
.StackLimit
, FALSE
);
198 /* Dereference the Process */
199 ObDereferenceObject(Process
);
203 * FUNCTION: Terminates the current thread
204 * See "Windows Internals" - Chapter 13, Page 50-53
208 PspExitThread(NTSTATUS ExitStatus
)
210 PETHREAD CurrentThread
;
212 PEPROCESS CurrentProcess
;
213 PTERMINATION_PORT TerminationPort
;
216 PLIST_ENTRY ApcEntry
, CurrentApc
;
219 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus
, PsGetCurrentThread());
221 /* Get the Current Thread and Process */
222 CurrentThread
= PsGetCurrentThread();
223 CurrentProcess
= CurrentThread
->ThreadsProcess
;
225 /* Set the Exit Status and Exit Time */
226 CurrentThread
->ExitStatus
= ExitStatus
;
227 KeQuerySystemTime(&CurrentThread
->ExitTime
);
229 /* Can't terminate a thread if it attached another process */
230 if (KeIsAttachedProcess()) {
232 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
233 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
234 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
235 (ULONG
) CurrentThread
);
238 /* Lower to Passive Level */
239 KeLowerIrql(PASSIVE_LEVEL
);
241 /* Lock the Process before we modify its thread entries */
242 PsLockProcess(CurrentProcess
, FALSE
);
244 /* wake up the thread so we don't deadlock on PsLockProcess */
245 KeForceResumeThread(&CurrentThread
->Tcb
);
247 /* Run Thread Notify Routines before we desintegrate the thread */
248 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
250 /* Remove the thread from the thread list of its process */
251 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
252 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
254 /* Set the last Thread Exit Status */
255 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
259 /* Save the Exit Time if not already done by NtTerminateProcess. This
260 happens when the last thread just terminates without explicitly
261 terminating the process. */
262 CurrentProcess
->ExitTime
= CurrentThread
->ExitTime
;
265 /* Check if the process has a debug port */
266 if (CurrentProcess
->DebugPort
) {
268 /* Notify the Debug API. TODO */
269 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
272 /* Process the Termination Ports */
273 while ((TerminationPort
= CurrentThread
->TerminationPort
)) {
275 DPRINT("TerminationPort: %p\n", TerminationPort
);
277 /* Get the next one */
278 CurrentThread
->TerminationPort
= TerminationPort
->Next
;
280 /* Send the LPC Message */
281 LpcSendTerminationPort(TerminationPort
->Port
, CurrentThread
->CreateTime
);
284 ExFreePool(TerminationPort
);
287 /* Rundown Win32 Structures */
288 PsTerminateWin32Thread(CurrentThread
);
289 if (Last
) PsTerminateWin32Process(CurrentProcess
);
291 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
292 //CmNotifyRunDown(CurrentThread);
295 if((Teb
= CurrentThread
->Tcb
.Teb
))
297 /* Clean up the stack first, if requested */
298 if (Teb
->FreeStackOnTermination
)
301 ZwFreeVirtualMemory(NtCurrentProcess(),
302 &Teb
->DeallocationStack
,
307 DPRINT("Decommit teb at %p\n", Teb
);
308 MmDeleteTeb(CurrentProcess
, Teb
);
309 CurrentThread
->Tcb
.Teb
= NULL
;
312 /* The last Thread shuts down the Process */
313 if (Last
) PspExitProcess(CurrentProcess
);
315 /* Unlock the Process */
316 PsUnlockProcess(CurrentProcess
);
318 /* Cancel I/O for the thread. */
319 IoCancelThreadIo(CurrentThread
);
323 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
325 /* If the Processor Control Block's NpxThread points to the current thread
328 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
329 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread
,
331 (PKPROCESS
)CurrentThread
);
332 KeLowerIrql(oldIrql
);
334 /* Rundown Mutexes */
337 /* Disable new APC Queuing, this is as far as we'll let them go */
338 KeDisableThreadApcQueueing(&CurrentThread
->Tcb
);
340 /* Flush the User APCs */
341 if ((ApcEntry
= KeFlushQueueApc(&CurrentThread
->Tcb
, UserMode
)))
343 CurrentApc
= ApcEntry
;
347 Apc
= CONTAINING_RECORD(CurrentApc
, KAPC
, ApcListEntry
);
349 /* Move to the next one */
350 CurrentApc
= CurrentApc
->Flink
;
352 /* Rundown the APC or de-allocate it */
353 if (Apc
->RundownRoutine
)
355 /* Call its own routine */
356 (Apc
->RundownRoutine
)(Apc
);
360 /* Do it ourselves */
364 while (CurrentApc
!= ApcEntry
);
367 /* Call the Lego routine */
368 if (CurrentThread
->Tcb
.LegoData
) PspRunLegoRoutine(&CurrentThread
->Tcb
);
370 /* Flush the APC queue, which should be empty */
371 if ((ApcEntry
= KeFlushQueueApc(&CurrentThread
->Tcb
, KernelMode
)))
374 KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT
,
376 CurrentThread
->Tcb
.KernelApcDisable
,
381 /* Terminate the Thread from the Scheduler */
382 KeTerminateThread(0);
383 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
389 PsExitSpecialApc(PKAPC Apc
,
390 PKNORMAL_ROUTINE
* NormalRoutine
,
391 PVOID
* NormalContext
,
392 PVOID
* SystemArgument1
,
393 PVOID
* SystemArguemnt2
)
395 DPRINT1("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n",
396 PsGetCurrentThread(), PsGetCurrentProcess());
398 /* Don't do anything unless we are in User-Mode */
399 if (Apc
->SystemArgument2
)
401 NTSTATUS ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
406 /* Terminate the Thread */
407 PspExitThread(ExitStatus
);
409 /* we should never reach this point! */
416 PspExitNormalApc(PVOID NormalContext
,
417 PVOID SystemArgument1
,
418 PVOID SystemArgument2
)
420 PKAPC Apc
= (PKAPC
)SystemArgument1
;
421 PETHREAD Thread
= PsGetCurrentThread();
424 DPRINT1("PspExitNormalApc called: 0x%x (proc: 0x%x)\n",
425 PsGetCurrentThread(), PsGetCurrentProcess());
427 /* This should never happen */
428 ASSERT(!SystemArgument2
);
430 /* If this is a system thread, we can safely kill it from Kernel-Mode */
431 if (PsIsSystemThread(Thread
))
433 /* Get the Exit Status */
434 DPRINT1("Killing System Thread\n");
435 ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
440 /* Exit the Thread */
441 PspExitThread(ExitStatus
);
444 /* If we're here, this is not a System Thread, so kill it from User-Mode */
445 DPRINT1("Initializing User-Mode APC\n");
448 OriginalApcEnvironment
,
455 /* Now insert the APC with the User-Mode Flag */
456 KeInsertQueueApc(Apc
, Apc
, (PVOID
)UserMode
, 2);
458 /* Forcefully resume the thread */
459 KeForceResumeThread(&Thread
->Tcb
);
463 * See "Windows Internals" - Chapter 13, Page 49
467 PspTerminateThreadByPointer(PETHREAD Thread
,
472 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
475 /* Check if we are already in the right context */
476 if (PsGetCurrentThread() == Thread
) {
478 /* Directly terminate the thread */
479 PspExitThread(ExitStatus
);
481 /* we should never reach this point! */
485 /* Allocate the APC */
486 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
488 /* Initialize a Kernel Mode APC to Kill the Thread */
491 OriginalApcEnvironment
,
498 /* Insert it into the APC Queue */
499 KeInsertQueueApc(Apc
,
504 /* Forcefully resume the thread */
505 KeForceResumeThread(&Thread
->Tcb
);
510 PspExitProcess(PEPROCESS Process
)
512 DPRINT("PspExitProcess 0x%x\n", Process
);
514 PspRunCreateProcessNotifyRoutines(Process
, FALSE
);
516 /* close all handles associated with our process, this needs to be done
517 when the last thread still runs */
518 ObKillProcess(Process
);
520 KeSetProcess(&Process
->Pcb
, IO_NO_INCREMENT
);
522 return(STATUS_SUCCESS
);
527 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
528 IN NTSTATUS ExitStatus
)
532 PETHREAD CurrentThread
;
533 BOOLEAN KillByHandle
;
537 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
538 ProcessHandle
, ExitStatus
);
540 KillByHandle
= (ProcessHandle
!= NULL
);
542 /* Get the Process Object */
543 Status
= ObReferenceObjectByHandle((KillByHandle
? ProcessHandle
: NtCurrentProcess()),
549 if (!NT_SUCCESS(Status
)) {
551 DPRINT1("Invalid handle to Process\n");
555 CurrentThread
= PsGetCurrentThread();
557 PsLockProcess(Process
, FALSE
);
559 if(Process
->ExitTime
.QuadPart
!= 0)
561 PsUnlockProcess(Process
);
562 ObDereferenceObject(Process
);
563 return STATUS_PROCESS_IS_TERMINATING
;
566 /* Terminate all the Process's Threads */
567 PspTerminateProcessThreads(Process
, ExitStatus
);
569 /* only kill the calling thread if it either passed a process handle or
570 NtCurrentProcess() */
573 /* set the exit time as we're about to release the process lock before
574 we kill ourselves to prevent threads outside of our process trying
576 KeQuerySystemTime(&Process
->ExitTime
);
578 /* Only master thread remains... kill it off */
579 if (CurrentThread
->ThreadsProcess
== Process
) {
581 /* mark our thread as terminating so attempts to terminate it, when
582 unlocking the process, fail */
583 CurrentThread
->Terminated
= TRUE
;
585 PsUnlockProcess(Process
);
587 /* we can safely dereference the process because the current thread
588 holds a reference to it until it gets reaped */
589 ObDereferenceObject(Process
);
591 /* now the other threads get a chance to terminate, we don't wait but
592 just kill ourselves right now. The process will be run down when the
593 last thread terminates */
595 PspExitThread(ExitStatus
);
597 /* we should never reach this point! */
602 /* unlock and dereference the process so the threads can kill themselves */
603 PsUnlockProcess(Process
);
604 ObDereferenceObject(Process
);
606 return(STATUS_SUCCESS
);
611 NtTerminateThread(IN HANDLE ThreadHandle
,
612 IN NTSTATUS ExitStatus
)
619 /* Handle the special NULL case */
622 /* Check if we're the only thread left */
623 if (IsListEmpty(&PsGetCurrentProcess()->Pcb
.ThreadListHead
))
625 /* This is invalid */
626 DPRINT1("Can't terminate self\n");
627 return STATUS_CANT_TERMINATE_SELF
;
631 /* Use current handle */
632 ThreadHandle
= NtCurrentThread();
636 /* Get the Thread Object */
637 Status
= ObReferenceObjectByHandle(ThreadHandle
,
643 if (!NT_SUCCESS(Status
)) {
645 DPRINT1("Could not reference thread object\n");
649 /* Make sure this is not a system thread */
650 if (PsIsSystemThread(Thread
)) {
652 DPRINT1("Trying to Terminate a system thread!\n");
653 ObDereferenceObject(Thread
);
654 return STATUS_INVALID_PARAMETER
;
657 /* Check to see if we're running in the same thread */
658 if (Thread
!= PsGetCurrentThread()) {
660 /* we need to lock the process to make sure it's not already terminating */
661 PsLockProcess(Thread
->ThreadsProcess
, FALSE
);
663 /* This isn't our thread, terminate it if not already done */
664 if (!Thread
->Terminated
) {
666 Thread
->Terminated
= TRUE
;
669 PspTerminateThreadByPointer(Thread
, ExitStatus
);
672 PsUnlockProcess(Thread
->ThreadsProcess
);
674 /* Dereference the Thread and return */
675 ObDereferenceObject(Thread
);
679 Thread
->Terminated
= TRUE
;
681 /* it's safe to dereference thread, there's at least the keep-alive
682 reference which will be removed by the thread reaper causing the
683 thread to be finally destroyed */
684 ObDereferenceObject(Thread
);
686 /* Terminate him, he's ours */
687 PspExitThread(ExitStatus
);
689 /* We do never reach this point */
693 return(STATUS_SUCCESS
);
701 PsTerminateSystemThread(NTSTATUS ExitStatus
)
703 PETHREAD Thread
= PsGetCurrentThread();
705 /* Make sure this is a system thread */
706 if (!PsIsSystemThread(Thread
)) {
708 DPRINT1("Trying to Terminate a non-system thread!\n");
709 return STATUS_INVALID_PARAMETER
;
712 /* Terminate it for real */
713 PspExitThread(ExitStatus
);
715 /* we should never reach this point! */
718 return(STATUS_SUCCESS
);
723 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
726 PTERMINATION_PORT TerminationPort
;
727 PVOID TerminationLpcPort
;
733 Status
= ObReferenceObjectByHandle(PortHandle
,
739 if (!NT_SUCCESS(Status
)) {
741 DPRINT1("Failed to reference Port\n");
745 /* Allocate the Port and make sure it suceeded */
746 if((TerminationPort
= ExAllocatePoolWithTag(NonPagedPool
,
747 sizeof(PTERMINATION_PORT
),
748 TAG('P', 's', 'T', '=')))) {
750 /* Associate the Port */
751 Thread
= PsGetCurrentThread();
752 TerminationPort
->Port
= TerminationLpcPort
;
753 DPRINT("TerminationPort: %p\n", TerminationPort
);
754 TerminationPort
->Next
= Thread
->TerminationPort
;
755 Thread
->TerminationPort
= TerminationPort
;
756 DPRINT("TerminationPort: %p\n", Thread
->TerminationPort
);
759 return(STATUS_SUCCESS
);
763 /* Dereference and Fail */
764 ObDereferenceObject(TerminationPort
);
765 return(STATUS_INSUFFICIENT_RESOURCES
);