fixed a few race conditions during thread/process termination leading to dead-locks
[reactos.git] / reactos / ntoskrnl / ps / kill.c
index 9c92f24..cda7b57 100644 (file)
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ps/kill.c
- * PURPOSE:         Terminating a thread
- * PROGRAMMER:      David Welch (welch@cwcom.net)
- * UPDATE HISTORY:
- *                  Created 22/05/98
+ * PURPOSE:         Thread Termination and Reaping
+ * 
+ * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
+ *                  David Welch (welch@cwcom.net)
  */
 
 /* INCLUDES *****************************************************************/
 
-#include <ddk/ntddk.h>
-#include <internal/ps.h>
-#include <internal/ke.h>
-#include <internal/mm.h>
-#include <internal/ob.h>
-
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 /* GLOBALS *******************************************************************/
+      
+#define TAG_TERMINATE_APC   TAG('T', 'A', 'P', 'C')
 
-extern ULONG PiNrThreads;
+PETHREAD PspReaperList = NULL;
+WORK_QUEUE_ITEM PspReaperWorkItem;
+BOOLEAN PspReaping = FALSE;
+extern LIST_ENTRY PsActiveProcessHead;
+extern FAST_MUTEX PspActiveProcessMutex;
 
 /* FUNCTIONS *****************************************************************/
 
-VOID PsTerminateCurrentThread(NTSTATUS ExitStatus)
+STDCALL
+VOID
+PspReapRoutine(PVOID Context)
+{
+    KIRQL OldIrql;
+    PETHREAD Thread, NewThread;
+    /* Acquire lock */
+    DPRINT("Evil reaper running!!\n");
+    OldIrql = KeAcquireDispatcherDatabaseLock();
+    
+    /* Get the first Thread Entry */
+    Thread = PspReaperList;
+    PspReaperList = NULL;
+    DPRINT("PspReaperList: %x\n", Thread);
+    
+    /* Check to see if the list is empty */
+    do {
+        
+        /* Unlock the Dispatcher */
+        KeReleaseDispatcherDatabaseLock(OldIrql);
+        
+        /* Is there a thread on the list? */
+        while (Thread) {
+            
+            /* Get the next Thread */
+            DPRINT("Thread: %x\n", Thread);
+            DPRINT("Thread: %x\n", Thread->ReaperLink);
+            NewThread = Thread->ReaperLink;
+            
+            /* Remove reference to current thread */
+            ObDereferenceObject(Thread);
+        
+            /* Move to next Thread */
+            Thread = NewThread;
+        }
+        
+        /* No more linked threads... Reacquire the Lock */
+        OldIrql = KeAcquireDispatcherDatabaseLock();
+        
+        /* Now try to get a new thread from the list */
+        Thread = PspReaperList;
+        PspReaperList = NULL;
+        DPRINT("PspReaperList: %x\n", Thread);
+        
+        /* Loop again if there is a new thread */
+    } while (Thread);
+    
+    PspReaping = FALSE;
+    DPRINT("Done reaping\n");
+    KeReleaseDispatcherDatabaseLock(OldIrql);
+}
+
+VOID
+STDCALL
+PspTerminateProcessThreads(PEPROCESS Process,
+                           NTSTATUS ExitStatus)
+{
+    PLIST_ENTRY CurrentEntry;
+    PETHREAD Thread, CurrentThread = PsGetCurrentThread();
+   
+    CurrentEntry = Process->ThreadListHead.Flink;
+    while (CurrentEntry != &Process->ThreadListHead) {
+       
+        /* Get the Current Thread */
+        Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
+        
+        /* Move to the Next Thread */
+        CurrentEntry = CurrentEntry->Flink;
+        
+        /* Make sure it's not the one we're in */
+        if (Thread != CurrentThread) {
+        
+            /* Make sure it didn't already terminate */
+            if (!Thread->HasTerminated) {
+
+                Thread->HasTerminated = TRUE;
+                
+                /* Terminate it by APC */
+                PspTerminateThreadByPointer(Thread, ExitStatus);
+            }
+        }
+    }
+}
+
+VOID 
+STDCALL 
+PspDeleteProcess(PVOID ObjectBody)
+{
+    PEPROCESS Process = (PEPROCESS)ObjectBody;
+
+    DPRINT("PiDeleteProcess(ObjectBody %x)\n",Process);
+
+    /* Delete the CID Handle */   
+    if(Process->UniqueProcessId != NULL) {
+    
+        PsDeleteCidHandle(Process->UniqueProcessId, PsProcessType);
+    }
+    
+    /* KDB hook */
+    KDB_DELETEPROCESS_HOOK(Process);
+    
+    /* Dereference the Token and release Memory Information */
+    ObDereferenceObject(Process->Token);
+    MmReleaseMmInfo(Process);
+    /* Delete the W32PROCESS structure if there's one associated */
+    if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
+}
+
+VOID 
+STDCALL
+PspDeleteThread(PVOID ObjectBody)
+{
+    PETHREAD Thread = (PETHREAD)ObjectBody;
+    PEPROCESS Process = Thread->ThreadsProcess;
+
+    DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
+
+    /* Deassociate the Process */
+    Thread->ThreadsProcess = NULL;
+
+    /* Delete the CID Handle */
+    if(Thread->Cid.UniqueThread != NULL) {
+        
+        PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
+    }
+  
+    /* Free the W32THREAD structure if present */
+    if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
+
+    /* Release the Thread */
+    KeReleaseThread(ETHREAD_TO_KTHREAD(Thread)); 
+    
+    /* Dereference the Process */
+    ObDereferenceObject(Process);
+}
+              
 /*
  * FUNCTION: Terminates the current thread
+ * See "Windows Internals" - Chapter 13, Page 50-53
  */
+VOID
+STDCALL
+PspExitThread(NTSTATUS ExitStatus)
 {
-   KIRQL oldlvl;
-   PETHREAD CurrentThread;
-   
-   PiNrThreads--;
-   
-   CurrentThread = PsGetCurrentThread();
+    PETHREAD CurrentThread;
+    BOOLEAN Last;
+    PEPROCESS CurrentProcess;
+    SIZE_T Length = PAGE_SIZE;
+    PVOID TebBlock;
+    PTERMINATION_PORT TerminationPort;
+
+    DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
+
+    /* Get the Current Thread and Process */
+    CurrentThread = PsGetCurrentThread();
+    CurrentProcess = CurrentThread->ThreadsProcess;
+    
+    /* Set the Exit Status and Exit Time */
+    CurrentThread->ExitStatus = ExitStatus;
+    KeQuerySystemTime(&CurrentThread->ExitTime);
+
+    /* 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);
+    }
+    
+    /* Lower to Passive Level */
+    KeLowerIrql(PASSIVE_LEVEL);
+
+    /* Lock the Process before we modify its thread entries */
+    PsLockProcess(CurrentProcess, FALSE);
+    
+    /* wake up the thread so we don't deadlock on PsLockProcess */
+    KeForceResumeThread(&CurrentThread->Tcb);
+    
+    /* Run Thread Notify Routines before we desintegrate the thread */
+    PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
+
+    /* Remove the thread from the thread list of its process */
+    RemoveEntryList(&CurrentThread->ThreadListEntry);
+    Last = IsListEmpty(&CurrentProcess->ThreadListHead);
+    
+    /* Set the last Thread Exit Status */
+    CurrentProcess->LastThreadExitStatus = ExitStatus;
+    
+    if (Last) {
+
+       /* 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;
+    }
+    
+    /* Check if the process has a debug port */
+    if (CurrentProcess->DebugPort) {
+    
+        /* Notify the Debug API. TODO */
+        //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
+    }
+    
+    /* Process the Termination Ports */  
+    TerminationPort = CurrentThread->TerminationPort;
+    DPRINT("TerminationPort: %p\n", TerminationPort);
+    while (TerminationPort) {
+        
+        /* Send the LPC Message */
+        LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
+        
+        /* Free the Port */
+        ExFreePool(TerminationPort);
+        
+        /* Get the next one */
+        TerminationPort = TerminationPort->Next;
+        DPRINT("TerminationPort: %p\n", TerminationPort);
+    }
+      
+    /* Rundown Win32 Structures */
+    PsTerminateWin32Thread(CurrentThread);
+    if (Last) PsTerminateWin32Process(CurrentProcess);
    
-   CurrentThread->ExitStatus = ExitStatus;
+    /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
+    //CmNotifyRunDown(CurrentThread);
+    
+    /* Free the TEB */
+    if(CurrentThread->Tcb.Teb) {
+
+        DPRINT("Decommit teb at %p\n", CurrentThread->Tcb.Teb);
+        TebBlock = MM_ROUND_DOWN(CurrentThread->Tcb.Teb, MM_VIRTMEM_GRANULARITY);
+
+        ZwFreeVirtualMemory(NtCurrentProcess(),
+                           (PVOID *)&CurrentThread->Tcb.Teb,
+                           &Length,
+                           MEM_DECOMMIT);
+
+        DPRINT("teb %p, TebBlock %p\n", CurrentThread->Tcb.Teb, TebBlock);
+
+        if (TebBlock != CurrentProcess->TebBlock ||
+            CurrentProcess->TebBlock == CurrentProcess->TebLastAllocated) {
+
+            MmLockAddressSpace(&CurrentProcess->AddressSpace);
+            MmReleaseMemoryAreaIfDecommitted(CurrentProcess, &CurrentProcess->AddressSpace, TebBlock);
+            MmUnlockAddressSpace(&CurrentProcess->AddressSpace);
+        }
+
+        CurrentThread->Tcb.Teb = NULL;
+    }
    
-   DPRINT("terminating %x\n",CurrentThread);
-   ObDereferenceObject(CurrentThread->ThreadsProcess);
-   CurrentThread->ThreadsProcess = NULL;
-   KeRaiseIrql(DISPATCH_LEVEL,&oldlvl);
-   CurrentThread->Tcb.State = THREAD_STATE_TERMINATED;
-   ZwYieldExecution();
-   for(;;);
+    /* The last Thread shuts down the Process */
+    if (Last) PspExitProcess(CurrentProcess);
+    
+    /* Unlock the Process */
+    PsUnlockProcess(CurrentProcess);
+    
+    /* Cancel I/O for the thread. */
+    IoCancelThreadIo(CurrentThread);
+    
+    /* Rundown Timers */
+    ExTimerRundown();
+    KeCancelTimer(&CurrentThread->Tcb.Timer);
+    
+    /* If the Processor Control Block's NpxThread points to the current thread
+     * unset it.
+     */
+    InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
+                                      NULL,
+                                      (PKPROCESS)CurrentThread);
+    
+    /* Rundown Mutexes */
+    KeRundownThread();
+
+    /* 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)
+{
+    NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
+    
+    DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n", PsGetCurrentThread(), PsGetCurrentProcess());
+    
+    /* Free the APC */
+    ExFreePool(Apc);
+    
+    /* Terminate the Thread */
+    PspExitThread(ExitStatus);
+    
+    /* we should never reach this point! */
+    KEBUGCHECK(0);
+}
+
+VOID 
+STDCALL
+PspExitNormalApc(PVOID NormalContext,
+                 PVOID SystemArgument1,
+                 PVOID SystemArgument2)
+{
+    /* Not fully supported yet... must work out some issues that 
+     * I don't understand yet -- Alex 
+     */
+    DPRINT1("APC2\n");
+    PspExitThread((NTSTATUS)NormalContext);
+    
+    /* we should never reach this point! */
+    KEBUGCHECK(0);
 }
 
-VOID PsTerminateOtherThread(PETHREAD Thread, NTSTATUS ExitStatus)
 /*
- * FUNCTION: Terminate a thread when calling from that thread's context
+ * See "Windows Internals" - Chapter 13, Page 49
  */
+VOID
+STDCALL
+PspTerminateThreadByPointer(PETHREAD Thread,
+                            NTSTATUS ExitStatus)
 {
-   KIRQL oldlvl;
-   
-   PiNrThreads--;
-   KeRaiseIrql(DISPATCH_LEVEL, &oldlvl);
-   Thread->Tcb.State = THREAD_STATE_TERMINATED;
-   ObDereferenceObject(Thread->ThreadsProcess);
-   Thread->ThreadsProcess = NULL;
-   KeLowerIrql(oldlvl);
+    PKAPC Apc;  
+    
+    DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
+            Thread, ExitStatus);
+    
+    /* Check if we are already in the right context */
+    if (PsGetCurrentThread() == Thread) {
+
+        /* Directly terminate the thread */
+        PspExitThread(ExitStatus);
+
+        /* we should never reach this point! */
+        KEBUGCHECK(0);
+    }
+    
+    /* Allocate the APC */
+    Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
+    
+    /* Initialize a Kernel Mode APC to Kill the Thread */
+    KeInitializeApc(Apc,
+                    &Thread->Tcb,
+                    OriginalApcEnvironment,
+                    PsExitSpecialApc,
+                    NULL,
+                    PspExitNormalApc,
+                    KernelMode,
+                    (PVOID)ExitStatus);
+    
+    /* Insert it into the APC Queue */
+    KeInsertQueueApc(Apc,
+                     Apc,
+                     NULL,
+                     2);
+    
+    /* Forcefully resume the thread */
+    KeForceResumeThread(&Thread->Tcb);
 }
+   
+NTSTATUS 
+STDCALL
+PspExitProcess(PEPROCESS Process)
+{
+    DPRINT("PspExitProcess 0x%x\n", Process);
+           
+    PspRunCreateProcessNotifyRoutines(Process, FALSE);
+           
+    /* Remove it from the Active List */
+    ExAcquireFastMutex(&PspActiveProcessMutex);
+    RemoveEntryList(&Process->ProcessListEntry);
+    ExReleaseFastMutex(&PspActiveProcessMutex);
+    
+    /* close all handles associated with our process, this needs to be done
+       when the last thread still runs */
+    ObKillProcess(Process);
 
+    KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
+    
+    return(STATUS_SUCCESS);
+}
 
-NTSTATUS
+NTSTATUS 
 STDCALL
-NtTerminateProcess (
-       IN      HANDLE          ProcessHandle,
-       IN      NTSTATUS        ExitStatus
-       )
+NtTerminateProcess(IN HANDLE ProcessHandle  OPTIONAL,
+                   IN NTSTATUS ExitStatus)
 {
-   NTSTATUS Status;
-   PEPROCESS Process;
-   KIRQL oldlvl;
+    NTSTATUS Status;
+    PEPROCESS Process;
+    PETHREAD CurrentThread;
+    BOOLEAN KillByHandle;
    
-   DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
-          ProcessHandle, ExitStatus);
+    PAGED_CODE();
    
-   Status = ObReferenceObjectByHandle(ProcessHandle,
-                                      PROCESS_TERMINATE,
-                                      PsProcessType,
-                                     UserMode,
-                                      (PVOID*)&Process,
-                                     NULL);
-   if (Status != STATUS_SUCCESS)
-   {
+    DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
+            ProcessHandle, ExitStatus);
+    
+    KillByHandle = (ProcessHandle != NULL);
+
+    /* Get the Process Object */
+    Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
+                                       PROCESS_TERMINATE,
+                                       PsProcessType,
+                                       KeGetPreviousMode(),
+                                       (PVOID*)&Process,
+                                       NULL);
+    if (!NT_SUCCESS(Status)) {
+
+        DPRINT1("Invalid handle to Process\n");
         return(Status);
-   }
-   
-   PiTerminateProcessThreads(Process, ExitStatus);
-   KeRaiseIrql(DISPATCH_LEVEL, &oldlvl);
-   Process->Pcb.ProcessState = PROCESS_STATE_TERMINATED;
-   KeDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
-   if (PsGetCurrentThread()->ThreadsProcess == Process)
-   {
-      KeLowerIrql(oldlvl);
-      ObDereferenceObject(Process);
-      PsTerminateCurrentThread(ExitStatus);
-   }
-   KeLowerIrql(oldlvl);
-   ObDereferenceObject(Process);
-   return(STATUS_SUCCESS);
-}
+    }
+    
+    CurrentThread = PsGetCurrentThread();
+    
+    PsLockProcess(Process, FALSE);
+    
+    if(Process->ExitTime.QuadPart != 0)
+    {
+      PsUnlockProcess(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 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);
 
-NTSTATUS
-STDCALL
-NtTerminateThread (
-       IN      HANDLE          ThreadHandle,
-       IN      NTSTATUS        ExitStatus
-       )
-{
-   PETHREAD Thread;
-   NTSTATUS Status;
-   
-   Status = ObReferenceObjectByHandle(ThreadHandle,
-                                     THREAD_TERMINATE,
-                                     PsThreadType,
-                                     UserMode,
-                                     (PVOID*)&Thread,
-                                     NULL);
-   if (Status != STATUS_SUCCESS)
-     {
-       return(Status);
-     }
-
-   if (Thread == PsGetCurrentThread())
-     {
-       PsTerminateCurrentThread(ExitStatus);
-     }
-   else
-     {
-       PsTerminateOtherThread(Thread, ExitStatus);
-     }
-   return(STATUS_SUCCESS);
+        /* Only master thread remains... kill it off */
+        if (CurrentThread->ThreadsProcess == Process) {
+
+            /* mark our thread as terminating so attempts to terminate it, when
+               unlocking the process, fail */
+            CurrentThread->HasTerminated = TRUE;
+
+            PsUnlockProcess(Process);
+
+            /* we can safely dereference the process because the current thread
+               holds a reference to it until it gets reaped */
+            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 */
+
+            PspExitThread(ExitStatus);
+
+            /* we should never reach this point! */
+            KEBUGCHECK(0);
+        }
+    }
+
+    /* unlock and dereference the process so the threads can kill themselves */
+    PsUnlockProcess(Process);
+    ObDereferenceObject(Process);
+    
+    return(STATUS_SUCCESS);
 }
 
-VOID PsReleaseThread(PETHREAD Thread)
-/*
- * FUNCTION: Called internally to release a thread's resources from another
- * thread's context
- * ARGUMENTS:
- *        Thread = Thread to release
- */
+NTSTATUS 
+STDCALL
+NtTerminateThread(IN HANDLE ThreadHandle,
+                  IN NTSTATUS ExitStatus)
 {
-   DPRINT("PsReleaseThread(Thread %x)\n",Thread);
+    PETHREAD Thread;
+    NTSTATUS Status;
    
-   RemoveEntryList(&Thread->Tcb.Entry);
-   HalReleaseTask(Thread);
-   ObDereferenceObject(Thread);
-}
+    PAGED_CODE();
+    
+    /* 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;
+    }
+     
+    /* 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->HasTerminated) {
+         
+             Thread->HasTerminated = TRUE;
+             
+             /* Terminate it */
+             PspTerminateThreadByPointer(Thread, ExitStatus);
+        }
+        
+        PsUnlockProcess(Thread->ThreadsProcess);
+        
+        /* Dereference the Thread and return */
+        ObDereferenceObject(Thread);
+        
+    } else {
 
+        Thread->HasTerminated = 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);
+}
 
-NTSTATUS PsTerminateSystemThread(NTSTATUS ExitStatus)
 /*
- * FUNCTION: Terminates the current thread
- * ARGUMENTS:
- *         ExitStatus = Status to pass to the creater
- * RETURNS: Doesn't
+ * @implemented
  */
+NTSTATUS 
+STDCALL
+PsTerminateSystemThread(NTSTATUS ExitStatus)
 {
-   PsTerminateCurrentThread(ExitStatus);
-   return(STATUS_SUCCESS);
+    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;
+    }
+        
+    /* Terminate it for real */
+    PspExitThread(ExitStatus);
+    
+    /* we should never reach this point! */
+    KEBUGCHECK(0);
+    
+    return(STATUS_SUCCESS);
 }
 
-
-NTSTATUS
+NTSTATUS 
 STDCALL
-NtRegisterThreadTerminatePort (
-       HANDLE  TerminationPort
-       )
+NtRegisterThreadTerminatePort(HANDLE PortHandle)
 {
-       UNIMPLEMENTED;
+    NTSTATUS Status;
+    PTERMINATION_PORT TerminationPort;
+    PVOID TerminationLpcPort;
+    PETHREAD Thread;
+   
+    PAGED_CODE();
+    
+    /* Get the Port */
+    Status = ObReferenceObjectByHandle(PortHandle,
+                                       PORT_ALL_ACCESS,
+                                       LpcPortObjectType,
+                                       KeGetPreviousMode(),
+                                       &TerminationLpcPort,
+                                       NULL);   
+    if (!NT_SUCCESS(Status)) {
+        
+        DPRINT1("Failed to reference Port\n");
+        return(Status);
+    }
+   
+    /* Allocate the Port and make sure it suceeded */
+    if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool, 
+                                                sizeof(PTERMINATION_PORT), 
+                                                TAG('P', 's', 'T', '=')))) {
+        
+        /* 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);
+    }
 }