[ntoskrnl]
[reactos.git] / reactos / ntoskrnl / ps / kill.c
index 518b78b..b1ea95f 100644 (file)
 /*
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
  * FILE:            ntoskrnl/ps/kill.c
- * PURPOSE:         Thread Termination and Reaping
- *
- * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
- *                  David Welch (welch@cwcom.net)
+ * PURPOSE:         Process Manager: Process and Thread Termination
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ *                  Filip Navara (xnavara@reactos.org)
+ *                  Thomas Weidenmueller (w3seek@reactos.org
  */
 
 /* INCLUDES *****************************************************************/
 
 #include <ntoskrnl.h>
 #define NDEBUG
-#include <internal/debug.h>
-
-#define LockEvent Spare0[0]
-#define LockCount Spare0[1]
-#define LockOwner Spare0[2]
-
+#include <debug.h>
 
 /* GLOBALS *******************************************************************/
 
-PETHREAD PspReaperList = NULL;
+LIST_ENTRY PspReaperListHead = { NULL, NULL };
 WORK_QUEUE_ITEM PspReaperWorkItem;
-BOOLEAN PspReaping = FALSE;
-extern LIST_ENTRY PsActiveProcessHead;
-extern FAST_MUTEX PspActiveProcessMutex;
-extern PHANDLE_TABLE PspCidTable;
+LARGE_INTEGER ShortTime = {{-10 * 100 * 1000, -1}};
 
-/* FUNCTIONS *****************************************************************/
+/* PRIVATE FUNCTIONS *********************************************************/
 
 VOID
-STDCALL
-PspReapRoutine(PVOID Context)
+NTAPI
+PspCatchCriticalBreak(IN PCHAR Message,
+                      IN PVOID ProcessOrThread,
+                      IN PCHAR ImageName)
 {
-    KIRQL OldIrql;
-    PETHREAD Thread, NewThread;
+    CHAR Action[2];
+    BOOLEAN Handled = FALSE;
+    PAGED_CODE();
 
-    /* Acquire lock */
-    DPRINT("Evil reaper running!!\n");
-    OldIrql = KeAcquireDispatcherDatabaseLock();
+    /* Check if a debugger is enabled */
+    if (KdDebuggerEnabled)
+    {
+        /* Print out the message */
+        DbgPrint(Message, ProcessOrThread, ImageName);
+        do
+        {
+            /* If a debugger isn't present, don't prompt */
+            if (KdDebuggerNotPresent) break;
 
-    /* Get the first Thread Entry */
-    Thread = PspReaperList;
-    PspReaperList = NULL;
-    DPRINT("PspReaperList: %x\n", Thread);
+            /* A debuger is active, prompt for action */
+            DbgPrompt("Break, or Ignore (bi)?", Action, sizeof(Action));
+            switch (Action[0])
+            {
+                /* Break */
+                case 'B': case 'b':
 
-    /* Check to see if the list is empty */
-    do {
+                    /* Do a breakpoint */
+                    DbgBreakPoint();
 
-        /* Unlock the Dispatcher */
-        KeReleaseDispatcherDatabaseLock(OldIrql);
+                /* Ignore */
+                case 'I': case 'i':
 
-        /* Is there a thread on the list? */
-        while (Thread) {
+                    /* Handle it */
+                    Handled = TRUE;
 
-            /* Get the next Thread */
-            DPRINT("Thread: %x\n", Thread);
-            DPRINT("Thread: %x\n", Thread->ReaperLink);
-            NewThread = Thread->ReaperLink;
+                /* Unrecognized */
+                default:
+                    break;
+            }
+        } while (!Handled);
+    }
 
-            /* Remove reference to current thread */
-            ObDereferenceObject(Thread);
+    /* Did we ultimately handle this? */
+    if (!Handled)
+    {
+        /* We didn't, bugcheck */
+        KeBugCheckEx(CRITICAL_OBJECT_TERMINATION,
+                     ((PKPROCESS)ProcessOrThread)->Header.Type,
+                     (ULONG_PTR)ProcessOrThread,
+                     (ULONG_PTR)ImageName,
+                     (ULONG_PTR)Message);
+    }
+}
 
-            /* Move to next Thread */
-            Thread = NewThread;
-        }
+NTSTATUS
+NTAPI
+PspTerminateProcess(IN PEPROCESS Process,
+                    IN NTSTATUS ExitStatus)
+{
+    PETHREAD Thread;
+    NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG,
+            "Process: %p ExitStatus: %p\n", Process, ExitStatus);
+    PSREFTRACE(Process);
 
-        /* No more linked threads... Reacquire the Lock */
-        OldIrql = KeAcquireDispatcherDatabaseLock();
+    /* Check if this is a Critical Process */
+    if (Process->BreakOnTermination)
+    {
+        /* Break to debugger */
+        PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
+                              Process,
+                              Process->ImageFileName);
+    }
 
-        /* Now try to get a new thread from the list */
-        Thread = PspReaperList;
-        PspReaperList = NULL;
-        DPRINT("PspReaperList: %x\n", Thread);
+    /* Set the delete flag */
+    InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT);
 
-        /* Loop again if there is a new thread */
-    } while (Thread);
+    /* Get the first thread */
+    Thread = PsGetNextProcessThread(Process, NULL);
+    while (Thread)
+    {
+        /* Kill it */
+        PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
+        Thread = PsGetNextProcessThread(Process, Thread);
+
+        /* We had at least one thread, so termination is OK */
+        Status = STATUS_SUCCESS;
+    }
 
-    PspReaping = FALSE;
-    DPRINT("Done reaping\n");
-    KeReleaseDispatcherDatabaseLock(OldIrql);
+    /* Check if there was nothing to terminate or if we have a debug port */
+    if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort))
+    {
+        /* Clear the handle table anyway */
+        ObClearProcessHandleTable(Process);
+    }
+
+    /* Return status */
+    return Status;
 }
 
-VOID
-STDCALL
-PspKillMostProcesses(VOID)
+NTSTATUS
+NTAPI
+PsTerminateProcess(IN PEPROCESS Process,
+                   IN NTSTATUS ExitStatus)
 {
-    PLIST_ENTRY current_entry;
-    PEPROCESS current;
-
-    ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess->UniqueProcessId);
+    /* Call the internal API */
+    return PspTerminateProcess(Process, ExitStatus);
+}
 
-    /* Acquire the Active Process Lock */
-    ExAcquireFastMutex(&PspActiveProcessMutex);
+VOID
+NTAPI
+PspShutdownProcessManager(VOID)
+{
+    PEPROCESS Process = NULL;
 
-    /* Loop all processes on the list */
-    current_entry = PsActiveProcessHead.Flink;
-    while (current_entry != &PsActiveProcessHead)
+    /* Loop every process */
+    Process = PsGetNextProcess(Process);
+    while (Process)
     {
-        current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
-        current_entry = current_entry->Flink;
-
-        if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId)
+        /* Make sure this isn't the idle or initial process */
+        if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
         {
-            /* Terminate all the Threads in this Process */
-            PspTerminateProcessThreads(current, STATUS_SUCCESS);
+            /* Kill it */
+            PspTerminateProcess(Process, STATUS_SYSTEM_SHUTDOWN);
         }
-    }
 
-    /* Release the lock */
-    ExReleaseFastMutex(&PspActiveProcessMutex);
+        /* Get the next process */
+        Process = PsGetNextProcess(Process);
+    }
 }
 
 VOID
-STDCALL
-PspTerminateProcessThreads(PEPROCESS Process,
-                           NTSTATUS ExitStatus)
+NTAPI
+PspExitApcRundown(IN PKAPC Apc)
 {
-    PLIST_ENTRY CurrentEntry;
-    PETHREAD Thread, CurrentThread = PsGetCurrentThread();
+    PAGED_CODE();
+
+    /* Free the APC */
+    ExFreePool(Apc);
+}
 
-    CurrentEntry = Process->ThreadListHead.Flink;
-    while (CurrentEntry != &Process->ThreadListHead) {
+VOID
+NTAPI
+PspReapRoutine(IN PVOID Context)
+{
+    PSINGLE_LIST_ENTRY NextEntry;
+    PETHREAD Thread;
+    PSTRACE(PS_KILL_DEBUG, "Context: %p\n", Context);
 
-        /* Get the Current Thread */
-        Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
+    /* Start main loop */
+    do
+    {
+        /* Write magic value and return the next entry to process */
+        NextEntry = InterlockedExchangePointer(&PspReaperListHead.Flink,
+                                               (PVOID)1);
+        ASSERT((NextEntry != NULL) && (NextEntry != (PVOID)1));
 
-        /* Move to the Next Thread */
-        CurrentEntry = CurrentEntry->Flink;
+        /* Start inner loop */
+        do
+        {
+            /* Get the first Thread Entry */
+            Thread = CONTAINING_RECORD(NextEntry, ETHREAD, ReaperLink);
 
-        /* Make sure it's not the one we're in */
-        if (Thread != CurrentThread) {
+            /* Delete this entry's kernel stack */
+            MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
+                                Thread->Tcb.LargeStack);
+            Thread->Tcb.InitialStack = NULL;
 
-            /* Make sure it didn't already terminate */
-            if (!Thread->Terminated) {
+            /* Move to the next entry */
+            NextEntry = NextEntry->Next;
 
-                Thread->Terminated = TRUE;
+            /* Dereference this thread */
+            ObDereferenceObject(Thread);
+        } while ((NextEntry != NULL) && (NextEntry != (PVOID)1));
 
-                /* Terminate it by APC */
-                PspTerminateThreadByPointer(Thread, ExitStatus);
-            }
-        }
-    }
+        /* Remove magic value, keep looping if it got changed */
+    } while (InterlockedCompareExchangePointer(&PspReaperListHead.Flink,
+                                               0,
+                                               1) != (PVOID)1);
 }
 
 VOID
-STDCALL
-PspDeleteProcess(PVOID ObjectBody)
+NTAPI
+PspDeleteProcess(IN PVOID ObjectBody)
 {
     PEPROCESS Process = (PEPROCESS)ObjectBody;
+    KAPC_STATE ApcState;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
+    PSREFTRACE(Process);
+
+    /* Check if it has an Active Process Link */
+    if (Process->ActiveProcessLinks.Flink)
+    {
+        /* Remove it from the Active List */
+        KeAcquireGuardedMutex(&PspActiveProcessMutex);
+        RemoveEntryList(&Process->ActiveProcessLinks);
+        KeReleaseGuardedMutex(&PspActiveProcessMutex);
+    }
+
+    /* Check for Auditing information */
+    if (Process->SeAuditProcessCreationInfo.ImageFileName)
+    {
+        /* Free it */
+        ExFreePool(Process->SeAuditProcessCreationInfo.ImageFileName);
+        Process->SeAuditProcessCreationInfo.ImageFileName = NULL;
+    }
+
+    /* Check if we have a job */
+    if (Process->Job)
+    {
+        /* Remove the process from the job */
+        PspRemoveProcessFromJob(Process, Process->Job);
 
-    DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
+        /* Dereference it */
+        ObDereferenceObject(Process->Job);
+        Process->Job = NULL;
+    }
 
-    /* Remove it from the Active List */
-    ExAcquireFastMutex(&PspActiveProcessMutex);
-    RemoveEntryList(&Process->ActiveProcessLinks);
-    ExReleaseFastMutex(&PspActiveProcessMutex);
+    /* Increase the stack count */
+    Process->Pcb.StackCount++;
 
-    /* Delete the CID Handle */
-    if(Process->UniqueProcessId)
+    /* Check if we have a debug port */
+    if (Process->DebugPort)
     {
-        ExDestroyHandle(PspCidTable, Process->UniqueProcessId);
+        /* Deference the Debug Port */
+        ObDereferenceObject(Process->DebugPort);
+        Process->DebugPort = NULL;
     }
 
-    /* Delete the process lock */
-    ExFreePool(Process->LockEvent);
+    /* Check if we have an exception port */
+    if (Process->ExceptionPort)
+    {
+        /* Deference the Exception Port */
+        ObDereferenceObject(Process->ExceptionPort);
+        Process->ExceptionPort = NULL;
+    }
 
-    /* KDB hook */
-    KDB_DELETEPROCESS_HOOK(Process);
+    /* Check if we have a section object */
+    if (Process->SectionObject)
+    {
+        /* Deference the Section Object */
+        ObDereferenceObject(Process->SectionObject);
+        Process->SectionObject = NULL;
+    }
 
-    /* Dereference the Token */
-    SeDeassignPrimaryToken(Process);
+#if defined(_X86_)
+    /* Clean Ldt and Vdm objects */
+    PspDeleteLdt(Process);
+    PspDeleteVdmObjects(Process);
+#endif
+
+    /* Delete the Object Table */
+    if (Process->ObjectTable)
+    {
+        /* Attach to the process */
+        KeStackAttachProcess(&Process->Pcb, &ApcState);
 
-    /* Release Memory Information */
-    MmReleaseMmInfo(Process);
+        /* Kill the Object Info */
+        ObKillProcess(Process);
 
-    /* Delete the W32PROCESS structure if there's one associated */
-    if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
+        /* Detach */
+        KeUnstackDetachProcess(&ApcState);
+    }
+
+    /* Check if we have an address space, and clean it */
+    if (Process->HasAddressSpace)
+    {
+        /* Attach to the process */
+        KeStackAttachProcess(&Process->Pcb, &ApcState);
+
+        /* Clean the Address Space */
+        PspExitProcess(FALSE, Process);
+
+        /* Detach */
+        KeUnstackDetachProcess(&ApcState);
+
+        /* Completely delete the Address Space */
+        MmDeleteProcessAddressSpace(Process);
+    }
+
+    /* See if we have a PID */
+    if (Process->UniqueProcessId)
+    {
+        /* Delete the PID */
+        if (!(ExDestroyHandle(PspCidTable, Process->UniqueProcessId, NULL)))
+        {
+            /* Something wrong happened, bugcheck */
+            KeBugCheck(CID_HANDLE_DELETION);
+        }
+    }
+
+    /* Cleanup security information */
+    PspDeleteProcessSecurity(Process);
+
+    /* Check if we have kept information on the Working Set */
+    if (Process->WorkingSetWatch)
+    {
+        /* Free it */
+        ExFreePool(Process->WorkingSetWatch);
+
+        /* And return the quota it was taking up */
+        PsReturnProcessNonPagedPoolQuota(Process, 0x2000);
+    }
+
+    /* Dereference the Device Map */
+    ObDereferenceDeviceMap(Process);
+
+    /* Destroy the Quota Block */
+    PspDestroyQuotaBlock(Process);
 }
 
 VOID
-STDCALL
-PspDeleteThread(PVOID ObjectBody)
+NTAPI
+PspDeleteThread(IN PVOID ObjectBody)
 {
     PETHREAD Thread = (PETHREAD)ObjectBody;
     PEPROCESS Process = Thread->ThreadsProcess;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
+    PSREFTRACE(Thread);
+    ASSERT(Thread->Tcb.Win32Thread == NULL);
 
-    DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
-
-    /* Deassociate the Process */
-    Thread->ThreadsProcess = NULL;
+    /* Check if we have a stack */
+    if (Thread->Tcb.InitialStack)
+    {
+        /* Release it */
+        MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
+                            Thread->Tcb.LargeStack);
+    }
 
-    /* Delete the CID Handle */
-    if(Thread->Cid.UniqueThread)
+    /* Check if we have a CID Handle */
+    if (Thread->Cid.UniqueThread)
     {
-        ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread);
+        /* Delete the CID Handle */
+        if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL)))
+        {
+            /* Something wrong happened, bugcheck */
+            KeBugCheck(CID_HANDLE_DELETION);
+        }
     }
 
-    /* Free the W32THREAD structure if present */
-    if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
+    /* Cleanup impersionation information */
+    PspDeleteThreadSecurity(Thread);
 
-    /* Release the Kernel Stack */
-    MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, Thread->Tcb.LargeStack);
+    /* Make sure the thread was inserted, before continuing */
+    if (!Process) return;
+
+    /* Check if the thread list is valid */
+    if (Thread->ThreadListEntry.Flink)
+    {
+        /* Lock the thread's process */
+        KeEnterCriticalRegion();
+        ExAcquirePushLockExclusive(&Process->ProcessLock);
+
+        /* Remove us from the list */
+        RemoveEntryList(&Thread->ThreadListEntry);
+
+        /* Release the lock */
+        ExReleasePushLockExclusive(&Process->ProcessLock);
+        KeLeaveCriticalRegion();
+    }
 
     /* Dereference the Process */
     ObDereferenceObject(Process);
@@ -213,145 +396,427 @@ PspDeleteThread(PVOID ObjectBody)
  * See "Windows Internals" - Chapter 13, Page 50-53
  */
 VOID
-STDCALL
-PspExitThread(NTSTATUS ExitStatus)
+NTAPI
+PspExitThread(IN NTSTATUS ExitStatus)
 {
-    PETHREAD CurrentThread;
-    BOOLEAN Last;
-    PEPROCESS CurrentProcess;
-    PTERMINATION_PORT TerminationPort;
+    CLIENT_DIED_MSG TerminationMsg;
+    NTSTATUS Status;
     PTEB Teb;
-    KIRQL oldIrql;
+    PEPROCESS CurrentProcess;
+    PETHREAD Thread, OtherThread, PreviousThread = NULL;
+    PVOID DeallocationStack;
+    ULONG Dummy;
+    BOOLEAN Last = FALSE;
+    PTERMINATION_PORT TerminationPort, NextPort;
     PLIST_ENTRY FirstEntry, CurrentEntry;
     PKAPC Apc;
-
-    DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
+    PTOKEN PrimaryToken;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG, "ExitStatus: %p\n", ExitStatus);
 
     /* Get the Current Thread and Process */
-    CurrentThread = PsGetCurrentThread();
-    CurrentProcess = CurrentThread->ThreadsProcess;
-
-    /* Set the Exit Status and Exit Time */
-    CurrentThread->ExitStatus = ExitStatus;
-    KeQuerySystemTime(&CurrentThread->ExitTime);
+    Thread = PsGetCurrentThread();
+    CurrentProcess = Thread->ThreadsProcess;
+    ASSERT((Thread) == PsGetCurrentThread());
 
     /* Can't terminate a thread if it attached another process */
-    if (KeIsAttachedProcess()) {
-
-        KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
-                     (ULONG) CurrentThread->Tcb.ApcState.Process,
-                     (ULONG) CurrentThread->Tcb.ApcStateIndex,
-                     (ULONG) CurrentThread);
+    if (KeIsAttachedProcess())
+    {
+        /* Bugcheck */
+        KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
+                     (ULONG_PTR)CurrentProcess,
+                     (ULONG_PTR)Thread->Tcb.ApcState.Process,
+                     (ULONG_PTR)Thread->Tcb.ApcStateIndex,
+                     (ULONG_PTR)Thread);
     }
 
     /* Lower to Passive Level */
     KeLowerIrql(PASSIVE_LEVEL);
 
-    /* Lock the Process before we modify its thread entries */
-    PsLockProcess(CurrentProcess, FALSE);
+    /* Can't be a worker thread */
+    if (Thread->ActiveExWorker)
+    {
+        /* Bugcheck */
+        KeBugCheckEx(ACTIVE_EX_WORKER_THREAD_TERMINATION,
+                     (ULONG_PTR)Thread,
+                     0,
+                     0,
+                     0);
+    }
+
+    /* Can't have pending APCs */
+    if (Thread->Tcb.CombinedApcDisable != 0)
+    {
+        /* Bugcheck */
+        KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
+                     0,
+                     Thread->Tcb.CombinedApcDisable,
+                     0,
+                     1);
+    }
+
+    /* Lock the thread */
+    ExWaitForRundownProtectionRelease(&Thread->RundownProtect);
+
+    /* Cleanup the power state */
+    PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState);
 
-    /* wake up the thread so we don't deadlock on PsLockProcess */
-    KeForceResumeThread(&CurrentThread->Tcb);
+    /* Call the WMI Callback for Threads */
+    //WmiTraceThread(Thread, NULL, FALSE);
 
     /* Run Thread Notify Routines before we desintegrate the thread */
-    PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
+    PspRunCreateThreadNotifyRoutines(Thread, FALSE);
 
-    /* Remove the thread from the thread list of its process */
-    RemoveEntryList(&CurrentThread->ThreadListEntry);
-    Last = IsListEmpty(&CurrentProcess->ThreadListHead);
+    /* Lock the Process before we modify its thread entries */
+    KeEnterCriticalRegion();
+    ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
 
-    /* Set the last Thread Exit Status */
-    CurrentProcess->LastThreadExitStatus = ExitStatus;
+    /* Decrease the active thread count, and check if it's 0 */
+    if (!(--CurrentProcess->ActiveThreads))
+    {
+        /* Set the delete flag */
+        InterlockedOr((PLONG)&CurrentProcess->Flags, PSF_PROCESS_DELETE_BIT);
 
-    if (Last) {
+        /* Remember we are last */
+        Last = TRUE;
 
-       /* Save the Exit Time if not already done by NtTerminateProcess. This
-          happens when the last thread just terminates without explicitly
-          terminating the process. */
-       CurrentProcess->ExitTime = CurrentThread->ExitTime;
-       CurrentProcess->ExitStatus = ExitStatus;
-    }
+        /* Check if this termination is due to the thread dying */
+        if (ExitStatus == STATUS_THREAD_IS_TERMINATING)
+        {
+            /* Check if the last thread was pending */
+            if (CurrentProcess->ExitStatus == STATUS_PENDING)
+            {
+                /* Use the last exit status */
+                CurrentProcess->ExitStatus = CurrentProcess->
+                                             LastThreadExitStatus;
+            }
+        }
+        else
+        {
+            /* Just a normal exit, write the code */
+            CurrentProcess->ExitStatus = ExitStatus;
+        }
 
-    /* Check if the process has a debug port */
-    if (CurrentProcess->DebugPort) {
+        /* Loop all the current threads */
+        FirstEntry = &CurrentProcess->ThreadListHead;
+        CurrentEntry = FirstEntry->Flink;
+        while (FirstEntry != CurrentEntry)
+        {
+            /* Get the thread on the list */
+            OtherThread = CONTAINING_RECORD(CurrentEntry,
+                                            ETHREAD,
+                                            ThreadListEntry);
+
+            /* Check if it's a thread that's still alive */
+            if ((OtherThread != Thread) &&
+                !(KeReadStateThread(&OtherThread->Tcb)) &&
+                (ObReferenceObjectSafe(OtherThread)))
+            {
+                /* It's a live thread and we referenced it, unlock process */
+                ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
+                KeLeaveCriticalRegion();
+
+                /* Wait on the thread */
+                KeWaitForSingleObject(OtherThread,
+                                      Executive,
+                                      KernelMode,
+                                      FALSE,
+                                      NULL);
+
+                /* Check if we had a previous thread to dereference */
+                if (PreviousThread) ObDereferenceObject(PreviousThread);
+
+                /* Remember the thread and re-lock the process */
+                PreviousThread = OtherThread;
+                KeEnterCriticalRegion();
+                ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
+            }
 
-        /* Notify the Debug API. TODO */
-        //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
+            /* Go to the next thread */
+            CurrentEntry = CurrentEntry->Flink;
+        }
+    }
+    else if (ExitStatus != STATUS_THREAD_IS_TERMINATING)
+    {
+        /* Write down the exit status of the last thread to get killed */
+        CurrentProcess->LastThreadExitStatus = ExitStatus;
     }
 
-    /* Process the Termination Ports */
-    while ((TerminationPort = CurrentThread->TerminationPort)) {
+    /* Unlock the Process */
+    ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
+    KeLeaveCriticalRegion();
 
-        DPRINT("TerminationPort: %p\n", TerminationPort);
+    /* Check if we had a previous thread to dereference */
+    if (PreviousThread) ObDereferenceObject(PreviousThread);
 
-        /* Get the next one */
-        CurrentThread->TerminationPort = TerminationPort->Next;
+    /* Check if the process has a debug port and if this is a user thread */
+    if ((CurrentProcess->DebugPort) && !(Thread->SystemThread))
+    {
+        /* Notify the Debug API. */
+        Last ? DbgkExitProcess(CurrentProcess->ExitStatus) :
+               DbgkExitThread(ExitStatus);
+    }
 
-        /* Send the LPC Message */
-        LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
-        ObDereferenceObject(TerminationPort->Port);
+    /* Check if this is a Critical Thread */
+    if ((KdDebuggerEnabled) && (Thread->BreakOnTermination))
+    {
+        /* Break to debugger */
+        PspCatchCriticalBreak("Critical thread 0x%p (in %s) exited\n",
+                              Thread,
+                              CurrentProcess->ImageFileName);
+    }
 
-        /* Free the Port */
-        ExFreePool(TerminationPort);
+    /* Check if it's the last thread and this is a Critical Process */
+    if ((Last) && (CurrentProcess->BreakOnTermination))
+    {
+        /* Check if a debugger is here to handle this */
+        if (KdDebuggerEnabled)
+        {
+            /* Break to debugger */
+            PspCatchCriticalBreak("Critical  process 0x%p (in %s) exited\n",
+                                  CurrentProcess,
+                                  CurrentProcess->ImageFileName);
+        }
+        else
+        {
+            /* Bugcheck, we can't allow this */
+            KeBugCheckEx(CRITICAL_PROCESS_DIED,
+                         (ULONG_PTR)CurrentProcess,
+                         0,
+                         0,
+                         0);
+        }
     }
 
-    /* Rundown Win32 Structures */
-    PsTerminateWin32Thread(CurrentThread);
-    if (Last) PsTerminateWin32Process(CurrentProcess);
+    /* Sanity check */
+    ASSERT(Thread->Tcb.CombinedApcDisable == 0);
 
-    /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
-    //CmNotifyRunDown(CurrentThread);
+    /* Process the Termination Ports */
+    TerminationPort = Thread->TerminationPort;
+    if (TerminationPort)
+    {
+        /* Setup the message header */
+        TerminationMsg.h.u2.ZeroInit = 0;
+        TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
+        TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
+        TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
+                                            sizeof(PORT_MESSAGE);
+
+        /* Loop each port */
+        do
+        {
+            /* Save the Create Time */
+            TerminationMsg.CreateTime = Thread->CreateTime;
+
+            /* Loop trying to send message */
+            while (TRUE)
+            {
+                /* Send the LPC Message */
+                Status = LpcRequestPort(TerminationPort->Port,
+                                        &TerminationMsg.h);
+                if ((Status == STATUS_NO_MEMORY) ||
+                    (Status == STATUS_INSUFFICIENT_RESOURCES))
+                {
+                    /* Wait a bit and try again */
+                    KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
+                    continue;
+                }
+                break;
+            }
+
+            /* Dereference this LPC Port */
+            ObDereferenceObject(TerminationPort->Port);
 
-    /* Free the TEB */
-    if((Teb = CurrentThread->Tcb.Teb))
+            /* Move to the next one */
+            NextPort = TerminationPort->Next;
+
+            /* Free the Termination Port Object */
+            ExFreePool(TerminationPort);
+
+            /* Keep looping as long as there is a port */
+            TerminationPort = NextPort;
+        } while (TerminationPort);
+    }
+    else if (((ExitStatus == STATUS_THREAD_IS_TERMINATING) &&
+              (Thread->DeadThread)) ||
+             !(Thread->DeadThread))
     {
-        /* Clean up the stack first, if requested */
-        if (Teb->FreeStackOnTermination)
+        /*
+         * This case is special and deserves some extra comments. What
+         * basically happens here is that this thread doesn't have a termination
+         * port, which means that it died before being fully created. Since we
+         * still have to notify an LPC Server, we'll use the exception port,
+         * which we know exists. However, we need to know how far the thread
+         * actually got created. We have three possibilites:
+         *
+         *  - NtCreateThread returned an error really early: DeadThread is set.
+         *  - NtCreateThread managed to create the thread: DeadThread is off.
+         *  - NtCreateThread was creating the thread (with Deadthread set,
+         *    but the thread got killed prematurely: STATUS_THREAD_IS_TERMINATING
+         *    is our exit code.)
+         *
+         * For the 2 & 3rd scenarios, the thread has been created far enough to
+         * warrant notification to the LPC Server.
+         */
+
+        /* Setup the message header */
+        TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
+        TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
+        TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
+                                            sizeof(PORT_MESSAGE);
+
+        /* Make sure the process has an exception port */
+        if (CurrentProcess->ExceptionPort)
         {
-            ULONG Dummy = 0;
-            ZwFreeVirtualMemory(NtCurrentProcess(),
-                                &Teb->DeallocationStack,
-                                &Dummy,
-                                MEM_RELEASE);
+            /* Save the Create Time */
+            TerminationMsg.CreateTime = Thread->CreateTime;
+
+            /* Loop trying to send message */
+            while (TRUE)
+            {
+                /* Send the LPC Message */
+                Status = LpcRequestPort(CurrentProcess->ExceptionPort,
+                                        &TerminationMsg.h);
+                if ((Status == STATUS_NO_MEMORY) ||
+                    (Status == STATUS_INSUFFICIENT_RESOURCES))
+                {
+                    /* Wait a bit and try again */
+                    KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
+                    continue;
+                }
+                break;
+            }
         }
-        
-        DPRINT("Decommit teb at %p\n", Teb);
-        MmDeleteTeb(CurrentProcess, Teb);
-        CurrentThread->Tcb.Teb = NULL;
     }
 
-    /* The last Thread shuts down the Process */
-    if (Last) PspExitProcess(CurrentProcess);
+    /* Rundown Win32 Thread if there is one */
+    if (Thread->Tcb.Win32Thread) PspW32ThreadCallout(Thread,
+                                                     PsW32ThreadCalloutExit);
 
-    /* Unlock the Process */
-    PsUnlockProcess(CurrentProcess);
+    /* If we are the last thread and have a W32 Process */
+    if ((Last) && (CurrentProcess->Win32Process))
+    {
+        /* Run it down too */
+        PspW32ProcessCallout(CurrentProcess, FALSE);
+    }
+
+    /* Make sure Stack Swap is enabled */
+    if (!Thread->Tcb.EnableStackSwap)
+    {
+        /* Stack swap really shouldn't be disabled during exit! */
+        KeBugCheckEx(KERNEL_STACK_LOCKED_AT_EXIT, 0, 0, 0, 0);
+    }
 
     /* Cancel I/O for the thread. */
-    IoCancelThreadIo(CurrentThread);
+    IoCancelThreadIo(Thread);
 
     /* Rundown Timers */
     ExTimerRundown();
-    KeCancelTimer(&CurrentThread->Tcb.Timer);
 
-    /* If the Processor Control Block's NpxThread points to the current thread
-     * unset it.
-     */
-    KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
-    (void)InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
-                                            NULL,
-                                            (PVOID)CurrentThread);
-    KeLowerIrql(oldIrql);
+    /* FIXME: Rundown Registry Notifications (NtChangeNotify)
+    CmNotifyRunDown(Thread); */
 
     /* Rundown Mutexes */
     KeRundownThread();
 
-    /* Disable new APC Queuing, this is as far as we'll let them go */
-    KeDisableThreadApcQueueing(&CurrentThread->Tcb);
+    /* Check if we have a TEB */
+    Teb = Thread->Tcb.Teb;
+    if (Teb)
+    {
+        /* Check if the thread is still alive */
+        if (!Thread->DeadThread)
+        {
+            /* Check if we need to free its stack */
+            if (Teb->FreeStackOnTermination)
+            {
+                /* Set the TEB's Deallocation Stack as the Base Address */
+                Dummy = 0;
+                DeallocationStack = Teb->DeallocationStack;
+
+                /* Free the Thread's Stack */
+                ZwFreeVirtualMemory(NtCurrentProcess(),
+                                    &DeallocationStack,
+                                    &Dummy,
+                                    MEM_RELEASE);
+            }
+
+            /* Free the debug handle */
+            if (Teb->DbgSsReserved[1]) ObCloseHandle(Teb->DbgSsReserved[1],
+                                                     UserMode);
+        }
+
+        /* Decommit the TEB */
+        MmDeleteTeb(CurrentProcess, Teb);
+        Thread->Tcb.Teb = NULL;
+    }
+
+    /* Free LPC Data */
+    LpcExitThread(Thread);
+
+    /* Save the exit status and exit time */
+    Thread->ExitStatus = ExitStatus;
+    KeQuerySystemTime(&Thread->ExitTime);
+
+    /* Sanity check */
+    ASSERT(Thread->Tcb.CombinedApcDisable == 0);
+
+    /* Check if this is the final thread or not */
+    if (Last)
+    {
+        /* Set the process exit time */
+        CurrentProcess->ExitTime = Thread->ExitTime;
+
+        /* Exit the process */
+        PspExitProcess(TRUE, CurrentProcess);
+
+        /* Get the process token and check if we need to audit */
+        PrimaryToken = PsReferencePrimaryToken(CurrentProcess);
+        if (SeDetailedAuditingWithToken(PrimaryToken))
+        {
+            /* Audit the exit */
+            SeAuditProcessExit(CurrentProcess);
+        }
+
+        /* Dereference the process token */
+        ObFastDereferenceObject(&CurrentProcess->Token, PrimaryToken);
+
+        /* Check if this is a VDM Process and rundown the VDM DPCs if so */
+        if (CurrentProcess->VdmObjects) { /* VdmRundownDpcs(CurrentProcess); */ }
+
+        /* Kill the process in the Object Manager */
+        ObKillProcess(CurrentProcess);
+
+        /* Check if we have a section object */
+        if (CurrentProcess->SectionObject)
+        {
+            /* Dereference and clear the Section Object */
+            ObDereferenceObject(CurrentProcess->SectionObject);
+            CurrentProcess->SectionObject = NULL;
+        }
+
+        /* Check if the process is part of a job */
+        if (CurrentProcess->Job)
+        {
+            /* Remove the process from the job */
+            PspExitProcessFromJob(CurrentProcess->Job, CurrentProcess);
+        }
+    }
+
+    /* Disable APCs */
+    KeEnterCriticalRegion();
+
+    /* Disable APC queueing, force a resumption */
+    Thread->Tcb.ApcQueueable = FALSE;
+    KeForceResumeThread(&Thread->Tcb);
+
+    /* Re-enable APCs */
+    KeLeaveCriticalRegion();
 
     /* Flush the User APCs */
-    FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, UserMode);
-    if (FirstEntry != NULL)
+    FirstEntry = KeFlushQueueApc(&Thread->Tcb, UserMode);
+    if (FirstEntry)
     {
+        /* Start with the first entry */
         CurrentEntry = FirstEntry;
         do
         {
@@ -365,7 +830,7 @@ PspExitThread(NTSTATUS ExitStatus)
            if (Apc->RundownRoutine)
            {
               /* Call its own routine */
-              (Apc->RundownRoutine)(Apc);
+              Apc->RundownRoutine(Apc);
            }
            else
            {
@@ -376,376 +841,454 @@ PspExitThread(NTSTATUS ExitStatus)
         while (CurrentEntry != FirstEntry);
     }
 
+    /* Clean address space if this was the last thread */
+    if (Last) MmCleanProcessAddressSpace(CurrentProcess);
+
     /* Call the Lego routine */
-    if (CurrentThread->Tcb.LegoData) PspRunLegoRoutine(&CurrentThread->Tcb);
+    if (Thread->Tcb.LegoData) PspRunLegoRoutine(&Thread->Tcb);
 
     /* Flush the APC queue, which should be empty */
-    if ((FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, KernelMode)))
+    FirstEntry = KeFlushQueueApc(&Thread->Tcb, KernelMode);
+    if ((FirstEntry) || (Thread->Tcb.CombinedApcDisable != 0))
     {
         /* Bugcheck time */
-        KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT,
+        KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
                      (ULONG_PTR)FirstEntry,
-                     CurrentThread->Tcb.KernelApcDisable,
-                     oldIrql,
+                     Thread->Tcb.CombinedApcDisable,
+                     KeGetCurrentIrql(),
                      0);
     }
 
+    /* Signal the process if this was the last thread */
+    if (Last) KeSetProcess(&CurrentProcess->Pcb, 0, FALSE);
+
     /* Terminate the Thread from the Scheduler */
     KeTerminateThread(0);
-    DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
-    KEBUGCHECK(0);
 }
 
 VOID
-STDCALL
-PsExitSpecialApc(PKAPC Apc,
-                 PKNORMAL_ROUTINE* NormalRoutine,
-                 PVOID* NormalContext,
-                 PVOID* SystemArgument1,
-                 PVOID* SystemArguemnt2)
+NTAPI
+PsExitSpecialApc(IN PKAPC Apc,
+                 IN OUT PKNORMAL_ROUTINE* NormalRoutine,
+                 IN OUT PVOID* NormalContext,
+                 IN OUT PVOID* SystemArgument1,
+                 IN OUT PVOID* SystemArgument2)
 {
-    DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x, '%.16s')\n", 
-           PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
+    NTSTATUS Status;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG,
+            "Apc: %p SystemArgument2: %p \n", Apc, SystemArgument2);
 
     /* Don't do anything unless we are in User-Mode */
     if (Apc->SystemArgument2)
     {
-        NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
-
         /* Free the APC */
-        ExFreePool(Apc);
+        Status = (NTSTATUS)Apc->NormalContext;
+        PspExitApcRundown(Apc);
 
         /* Terminate the Thread */
-        PspExitThread(ExitStatus);
-
-        /* we should never reach this point! */
-        KEBUGCHECK(0);
+        PspExitThread(Status);
     }
 }
 
 VOID
-STDCALL
-PspExitNormalApc(PVOID NormalContext,
-                 PVOID SystemArgument1,
-                 PVOID SystemArgument2)
+NTAPI
+PspExitNormalApc(IN PVOID NormalContext,
+                 IN PVOID SystemArgument1,
+                 IN PVOID SystemArgument2)
 {
     PKAPC Apc = (PKAPC)SystemArgument1;
     PETHREAD Thread = PsGetCurrentThread();
-    NTSTATUS ExitStatus;
-        
-    DPRINT("PspExitNormalApc called: 0x%x (proc: 0x%x, '%.16s')\n", 
-           PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG, "SystemArgument2: %p \n", SystemArgument2);
 
     /* This should never happen */
-    ASSERT(!SystemArgument2);
-
-    /* If this is a system thread, we can safely kill it from Kernel-Mode */
-    if (PsIsSystemThread(Thread))
-    {
-        /* Get the Exit Status */
-        DPRINT1("Killing System Thread\n");
-        ExitStatus = (NTSTATUS)Apc->NormalContext;
-
-        /* Free the APC */
-        ExFreePool(Apc);
-
-        /* Exit the Thread */
-        PspExitThread(ExitStatus);
-    }
+    ASSERT(!(((ULONG_PTR)SystemArgument2) & 1));
 
     /* If we're here, this is not a System Thread, so kill it from User-Mode */
-    DPRINT("Initializing User-Mode APC\n");
     KeInitializeApc(Apc,
                     &Thread->Tcb,
                     OriginalApcEnvironment,
                     PsExitSpecialApc,
-                    NULL,
+                    PspExitApcRundown,
                     PspExitNormalApc,
                     UserMode,
                     NormalContext);
 
     /* Now insert the APC with the User-Mode Flag */
-    KeInsertQueueApc(Apc, Apc, (PVOID)UserMode, 2);
+    if (!(KeInsertQueueApc(Apc,
+                           Apc,
+                           (PVOID)((ULONG_PTR)SystemArgument2 | 1),
+                           2)))
+    {
+        /* Failed to insert, free the APC */
+        PspExitApcRundown(Apc);
+    }
 
-    /* Forcefully resume the thread */
-    KeForceResumeThread(&Thread->Tcb);
+    /* Set the APC Pending flag */
+    Thread->Tcb.ApcState.UserApcPending = TRUE;
 }
 
 /*
  * See "Windows Internals" - Chapter 13, Page 49
  */
-VOID
-STDCALL
-PspTerminateThreadByPointer(PETHREAD Thread,
-                            NTSTATUS ExitStatus)
+NTSTATUS
+NTAPI
+PspTerminateThreadByPointer(IN PETHREAD Thread,
+                            IN NTSTATUS ExitStatus,
+                            IN BOOLEAN bSelf)
 {
     PKAPC Apc;
+    NTSTATUS Status = STATUS_SUCCESS;
+    ULONG Flags;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %p\n", Thread, ExitStatus);
+    PSREFTRACE(Thread);
+
+    /* Check if this is a Critical Thread, and Bugcheck */
+    if (Thread->BreakOnTermination)
+    {
+        /* Break to debugger */
+        PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n",
+                              Thread,
+                              Thread->ThreadsProcess->ImageFileName);
+    }
 
-    DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
-            Thread, ExitStatus);
+    /* Check if we are already inside the thread */
+    if ((bSelf) || (PsGetCurrentThread() == Thread))
+    {
+        /* This should only happen at passive */
+        ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
 
-    /* Check if we are already in the right context */
-    if (PsGetCurrentThread() == Thread) {
+        /* Mark it as terminated */
+        PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT);
 
         /* Directly terminate the thread */
         PspExitThread(ExitStatus);
-
-        /* we should never reach this point! */
-        KEBUGCHECK(0);
     }
 
+    /* This shouldn't be a system thread */
+    if (Thread->SystemThread) return STATUS_ACCESS_DENIED;
+
     /* Allocate the APC */
     Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
+    if (!Apc) return STATUS_INSUFFICIENT_RESOURCES;
 
-    /* Initialize a Kernel Mode APC to Kill the Thread */
-    KeInitializeApc(Apc,
-                    &Thread->Tcb,
-                    OriginalApcEnvironment,
-                    PsExitSpecialApc,
-                    NULL,
-                    PspExitNormalApc,
-                    KernelMode,
-                    (PVOID)ExitStatus);
+    /* Set the Terminated Flag */
+    Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT;
+
+    /* Set it, and check if it was already set while we were running */
+    if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) &
+          CT_TERMINATED_BIT))
+    {
+        /* Initialize a Kernel Mode APC to Kill the Thread */
+        KeInitializeApc(Apc,
+                        &Thread->Tcb,
+                        OriginalApcEnvironment,
+                        PsExitSpecialApc,
+                        PspExitApcRundown,
+                        PspExitNormalApc,
+                        KernelMode,
+                        (PVOID)ExitStatus);
+
+        /* Insert it into the APC Queue */
+        if (!KeInsertQueueApc(Apc, Apc, NULL, 2))
+        {
+            /* The APC was already in the queue, fail */
+            Status = STATUS_UNSUCCESSFUL;
+        }
+        else
+        {
+            /* Forcefully resume the thread and return */
+            KeForceResumeThread(&Thread->Tcb);
+            return Status;
+        }
+    }
 
-    /* Insert it into the APC Queue */
-    KeInsertQueueApc(Apc,
-                     Apc,
-                     NULL,
-                     2);
+    /* We failed, free the APC */
+    ExFreePool(Apc);
 
-    /* Forcefully resume the thread */
-    KeForceResumeThread(&Thread->Tcb);
+    /* Return Status */
+    return Status;
 }
 
-NTSTATUS
-STDCALL
-PspExitProcess(PEPROCESS Process)
+VOID
+NTAPI
+PspExitProcess(IN BOOLEAN LastThread,
+               IN PEPROCESS Process)
 {
-    DPRINT("PspExitProcess 0x%x\n", Process);
+    ULONG Actual;
+    PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG,
+            "LastThread: %p Process: %p\n", LastThread, Process);
+    PSREFTRACE(Process);
 
-    PspRunCreateProcessNotifyRoutines(Process, FALSE);
+    /* Set Process Exit flag */
+    InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_EXITING_BIT);
 
-    PspDestroyQuotaBlock(Process);
+    /* Check if we are the last thread */
+    if (LastThread)
+    {
+        /* Notify the WMI Process Callback */
+        //WmiTraceProcess(Process, FALSE);
 
-    /* close all handles associated with our process, this needs to be done
-       when the last thread still runs */
-    ObKillProcess(Process);
+        /* Run the Notification Routines */
+        PspRunCreateProcessNotifyRoutines(Process, FALSE);
+    }
 
-    KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
-    
-    /* release the keep-alive reference of the process object */
-    ObDereferenceObject(Process);
+    /* Cleanup the power state */
+    PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState);
+
+    /* Clear the security port */
+    if (!Process->SecurityPort)
+    {
+        /* So we don't double-dereference */
+        Process->SecurityPort = (PVOID)1;
+    }
+    else if (Process->SecurityPort != (PVOID)1)
+    {
+        /* Dereference it */
+        ObDereferenceObject(Process->SecurityPort);
+        Process->SecurityPort = (PVOID)1;
+    }
+
+    /* Check if we are the last thread */
+    if (LastThread)
+    {
+        /* Check if we have to set the Timer Resolution */
+        if (Process->SetTimerResolution)
+        {
+            /* Set it to default */
+            ZwSetTimerResolution(KeMaximumIncrement, 0, &Actual);
+        }
+
+        /* Check if we are part of a Job that has a completion port */
+        if ((Process->Job) && (Process->Job->CompletionPort))
+        {
+            /* FIXME: Check job status code and do I/O completion if needed */
+        }
+
+        /* FIXME: Notify the Prefetcher */
+    }
+    else
+    {
+        /* Clear process' address space here */
+        MmCleanProcessAddressSpace(Process);
+    }
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+PsTerminateSystemThread(IN NTSTATUS ExitStatus)
+{
+    PETHREAD Thread = PsGetCurrentThread();
 
-    return(STATUS_SUCCESS);
+    /* Make sure this is a system thread */
+    if (!Thread->SystemThread) return STATUS_INVALID_PARAMETER;
+
+    /* Terminate it for real */
+    return PspTerminateThreadByPointer(Thread, ExitStatus, TRUE);
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
-STDCALL
-NtTerminateProcess(IN HANDLE ProcessHandle  OPTIONAL,
+NTAPI
+NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
                    IN NTSTATUS ExitStatus)
 {
     NTSTATUS Status;
-    PEPROCESS Process;
-    PETHREAD CurrentThread;
+    PEPROCESS Process, CurrentProcess = PsGetCurrentProcess();
+    PETHREAD Thread, CurrentThread = PsGetCurrentThread();
     BOOLEAN KillByHandle;
-
     PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG,
+            "ProcessHandle: %p ExitStatus: %p\n", ProcessHandle, ExitStatus);
 
-    DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
-            ProcessHandle, ExitStatus);
-
-    KillByHandle = (ProcessHandle != NULL);
+    /* Were we passed a process handle? */
+    if (ProcessHandle)
+    {
+        /* Yes we were, use it */
+        KillByHandle = TRUE;
+    }
+    else
+    {
+        /* We weren't... we assume this is suicide */
+        KillByHandle = FALSE;
+        ProcessHandle = NtCurrentProcess();
+    }
 
     /* Get the Process Object */
-    Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
+    Status = ObReferenceObjectByHandle(ProcessHandle,
                                        PROCESS_TERMINATE,
                                        PsProcessType,
                                        KeGetPreviousMode(),
                                        (PVOID*)&Process,
                                        NULL);
-    if (!NT_SUCCESS(Status)) {
+    if (!NT_SUCCESS(Status)) return(Status);
 
-        DPRINT1("Invalid handle to Process\n");
-        return(Status);
+    /* Check if this is a Critical Process, and Bugcheck */
+    if (Process->BreakOnTermination)
+    {
+        /* Break to debugger */
+        PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
+                              Process,
+                              Process->ImageFileName);
     }
 
-    CurrentThread = PsGetCurrentThread();
-
-    PsLockProcess(Process, FALSE);
-
-    if(Process->ExitTime.QuadPart != 0)
+    /* Lock the Process */
+    if (!ExAcquireRundownProtection(&Process->RundownProtect))
     {
-      PsUnlockProcess(Process);
-      ObDereferenceObject(Process);
-      return STATUS_PROCESS_IS_TERMINATING;
+        /* Failed to lock, fail */
+        ObDereferenceObject (Process);
+        return STATUS_PROCESS_IS_TERMINATING;
     }
 
-    /* Terminate all the Process's Threads */
-    PspTerminateProcessThreads(Process, ExitStatus);
-
-    /* only kill the calling thread if it either passed a process handle or
-       NtCurrentProcess() */
-    if (KillByHandle) {
+    /* Set the delete flag, unless the process is comitting suicide */
+    if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT);
 
-        /* set the exit time as we're about to release the process lock before
-           we kill ourselves to prevent threads outside of our process trying
-           to kill us */
-        KeQuerySystemTime(&Process->ExitTime);
-        Process->ExitStatus = ExitStatus;
+    /* Get the first thread */
+    Status = STATUS_NOTHING_TO_TERMINATE;
+    Thread = PsGetNextProcessThread(Process, NULL);
+    if (Thread)
+    {
+        /* We know we have at least a thread */
+        Status = STATUS_SUCCESS;
 
-        /* Only master thread remains... kill it off */
-        if (CurrentThread->ThreadsProcess == Process) {
+        /* Loop and kill the others */
+        do
+        {
+            /* Ensure it's not ours*/
+            if (Thread != CurrentThread)
+            {
+                /* Kill it */
+                PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
+            }
 
-            /* mark our thread as terminating so attempts to terminate it, when
-               unlocking the process, fail */
-            CurrentThread->Terminated = TRUE;
+            /* Move to the next thread */
+            Thread = PsGetNextProcessThread(Process, Thread);
+        } while (Thread);
+    }
 
-            PsUnlockProcess(Process);
+    /* Unlock the process */
+    ExReleaseRundownProtection(&Process->RundownProtect);
 
-            /* we can safely dereference the process because the current thread
-               holds a reference to it until it gets reaped */
+    /* Check if we are killing ourselves */
+    if (Process == CurrentProcess)
+    {
+        /* Also make sure the caller gave us our handle */
+        if (KillByHandle)
+        {
+            /* Dereference the process */
             ObDereferenceObject(Process);
 
-            /* now the other threads get a chance to terminate, we don't wait but
-               just kill ourselves right now. The process will be run down when the
-               last thread terminates */
+            /* Terminate ourselves */
+            PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE);
+        }
+    }
+    else if (ExitStatus == DBG_TERMINATE_PROCESS)
+    {
+        /* Disable debugging on this process */
+        DbgkClearProcessDebugObject(Process, NULL);
+    }
 
-            PspExitThread(ExitStatus);
+    /* Check if there was nothing to terminate, or if we have a Debug Port */
+    if ((Status == STATUS_NOTHING_TO_TERMINATE) ||
+        ((Process->DebugPort) && (KillByHandle)))
+    {
+        /* Clear the handle table */
+        ObClearProcessHandleTable(Process);
 
-            /* we should never reach this point! */
-            KEBUGCHECK(0);
-        }
+        /* Return status now */
+        Status = STATUS_SUCCESS;
     }
 
-    /* unlock and dereference the process so the threads can kill themselves */
-    PsUnlockProcess(Process);
+    /* Decrease the reference count we added */
     ObDereferenceObject(Process);
 
-    return(STATUS_SUCCESS);
+    /* Return status */
+    return Status;
 }
 
 NTSTATUS
-STDCALL
+NTAPI
 NtTerminateThread(IN HANDLE ThreadHandle,
                   IN NTSTATUS ExitStatus)
 {
     PETHREAD Thread;
+    PETHREAD CurrentThread = PsGetCurrentThread();
     NTSTATUS Status;
-
     PAGED_CODE();
-    
+    PSTRACE(PS_KILL_DEBUG,
+            "ThreadHandle: %p ExitStatus: %p\n", ThreadHandle, ExitStatus);
+
     /* Handle the special NULL case */
     if (!ThreadHandle)
     {
         /* Check if we're the only thread left */
-        if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
+        if (PsGetCurrentProcess()->ActiveThreads == 1)
         {
             /* This is invalid */
-            DPRINT1("Can't terminate self\n");
             return STATUS_CANT_TERMINATE_SELF;
         }
-        else
-        {
-            /* Use current handle */
-            ThreadHandle = NtCurrentThread();
-        }
+
+        /* Terminate us directly */
+        goto TerminateSelf;
+    }
+    else if (ThreadHandle == NtCurrentThread())
+    {
+TerminateSelf:
+        /* Terminate this thread */
+        return PspTerminateThreadByPointer(CurrentThread,
+                                           ExitStatus,
+                                           TRUE);
     }
 
-    /* Get the Thread Object */
+    /* We are terminating another thread, get the Thread Object */
     Status = ObReferenceObjectByHandle(ThreadHandle,
                                        THREAD_TERMINATE,
                                        PsThreadType,
                                        KeGetPreviousMode(),
                                        (PVOID*)&Thread,
                                        NULL);
-    if (!NT_SUCCESS(Status)) {
-
-        DPRINT1("Could not reference thread object\n");
-        return(Status);
-    }
-
-    /* Make sure this is not a system thread */
-    if (PsIsSystemThread(Thread)) {
-
-        DPRINT1("Trying to Terminate a system thread!\n");
-        ObDereferenceObject(Thread);
-        return STATUS_INVALID_PARAMETER;
-    }
+    if (!NT_SUCCESS(Status)) return Status;
 
     /* Check to see if we're running in the same thread */
-    if (Thread != PsGetCurrentThread())  {
-
-        /* we need to lock the process to make sure it's not already terminating */
-        PsLockProcess(Thread->ThreadsProcess, FALSE);
-
-        /* This isn't our thread, terminate it if not already done */
-        if (!Thread->Terminated) {
-
-             Thread->Terminated = TRUE;
-
-             /* Terminate it */
-             PspTerminateThreadByPointer(Thread, ExitStatus);
-        }
-
-        PsUnlockProcess(Thread->ThreadsProcess);
+    if (Thread != CurrentThread)
+    {
+        /* Terminate it */
+        Status = PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
 
         /* Dereference the Thread and return */
         ObDereferenceObject(Thread);
-
-    } else {
-
-        Thread->Terminated = TRUE;
-
-        /* it's safe to dereference thread, there's at least the keep-alive
-           reference which will be removed by the thread reaper causing the
-           thread to be finally destroyed */
-        ObDereferenceObject(Thread);
-
-        /* Terminate him, he's ours */
-        PspExitThread(ExitStatus);
-
-        /* We do never reach this point */
-        KEBUGCHECK(0);
     }
-
-    return(STATUS_SUCCESS);
-}
-
-/*
- * @implemented
- */
-NTSTATUS
-STDCALL
-PsTerminateSystemThread(NTSTATUS ExitStatus)
-{
-    PETHREAD Thread = PsGetCurrentThread();
-
-    /* Make sure this is a system thread */
-    if (!PsIsSystemThread(Thread)) {
-
-        DPRINT1("Trying to Terminate a non-system thread!\n");
-        return STATUS_INVALID_PARAMETER;
+    else
+    {
+        /* Dereference the thread and terminate ourselves */
+        ObDereferenceObject(Thread);
+        goto TerminateSelf;
     }
 
-    /* Terminate it for real */
-    PspExitThread(ExitStatus);
-
-    /* we should never reach this point! */
-    KEBUGCHECK(0);
-
-    return(STATUS_SUCCESS);
+    /* Return status */
+    return Status;
 }
 
 NTSTATUS
-STDCALL
-NtRegisterThreadTerminatePort(HANDLE PortHandle)
+NTAPI
+NtRegisterThreadTerminatePort(IN HANDLE PortHandle)
 {
     NTSTATUS Status;
     PTERMINATION_PORT TerminationPort;
     PVOID TerminationLpcPort;
     PETHREAD Thread;
-
     PAGED_CODE();
+    PSTRACE(PS_KILL_DEBUG, "PortHandle: %p\n", PortHandle);
 
     /* Get the Port */
     Status = ObReferenceObjectByHandle(PortHandle,
@@ -754,32 +1297,25 @@ NtRegisterThreadTerminatePort(HANDLE PortHandle)
                                        KeGetPreviousMode(),
                                        &TerminationLpcPort,
                                        NULL);
-    if (!NT_SUCCESS(Status)) {
-
-        DPRINT1("Failed to reference Port\n");
-        return(Status);
-    }
+    if (!NT_SUCCESS(Status)) return(Status);
 
     /* Allocate the Port and make sure it suceeded */
-    if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
-                                                sizeof(TERMINATION_PORT),
-                                                TAG('P', 's', 'T', '=')))) {
-
+    TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
+                                            sizeof(TERMINATION_PORT),
+                                            '=TsP');
+    if(TerminationPort)
+    {
         /* Associate the Port */
         Thread = PsGetCurrentThread();
         TerminationPort->Port = TerminationLpcPort;
-        DPRINT("TerminationPort: %p\n", TerminationPort);
         TerminationPort->Next = Thread->TerminationPort;
         Thread->TerminationPort = TerminationPort;
-        DPRINT("TerminationPort: %p\n", Thread->TerminationPort);
 
         /* Return success */
-        return(STATUS_SUCCESS);
-
-    } else {
-
-        /* Dereference and Fail */
-        ObDereferenceObject(TerminationPort);
-        return(STATUS_INSUFFICIENT_RESOURCES);
+        return STATUS_SUCCESS;
     }
+
+    /* Dereference and Fail */
+    ObDereferenceObject(TerminationPort);
+    return STATUS_INSUFFICIENT_RESOURCES;
 }