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
;
24 extern PHANDLE_TABLE PspCidTable
;
26 /* FUNCTIONS *****************************************************************/
30 PspReapRoutine(PVOID Context
)
33 PETHREAD Thread
, NewThread
;
36 DPRINT("Evil reaper running!!\n");
37 OldIrql
= KeAcquireDispatcherDatabaseLock();
39 /* Get the first Thread Entry */
40 Thread
= PspReaperList
;
42 DPRINT("PspReaperList: %x\n", Thread
);
44 /* Check to see if the list is empty */
47 /* Unlock the Dispatcher */
48 KeReleaseDispatcherDatabaseLock(OldIrql
);
50 /* Is there a thread on the list? */
53 /* Get the next Thread */
54 DPRINT("Thread: %x\n", Thread
);
55 DPRINT("Thread: %x\n", Thread
->ReaperLink
);
56 NewThread
= Thread
->ReaperLink
;
58 /* Remove reference to current thread */
59 ObDereferenceObject(Thread
);
61 /* Move to next Thread */
65 /* No more linked threads... Reacquire the Lock */
66 OldIrql
= KeAcquireDispatcherDatabaseLock();
68 /* Now try to get a new thread from the list */
69 Thread
= PspReaperList
;
71 DPRINT("PspReaperList: %x\n", Thread
);
73 /* Loop again if there is a new thread */
77 DPRINT("Done reaping\n");
78 KeReleaseDispatcherDatabaseLock(OldIrql
);
83 PspKillMostProcesses(VOID
)
85 PLIST_ENTRY current_entry
;
88 ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess
->UniqueProcessId
);
90 /* Acquire the Active Process Lock */
91 ExAcquireFastMutex(&PspActiveProcessMutex
);
93 /* Loop all processes on the list */
94 current_entry
= PsActiveProcessHead
.Flink
;
95 while (current_entry
!= &PsActiveProcessHead
)
97 current
= CONTAINING_RECORD(current_entry
, EPROCESS
, ActiveProcessLinks
);
98 current_entry
= current_entry
->Flink
;
100 if (current
->UniqueProcessId
!= PsInitialSystemProcess
->UniqueProcessId
)
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
->Terminated
) {
134 Thread
->Terminated
= 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", ObjectBody
);
151 /* Remove it from the Active List */
152 ExAcquireFastMutex(&PspActiveProcessMutex
);
153 RemoveEntryList(&Process
->ActiveProcessLinks
);
154 ExReleaseFastMutex(&PspActiveProcessMutex
);
156 /* Delete the CID Handle */
157 if(Process
->UniqueProcessId
)
159 ExDestroyHandle(PspCidTable
, Process
->UniqueProcessId
);
163 KDB_DELETEPROCESS_HOOK(Process
);
165 /* Dereference the Token */
166 SeDeassignPrimaryToken(Process
);
168 /* Release Memory Information */
169 MmReleaseMmInfo(Process
);
171 /* Delete the W32PROCESS structure if there's one associated */
172 if(Process
->Win32Process
!= NULL
) ExFreePool(Process
->Win32Process
);
177 PspDeleteThread(PVOID ObjectBody
)
179 PETHREAD Thread
= (PETHREAD
)ObjectBody
;
180 PEPROCESS Process
= Thread
->ThreadsProcess
;
182 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody
, Thread
->ThreadsProcess
);
184 /* Deassociate the Process */
185 Thread
->ThreadsProcess
= NULL
;
187 /* Delete the CID Handle */
188 if(Thread
->Cid
.UniqueThread
)
190 ExDestroyHandle(PspCidTable
, Thread
->Cid
.UniqueThread
);
193 /* Free the W32THREAD structure if present */
194 if(Thread
->Tcb
.Win32Thread
!= NULL
) ExFreePool (Thread
->Tcb
.Win32Thread
);
196 /* Release the Kernel Stack */
197 MmDeleteKernelStack((PVOID
)Thread
->Tcb
.StackLimit
, FALSE
);
199 /* Dereference the Process */
200 ObDereferenceObject(Process
);
204 * FUNCTION: Terminates the current thread
205 * See "Windows Internals" - Chapter 13, Page 50-53
209 PspExitThread(NTSTATUS ExitStatus
)
211 PETHREAD CurrentThread
;
213 PEPROCESS CurrentProcess
;
214 PTERMINATION_PORT TerminationPort
;
217 PLIST_ENTRY FirstEntry
, CurrentEntry
;
220 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus
, PsGetCurrentThread());
222 /* Get the Current Thread and Process */
223 CurrentThread
= PsGetCurrentThread();
224 CurrentProcess
= CurrentThread
->ThreadsProcess
;
226 /* Set the Exit Status and Exit Time */
227 CurrentThread
->ExitStatus
= ExitStatus
;
228 KeQuerySystemTime(&CurrentThread
->ExitTime
);
230 /* Can't terminate a thread if it attached another process */
231 if (KeIsAttachedProcess()) {
233 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
234 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
235 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
236 (ULONG
) CurrentThread
);
239 /* Lower to Passive Level */
240 KeLowerIrql(PASSIVE_LEVEL
);
242 /* Lock the Process before we modify its thread entries */
243 PsLockProcess(CurrentProcess
, FALSE
);
245 /* wake up the thread so we don't deadlock on PsLockProcess */
246 KeForceResumeThread(&CurrentThread
->Tcb
);
248 /* Run Thread Notify Routines before we desintegrate the thread */
249 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
251 /* Remove the thread from the thread list of its process */
252 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
253 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
255 /* Set the last Thread Exit Status */
256 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
260 /* Save the Exit Time if not already done by NtTerminateProcess. This
261 happens when the last thread just terminates without explicitly
262 terminating the process. */
263 CurrentProcess
->ExitTime
= CurrentThread
->ExitTime
;
264 CurrentProcess
->ExitStatus
= ExitStatus
;
267 /* Check if the process has a debug port */
268 if (CurrentProcess
->DebugPort
) {
270 /* Notify the Debug API. TODO */
271 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
274 /* Process the Termination Ports */
275 while ((TerminationPort
= CurrentThread
->TerminationPort
)) {
277 DPRINT("TerminationPort: %p\n", TerminationPort
);
279 /* Get the next one */
280 CurrentThread
->TerminationPort
= TerminationPort
->Next
;
282 /* Send the LPC Message */
283 LpcSendTerminationPort(TerminationPort
->Port
, CurrentThread
->CreateTime
);
284 ObDereferenceObject(TerminationPort
->Port
);
287 ExFreePool(TerminationPort
);
290 /* Rundown Win32 Structures */
291 PsTerminateWin32Thread(CurrentThread
);
292 if (Last
) PsTerminateWin32Process(CurrentProcess
);
294 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
295 //CmNotifyRunDown(CurrentThread);
298 if((Teb
= CurrentThread
->Tcb
.Teb
))
300 /* Clean up the stack first, if requested */
301 if (Teb
->FreeStackOnTermination
)
304 ZwFreeVirtualMemory(NtCurrentProcess(),
305 &Teb
->DeallocationStack
,
310 DPRINT("Decommit teb at %p\n", Teb
);
311 MmDeleteTeb(CurrentProcess
, Teb
);
312 CurrentThread
->Tcb
.Teb
= NULL
;
315 /* The last Thread shuts down the Process */
316 if (Last
) PspExitProcess(CurrentProcess
);
318 /* Unlock the Process */
319 PsUnlockProcess(CurrentProcess
);
321 /* Cancel I/O for the thread. */
322 IoCancelThreadIo(CurrentThread
);
326 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
328 /* If the Processor Control Block's NpxThread points to the current thread
331 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
332 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread
,
334 (PKPROCESS
)CurrentThread
);
335 KeLowerIrql(oldIrql
);
337 /* Rundown Mutexes */
340 /* Disable new APC Queuing, this is as far as we'll let them go */
341 KeDisableThreadApcQueueing(&CurrentThread
->Tcb
);
343 /* Flush the User APCs */
344 FirstEntry
= KeFlushQueueApc(&CurrentThread
->Tcb
, UserMode
);
345 if (FirstEntry
!= NULL
)
347 CurrentEntry
= FirstEntry
;
351 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
353 /* Move to the next one */
354 CurrentEntry
= CurrentEntry
->Flink
;
356 /* Rundown the APC or de-allocate it */
357 if (Apc
->RundownRoutine
)
359 /* Call its own routine */
360 (Apc
->RundownRoutine
)(Apc
);
364 /* Do it ourselves */
368 while (CurrentEntry
!= FirstEntry
);
371 /* Call the Lego routine */
372 if (CurrentThread
->Tcb
.LegoData
) PspRunLegoRoutine(&CurrentThread
->Tcb
);
374 /* Flush the APC queue, which should be empty */
375 if ((FirstEntry
= KeFlushQueueApc(&CurrentThread
->Tcb
, KernelMode
)))
378 KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT
,
379 (ULONG_PTR
)FirstEntry
,
380 CurrentThread
->Tcb
.KernelApcDisable
,
385 /* Terminate the Thread from the Scheduler */
386 KeTerminateThread(0);
387 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
393 PsExitSpecialApc(PKAPC Apc
,
394 PKNORMAL_ROUTINE
* NormalRoutine
,
395 PVOID
* NormalContext
,
396 PVOID
* SystemArgument1
,
397 PVOID
* SystemArguemnt2
)
399 DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x, '%.16s')\n",
400 PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName
);
402 /* Don't do anything unless we are in User-Mode */
403 if (Apc
->SystemArgument2
)
405 NTSTATUS ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
410 /* Terminate the Thread */
411 PspExitThread(ExitStatus
);
413 /* we should never reach this point! */
420 PspExitNormalApc(PVOID NormalContext
,
421 PVOID SystemArgument1
,
422 PVOID SystemArgument2
)
424 PKAPC Apc
= (PKAPC
)SystemArgument1
;
425 PETHREAD Thread
= PsGetCurrentThread();
428 DPRINT("PspExitNormalApc called: 0x%x (proc: 0x%x, '%.16s')\n",
429 PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName
);
431 /* This should never happen */
432 ASSERT(!SystemArgument2
);
434 /* If this is a system thread, we can safely kill it from Kernel-Mode */
435 if (PsIsSystemThread(Thread
))
437 /* Get the Exit Status */
438 DPRINT1("Killing System Thread\n");
439 ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
444 /* Exit the Thread */
445 PspExitThread(ExitStatus
);
448 /* If we're here, this is not a System Thread, so kill it from User-Mode */
449 DPRINT("Initializing User-Mode APC\n");
452 OriginalApcEnvironment
,
459 /* Now insert the APC with the User-Mode Flag */
460 KeInsertQueueApc(Apc
, Apc
, (PVOID
)UserMode
, 2);
462 /* Forcefully resume the thread */
463 KeForceResumeThread(&Thread
->Tcb
);
467 * See "Windows Internals" - Chapter 13, Page 49
471 PspTerminateThreadByPointer(PETHREAD Thread
,
476 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
479 /* Check if we are already in the right context */
480 if (PsGetCurrentThread() == Thread
) {
482 /* Directly terminate the thread */
483 PspExitThread(ExitStatus
);
485 /* we should never reach this point! */
489 /* Allocate the APC */
490 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
492 /* Initialize a Kernel Mode APC to Kill the Thread */
495 OriginalApcEnvironment
,
502 /* Insert it into the APC Queue */
503 KeInsertQueueApc(Apc
,
508 /* Forcefully resume the thread */
509 KeForceResumeThread(&Thread
->Tcb
);
514 PspExitProcess(PEPROCESS Process
)
516 DPRINT("PspExitProcess 0x%x\n", Process
);
518 PspRunCreateProcessNotifyRoutines(Process
, FALSE
);
520 PspDestroyQuotaBlock(Process
);
522 /* close all handles associated with our process, this needs to be done
523 when the last thread still runs */
524 ObKillProcess(Process
);
526 KeSetProcess(&Process
->Pcb
, IO_NO_INCREMENT
);
528 return(STATUS_SUCCESS
);
533 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
534 IN NTSTATUS ExitStatus
)
538 PETHREAD CurrentThread
;
539 BOOLEAN KillByHandle
;
543 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
544 ProcessHandle
, ExitStatus
);
546 KillByHandle
= (ProcessHandle
!= NULL
);
548 /* Get the Process Object */
549 Status
= ObReferenceObjectByHandle((KillByHandle
? ProcessHandle
: NtCurrentProcess()),
555 if (!NT_SUCCESS(Status
)) {
557 DPRINT1("Invalid handle to Process\n");
561 CurrentThread
= PsGetCurrentThread();
563 PsLockProcess(Process
, FALSE
);
565 if(Process
->ExitTime
.QuadPart
!= 0)
567 PsUnlockProcess(Process
);
568 ObDereferenceObject(Process
);
569 return STATUS_PROCESS_IS_TERMINATING
;
572 /* Terminate all the Process's Threads */
573 PspTerminateProcessThreads(Process
, ExitStatus
);
575 /* only kill the calling thread if it either passed a process handle or
576 NtCurrentProcess() */
579 /* set the exit time as we're about to release the process lock before
580 we kill ourselves to prevent threads outside of our process trying
582 KeQuerySystemTime(&Process
->ExitTime
);
583 Process
->ExitStatus
= ExitStatus
;
585 /* Only master thread remains... kill it off */
586 if (CurrentThread
->ThreadsProcess
== Process
) {
588 /* mark our thread as terminating so attempts to terminate it, when
589 unlocking the process, fail */
590 CurrentThread
->Terminated
= TRUE
;
592 PsUnlockProcess(Process
);
594 /* we can safely dereference the process because the current thread
595 holds a reference to it until it gets reaped */
596 ObDereferenceObject(Process
);
598 /* now the other threads get a chance to terminate, we don't wait but
599 just kill ourselves right now. The process will be run down when the
600 last thread terminates */
602 PspExitThread(ExitStatus
);
604 /* we should never reach this point! */
609 /* unlock and dereference the process so the threads can kill themselves */
610 PsUnlockProcess(Process
);
611 ObDereferenceObject(Process
);
613 return(STATUS_SUCCESS
);
618 NtTerminateThread(IN HANDLE ThreadHandle
,
619 IN NTSTATUS ExitStatus
)
626 /* Handle the special NULL case */
629 /* Check if we're the only thread left */
630 if (IsListEmpty(&PsGetCurrentProcess()->Pcb
.ThreadListHead
))
632 /* This is invalid */
633 DPRINT1("Can't terminate self\n");
634 return STATUS_CANT_TERMINATE_SELF
;
638 /* Use current handle */
639 ThreadHandle
= NtCurrentThread();
643 /* Get the Thread Object */
644 Status
= ObReferenceObjectByHandle(ThreadHandle
,
650 if (!NT_SUCCESS(Status
)) {
652 DPRINT1("Could not reference thread object\n");
656 /* Make sure this is not a system thread */
657 if (PsIsSystemThread(Thread
)) {
659 DPRINT1("Trying to Terminate a system thread!\n");
660 ObDereferenceObject(Thread
);
661 return STATUS_INVALID_PARAMETER
;
664 /* Check to see if we're running in the same thread */
665 if (Thread
!= PsGetCurrentThread()) {
667 /* we need to lock the process to make sure it's not already terminating */
668 PsLockProcess(Thread
->ThreadsProcess
, FALSE
);
670 /* This isn't our thread, terminate it if not already done */
671 if (!Thread
->Terminated
) {
673 Thread
->Terminated
= TRUE
;
676 PspTerminateThreadByPointer(Thread
, ExitStatus
);
679 PsUnlockProcess(Thread
->ThreadsProcess
);
681 /* Dereference the Thread and return */
682 ObDereferenceObject(Thread
);
686 Thread
->Terminated
= TRUE
;
688 /* it's safe to dereference thread, there's at least the keep-alive
689 reference which will be removed by the thread reaper causing the
690 thread to be finally destroyed */
691 ObDereferenceObject(Thread
);
693 /* Terminate him, he's ours */
694 PspExitThread(ExitStatus
);
696 /* We do never reach this point */
700 return(STATUS_SUCCESS
);
708 PsTerminateSystemThread(NTSTATUS ExitStatus
)
710 PETHREAD Thread
= PsGetCurrentThread();
712 /* Make sure this is a system thread */
713 if (!PsIsSystemThread(Thread
)) {
715 DPRINT1("Trying to Terminate a non-system thread!\n");
716 return STATUS_INVALID_PARAMETER
;
719 /* Terminate it for real */
720 PspExitThread(ExitStatus
);
722 /* we should never reach this point! */
725 return(STATUS_SUCCESS
);
730 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
733 PTERMINATION_PORT TerminationPort
;
734 PVOID TerminationLpcPort
;
740 Status
= ObReferenceObjectByHandle(PortHandle
,
746 if (!NT_SUCCESS(Status
)) {
748 DPRINT1("Failed to reference Port\n");
752 /* Allocate the Port and make sure it suceeded */
753 if((TerminationPort
= ExAllocatePoolWithTag(NonPagedPool
,
754 sizeof(PTERMINATION_PORT
),
755 TAG('P', 's', 'T', '=')))) {
757 /* Associate the Port */
758 Thread
= PsGetCurrentThread();
759 TerminationPort
->Port
= TerminationLpcPort
;
760 DPRINT("TerminationPort: %p\n", TerminationPort
);
761 TerminationPort
->Next
= Thread
->TerminationPort
;
762 Thread
->TerminationPort
= TerminationPort
;
763 DPRINT("TerminationPort: %p\n", Thread
->TerminationPort
);
766 return(STATUS_SUCCESS
);
770 /* Dereference and Fail */
771 ObDereferenceObject(TerminationPort
);
772 return(STATUS_INSUFFICIENT_RESOURCES
);