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
;
217 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus
, PsGetCurrentThread());
219 /* Get the Current Thread and Process */
220 CurrentThread
= PsGetCurrentThread();
221 CurrentProcess
= CurrentThread
->ThreadsProcess
;
223 /* Set the Exit Status and Exit Time */
224 CurrentThread
->ExitStatus
= ExitStatus
;
225 KeQuerySystemTime(&CurrentThread
->ExitTime
);
227 /* Can't terminate a thread if it attached another process */
228 if (KeIsAttachedProcess()) {
230 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
231 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
232 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
233 (ULONG
) CurrentThread
);
236 /* Lower to Passive Level */
237 KeLowerIrql(PASSIVE_LEVEL
);
239 /* Lock the Process before we modify its thread entries */
240 PsLockProcess(CurrentProcess
, FALSE
);
242 /* wake up the thread so we don't deadlock on PsLockProcess */
243 KeForceResumeThread(&CurrentThread
->Tcb
);
245 /* Run Thread Notify Routines before we desintegrate the thread */
246 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
248 /* Remove the thread from the thread list of its process */
249 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
250 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
252 /* Set the last Thread Exit Status */
253 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
257 /* Save the Exit Time if not already done by NtTerminateProcess. This
258 happens when the last thread just terminates without explicitly
259 terminating the process. */
260 CurrentProcess
->ExitTime
= CurrentThread
->ExitTime
;
263 /* Check if the process has a debug port */
264 if (CurrentProcess
->DebugPort
) {
266 /* Notify the Debug API. TODO */
267 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
270 /* Process the Termination Ports */
271 while ((TerminationPort
= CurrentThread
->TerminationPort
)) {
273 DPRINT("TerminationPort: %p\n", TerminationPort
);
275 /* Get the next one */
276 CurrentThread
->TerminationPort
= TerminationPort
->Next
;
278 /* Send the LPC Message */
279 LpcSendTerminationPort(TerminationPort
->Port
, CurrentThread
->CreateTime
);
282 ExFreePool(TerminationPort
);
285 /* Rundown Win32 Structures */
286 PsTerminateWin32Thread(CurrentThread
);
287 if (Last
) PsTerminateWin32Process(CurrentProcess
);
289 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
290 //CmNotifyRunDown(CurrentThread);
293 if((Teb
= CurrentThread
->Tcb
.Teb
))
295 /* Clean up the stack first, if requested */
296 if (Teb
->FreeStackOnTermination
)
299 ZwFreeVirtualMemory(NtCurrentProcess(),
300 &Teb
->DeallocationStack
,
305 DPRINT("Decommit teb at %p\n", Teb
);
306 MmDeleteTeb(CurrentProcess
, Teb
);
307 CurrentThread
->Tcb
.Teb
= NULL
;
310 /* The last Thread shuts down the Process */
311 if (Last
) PspExitProcess(CurrentProcess
);
313 /* Unlock the Process */
314 PsUnlockProcess(CurrentProcess
);
316 /* Cancel I/O for the thread. */
317 IoCancelThreadIo(CurrentThread
);
321 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
323 /* If the Processor Control Block's NpxThread points to the current thread
326 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
327 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread
,
329 (PKPROCESS
)CurrentThread
);
330 KeLowerIrql(oldIrql
);
332 /* Rundown Mutexes */
335 /* Terminate the Thread from the Scheduler */
336 KeTerminateThread(0);
337 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
343 PsExitSpecialApc(PKAPC Apc
,
344 PKNORMAL_ROUTINE
* NormalRoutine
,
345 PVOID
* NormalContext
,
346 PVOID
* SystemArgument1
,
347 PVOID
* SystemArguemnt2
)
349 DPRINT1("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n",
350 PsGetCurrentThread(), PsGetCurrentProcess());
352 /* Don't do anything unless we are in User-Mode */
353 if (Apc
->SystemArgument2
)
355 NTSTATUS ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
360 /* Terminate the Thread */
361 PspExitThread(ExitStatus
);
363 /* we should never reach this point! */
370 PspExitNormalApc(PVOID NormalContext
,
371 PVOID SystemArgument1
,
372 PVOID SystemArgument2
)
374 PKAPC Apc
= (PKAPC
)SystemArgument1
;
375 PETHREAD Thread
= PsGetCurrentThread();
378 DPRINT1("PspExitNormalApc called: 0x%x (proc: 0x%x)\n",
379 PsGetCurrentThread(), PsGetCurrentProcess());
381 /* This should never happen */
382 ASSERT(!SystemArgument2
);
384 /* If this is a system thread, we can safely kill it from Kernel-Mode */
385 if (PsIsSystemThread(Thread
))
387 /* Get the Exit Status */
388 DPRINT1("Killing System Thread\n");
389 ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
394 /* Exit the Thread */
395 PspExitThread(ExitStatus
);
398 /* If we're here, this is not a System Thread, so kill it from User-Mode */
399 DPRINT1("Initializing User-Mode APC\n");
402 OriginalApcEnvironment
,
409 /* Now insert the APC with the User-Mode Flag */
410 KeInsertQueueApc(Apc
, Apc
, (PVOID
)UserMode
, 2);
412 /* Forcefully resume the thread */
413 KeForceResumeThread(&Thread
->Tcb
);
417 * See "Windows Internals" - Chapter 13, Page 49
421 PspTerminateThreadByPointer(PETHREAD Thread
,
426 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
429 /* Check if we are already in the right context */
430 if (PsGetCurrentThread() == Thread
) {
432 /* Directly terminate the thread */
433 PspExitThread(ExitStatus
);
435 /* we should never reach this point! */
439 /* Allocate the APC */
440 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
442 /* Initialize a Kernel Mode APC to Kill the Thread */
445 OriginalApcEnvironment
,
452 /* Insert it into the APC Queue */
453 KeInsertQueueApc(Apc
,
458 /* Forcefully resume the thread */
459 KeForceResumeThread(&Thread
->Tcb
);
464 PspExitProcess(PEPROCESS Process
)
466 DPRINT("PspExitProcess 0x%x\n", Process
);
468 PspRunCreateProcessNotifyRoutines(Process
, FALSE
);
470 /* close all handles associated with our process, this needs to be done
471 when the last thread still runs */
472 ObKillProcess(Process
);
474 KeSetProcess(&Process
->Pcb
, IO_NO_INCREMENT
);
476 return(STATUS_SUCCESS
);
481 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
482 IN NTSTATUS ExitStatus
)
486 PETHREAD CurrentThread
;
487 BOOLEAN KillByHandle
;
491 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
492 ProcessHandle
, ExitStatus
);
494 KillByHandle
= (ProcessHandle
!= NULL
);
496 /* Get the Process Object */
497 Status
= ObReferenceObjectByHandle((KillByHandle
? ProcessHandle
: NtCurrentProcess()),
503 if (!NT_SUCCESS(Status
)) {
505 DPRINT1("Invalid handle to Process\n");
509 CurrentThread
= PsGetCurrentThread();
511 PsLockProcess(Process
, FALSE
);
513 if(Process
->ExitTime
.QuadPart
!= 0)
515 PsUnlockProcess(Process
);
516 ObDereferenceObject(Process
);
517 return STATUS_PROCESS_IS_TERMINATING
;
520 /* Terminate all the Process's Threads */
521 PspTerminateProcessThreads(Process
, ExitStatus
);
523 /* only kill the calling thread if it either passed a process handle or
524 NtCurrentProcess() */
527 /* set the exit time as we're about to release the process lock before
528 we kill ourselves to prevent threads outside of our process trying
530 KeQuerySystemTime(&Process
->ExitTime
);
532 /* Only master thread remains... kill it off */
533 if (CurrentThread
->ThreadsProcess
== Process
) {
535 /* mark our thread as terminating so attempts to terminate it, when
536 unlocking the process, fail */
537 CurrentThread
->Terminated
= TRUE
;
539 PsUnlockProcess(Process
);
541 /* we can safely dereference the process because the current thread
542 holds a reference to it until it gets reaped */
543 ObDereferenceObject(Process
);
545 /* now the other threads get a chance to terminate, we don't wait but
546 just kill ourselves right now. The process will be run down when the
547 last thread terminates */
549 PspExitThread(ExitStatus
);
551 /* we should never reach this point! */
556 /* unlock and dereference the process so the threads can kill themselves */
557 PsUnlockProcess(Process
);
558 ObDereferenceObject(Process
);
560 return(STATUS_SUCCESS
);
565 NtTerminateThread(IN HANDLE ThreadHandle
,
566 IN NTSTATUS ExitStatus
)
573 /* Handle the special NULL case */
576 /* Check if we're the only thread left */
577 if (IsListEmpty(&PsGetCurrentProcess()->Pcb
.ThreadListHead
))
579 /* This is invalid */
580 DPRINT1("Can't terminate self\n");
581 return STATUS_CANT_TERMINATE_SELF
;
585 /* Use current handle */
586 ThreadHandle
= NtCurrentThread();
590 /* Get the Thread Object */
591 Status
= ObReferenceObjectByHandle(ThreadHandle
,
597 if (!NT_SUCCESS(Status
)) {
599 DPRINT1("Could not reference thread object\n");
603 /* Make sure this is not a system thread */
604 if (PsIsSystemThread(Thread
)) {
606 DPRINT1("Trying to Terminate a system thread!\n");
607 ObDereferenceObject(Thread
);
608 return STATUS_INVALID_PARAMETER
;
611 /* Check to see if we're running in the same thread */
612 if (Thread
!= PsGetCurrentThread()) {
614 /* we need to lock the process to make sure it's not already terminating */
615 PsLockProcess(Thread
->ThreadsProcess
, FALSE
);
617 /* This isn't our thread, terminate it if not already done */
618 if (!Thread
->Terminated
) {
620 Thread
->Terminated
= TRUE
;
623 PspTerminateThreadByPointer(Thread
, ExitStatus
);
626 PsUnlockProcess(Thread
->ThreadsProcess
);
628 /* Dereference the Thread and return */
629 ObDereferenceObject(Thread
);
633 Thread
->Terminated
= TRUE
;
635 /* it's safe to dereference thread, there's at least the keep-alive
636 reference which will be removed by the thread reaper causing the
637 thread to be finally destroyed */
638 ObDereferenceObject(Thread
);
640 /* Terminate him, he's ours */
641 PspExitThread(ExitStatus
);
643 /* We do never reach this point */
647 return(STATUS_SUCCESS
);
655 PsTerminateSystemThread(NTSTATUS ExitStatus
)
657 PETHREAD Thread
= PsGetCurrentThread();
659 /* Make sure this is a system thread */
660 if (!PsIsSystemThread(Thread
)) {
662 DPRINT1("Trying to Terminate a non-system thread!\n");
663 return STATUS_INVALID_PARAMETER
;
666 /* Terminate it for real */
667 PspExitThread(ExitStatus
);
669 /* we should never reach this point! */
672 return(STATUS_SUCCESS
);
677 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
680 PTERMINATION_PORT TerminationPort
;
681 PVOID TerminationLpcPort
;
687 Status
= ObReferenceObjectByHandle(PortHandle
,
693 if (!NT_SUCCESS(Status
)) {
695 DPRINT1("Failed to reference Port\n");
699 /* Allocate the Port and make sure it suceeded */
700 if((TerminationPort
= ExAllocatePoolWithTag(NonPagedPool
,
701 sizeof(PTERMINATION_PORT
),
702 TAG('P', 's', 'T', '=')))) {
704 /* Associate the Port */
705 Thread
= PsGetCurrentThread();
706 TerminationPort
->Port
= TerminationLpcPort
;
707 DPRINT("TerminationPort: %p\n", TerminationPort
);
708 TerminationPort
->Next
= Thread
->TerminationPort
;
709 Thread
->TerminationPort
= TerminationPort
;
710 DPRINT("TerminationPort: %p\n", Thread
->TerminationPort
);
713 return(STATUS_SUCCESS
);
717 /* Dereference and Fail */
718 ObDereferenceObject(TerminationPort
);
719 return(STATUS_INSUFFICIENT_RESOURCES
);