- Disable APC Queuing & Flush APC queues during thread shutdown.
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /*
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
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 PETHREAD PspReaperList = NULL;
20 WORK_QUEUE_ITEM PspReaperWorkItem;
21 BOOLEAN PspReaping = FALSE;
22 extern LIST_ENTRY PsActiveProcessHead;
23 extern FAST_MUTEX PspActiveProcessMutex;
24
25 /* FUNCTIONS *****************************************************************/
26
27 VOID
28 STDCALL
29 PspReapRoutine(PVOID Context)
30 {
31 KIRQL OldIrql;
32 PETHREAD Thread, NewThread;
33
34 /* Acquire lock */
35 DPRINT("Evil reaper running!!\n");
36 OldIrql = KeAcquireDispatcherDatabaseLock();
37
38 /* Get the first Thread Entry */
39 Thread = PspReaperList;
40 PspReaperList = NULL;
41 DPRINT("PspReaperList: %x\n", Thread);
42
43 /* Check to see if the list is empty */
44 do {
45
46 /* Unlock the Dispatcher */
47 KeReleaseDispatcherDatabaseLock(OldIrql);
48
49 /* Is there a thread on the list? */
50 while (Thread) {
51
52 /* Get the next Thread */
53 DPRINT("Thread: %x\n", Thread);
54 DPRINT("Thread: %x\n", Thread->ReaperLink);
55 NewThread = Thread->ReaperLink;
56
57 /* Remove reference to current thread */
58 ObDereferenceObject(Thread);
59
60 /* Move to next Thread */
61 Thread = NewThread;
62 }
63
64 /* No more linked threads... Reacquire the Lock */
65 OldIrql = KeAcquireDispatcherDatabaseLock();
66
67 /* Now try to get a new thread from the list */
68 Thread = PspReaperList;
69 PspReaperList = NULL;
70 DPRINT("PspReaperList: %x\n", Thread);
71
72 /* Loop again if there is a new thread */
73 } while (Thread);
74
75 PspReaping = FALSE;
76 DPRINT("Done reaping\n");
77 KeReleaseDispatcherDatabaseLock(OldIrql);
78 }
79
80 VOID
81 STDCALL
82 PspKillMostProcesses(VOID)
83 {
84 PLIST_ENTRY current_entry;
85 PEPROCESS current;
86
87 ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess->UniqueProcessId);
88
89 /* Acquire the Active Process Lock */
90 ExAcquireFastMutex(&PspActiveProcessMutex);
91
92 /* Loop all processes on the list */
93 current_entry = PsActiveProcessHead.Flink;
94 while (current_entry != &PsActiveProcessHead)
95 {
96 current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
97 current_entry = current_entry->Flink;
98
99 if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId)
100 {
101 /* Terminate all the Threads in this Process */
102 PspTerminateProcessThreads(current, STATUS_SUCCESS);
103 }
104 }
105
106 /* Release the lock */
107 ExReleaseFastMutex(&PspActiveProcessMutex);
108 }
109
110 VOID
111 STDCALL
112 PspTerminateProcessThreads(PEPROCESS Process,
113 NTSTATUS ExitStatus)
114 {
115 PLIST_ENTRY CurrentEntry;
116 PETHREAD Thread, CurrentThread = PsGetCurrentThread();
117
118 CurrentEntry = Process->ThreadListHead.Flink;
119 while (CurrentEntry != &Process->ThreadListHead) {
120
121 /* Get the Current Thread */
122 Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
123
124 /* Move to the Next Thread */
125 CurrentEntry = CurrentEntry->Flink;
126
127 /* Make sure it's not the one we're in */
128 if (Thread != CurrentThread) {
129
130 /* Make sure it didn't already terminate */
131 if (!Thread->Terminated) {
132
133 Thread->Terminated = TRUE;
134
135 /* Terminate it by APC */
136 PspTerminateThreadByPointer(Thread, ExitStatus);
137 }
138 }
139 }
140 }
141
142 VOID
143 STDCALL
144 PspDeleteProcess(PVOID ObjectBody)
145 {
146 PEPROCESS Process = (PEPROCESS)ObjectBody;
147
148 DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
149
150 /* Remove it from the Active List */
151 ExAcquireFastMutex(&PspActiveProcessMutex);
152 RemoveEntryList(&Process->ActiveProcessLinks);
153 ExReleaseFastMutex(&PspActiveProcessMutex);
154
155 /* Delete the CID Handle */
156 if(Process->UniqueProcessId != NULL) {
157
158 PsDeleteCidHandle(Process->UniqueProcessId, PsProcessType);
159 }
160
161 /* KDB hook */
162 KDB_DELETEPROCESS_HOOK(Process);
163
164 /* Dereference the Token */
165 SeDeassignPrimaryToken(Process);
166
167 /* Release Memory Information */
168 MmReleaseMmInfo(Process);
169
170 /* Delete the W32PROCESS structure if there's one associated */
171 if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
172 }
173
174 VOID
175 STDCALL
176 PspDeleteThread(PVOID ObjectBody)
177 {
178 PETHREAD Thread = (PETHREAD)ObjectBody;
179 PEPROCESS Process = Thread->ThreadsProcess;
180
181 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
182
183 /* Deassociate the Process */
184 Thread->ThreadsProcess = NULL;
185
186 /* Delete the CID Handle */
187 if(Thread->Cid.UniqueThread != NULL) {
188
189 PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
190 }
191
192 /* Free the W32THREAD structure if present */
193 if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
194
195 /* Release the Kernel Stack */
196 MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, FALSE);
197
198 /* Dereference the Process */
199 ObDereferenceObject(Process);
200 }
201
202 /*
203 * FUNCTION: Terminates the current thread
204 * See "Windows Internals" - Chapter 13, Page 50-53
205 */
206 VOID
207 STDCALL
208 PspExitThread(NTSTATUS ExitStatus)
209 {
210 PETHREAD CurrentThread;
211 BOOLEAN Last;
212 PEPROCESS CurrentProcess;
213 PTERMINATION_PORT TerminationPort;
214 PTEB Teb;
215 KIRQL oldIrql;
216 PLIST_ENTRY ApcEntry, CurrentApc;
217 PKAPC Apc;
218
219 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
220
221 /* Get the Current Thread and Process */
222 CurrentThread = PsGetCurrentThread();
223 CurrentProcess = CurrentThread->ThreadsProcess;
224
225 /* Set the Exit Status and Exit Time */
226 CurrentThread->ExitStatus = ExitStatus;
227 KeQuerySystemTime(&CurrentThread->ExitTime);
228
229 /* Can't terminate a thread if it attached another process */
230 if (KeIsAttachedProcess()) {
231
232 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
233 (ULONG) CurrentThread->Tcb.ApcState.Process,
234 (ULONG) CurrentThread->Tcb.ApcStateIndex,
235 (ULONG) CurrentThread);
236 }
237
238 /* Lower to Passive Level */
239 KeLowerIrql(PASSIVE_LEVEL);
240
241 /* Lock the Process before we modify its thread entries */
242 PsLockProcess(CurrentProcess, FALSE);
243
244 /* wake up the thread so we don't deadlock on PsLockProcess */
245 KeForceResumeThread(&CurrentThread->Tcb);
246
247 /* Run Thread Notify Routines before we desintegrate the thread */
248 PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
249
250 /* Remove the thread from the thread list of its process */
251 RemoveEntryList(&CurrentThread->ThreadListEntry);
252 Last = IsListEmpty(&CurrentProcess->ThreadListHead);
253
254 /* Set the last Thread Exit Status */
255 CurrentProcess->LastThreadExitStatus = ExitStatus;
256
257 if (Last) {
258
259 /* Save the Exit Time if not already done by NtTerminateProcess. This
260 happens when the last thread just terminates without explicitly
261 terminating the process. */
262 CurrentProcess->ExitTime = CurrentThread->ExitTime;
263 }
264
265 /* Check if the process has a debug port */
266 if (CurrentProcess->DebugPort) {
267
268 /* Notify the Debug API. TODO */
269 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
270 }
271
272 /* Process the Termination Ports */
273 while ((TerminationPort = CurrentThread->TerminationPort)) {
274
275 DPRINT("TerminationPort: %p\n", TerminationPort);
276
277 /* Get the next one */
278 CurrentThread->TerminationPort = TerminationPort->Next;
279
280 /* Send the LPC Message */
281 LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
282
283 /* Free the Port */
284 ExFreePool(TerminationPort);
285 }
286
287 /* Rundown Win32 Structures */
288 PsTerminateWin32Thread(CurrentThread);
289 if (Last) PsTerminateWin32Process(CurrentProcess);
290
291 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
292 //CmNotifyRunDown(CurrentThread);
293
294 /* Free the TEB */
295 if((Teb = CurrentThread->Tcb.Teb))
296 {
297 /* Clean up the stack first, if requested */
298 if (Teb->FreeStackOnTermination)
299 {
300 ULONG Dummy = 0;
301 ZwFreeVirtualMemory(NtCurrentProcess(),
302 &Teb->DeallocationStack,
303 &Dummy,
304 MEM_RELEASE);
305 }
306
307 DPRINT("Decommit teb at %p\n", Teb);
308 MmDeleteTeb(CurrentProcess, Teb);
309 CurrentThread->Tcb.Teb = NULL;
310 }
311
312 /* The last Thread shuts down the Process */
313 if (Last) PspExitProcess(CurrentProcess);
314
315 /* Unlock the Process */
316 PsUnlockProcess(CurrentProcess);
317
318 /* Cancel I/O for the thread. */
319 IoCancelThreadIo(CurrentThread);
320
321 /* Rundown Timers */
322 ExTimerRundown();
323 KeCancelTimer(&CurrentThread->Tcb.Timer);
324
325 /* If the Processor Control Block's NpxThread points to the current thread
326 * unset it.
327 */
328 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
329 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
330 NULL,
331 (PKPROCESS)CurrentThread);
332 KeLowerIrql(oldIrql);
333
334 /* Rundown Mutexes */
335 KeRundownThread();
336
337 /* Disable new APC Queuing, this is as far as we'll let them go */
338 KeDisableThreadApcQueueing(&CurrentThread->Tcb);
339
340 /* Flush the User APCs */
341 if ((ApcEntry = KeFlushQueueApc(&CurrentThread->Tcb, UserMode)))
342 {
343 CurrentApc = ApcEntry;
344 do
345 {
346 /* Get the APC */
347 Apc = CONTAINING_RECORD(CurrentApc, KAPC, ApcListEntry);
348
349 /* Move to the next one */
350 CurrentApc = CurrentApc->Flink;
351
352 /* Rundown the APC or de-allocate it */
353 if (Apc->RundownRoutine)
354 {
355 /* Call its own routine */
356 (Apc->RundownRoutine)(Apc);
357 }
358 else
359 {
360 /* Do it ourselves */
361 ExFreePool(Apc);
362 }
363 }
364 while (CurrentApc != ApcEntry);
365 }
366
367 /* Call the Lego routine */
368 if (CurrentThread->Tcb.LegoData) PspRunLegoRoutine(&CurrentThread->Tcb);
369
370 /* Flush the APC queue, which should be empty */
371 if ((ApcEntry = KeFlushQueueApc(&CurrentThread->Tcb, KernelMode)))
372 {
373 /* Bugcheck time */
374 KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT,
375 (ULONG_PTR)ApcEntry,
376 CurrentThread->Tcb.KernelApcDisable,
377 oldIrql,
378 0);
379 }
380
381 /* Terminate the Thread from the Scheduler */
382 KeTerminateThread(0);
383 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
384 KEBUGCHECK(0);
385 }
386
387 VOID
388 STDCALL
389 PsExitSpecialApc(PKAPC Apc,
390 PKNORMAL_ROUTINE* NormalRoutine,
391 PVOID* NormalContext,
392 PVOID* SystemArgument1,
393 PVOID* SystemArguemnt2)
394 {
395 DPRINT1("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n",
396 PsGetCurrentThread(), PsGetCurrentProcess());
397
398 /* Don't do anything unless we are in User-Mode */
399 if (Apc->SystemArgument2)
400 {
401 NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
402
403 /* Free the APC */
404 ExFreePool(Apc);
405
406 /* Terminate the Thread */
407 PspExitThread(ExitStatus);
408
409 /* we should never reach this point! */
410 KEBUGCHECK(0);
411 }
412 }
413
414 VOID
415 STDCALL
416 PspExitNormalApc(PVOID NormalContext,
417 PVOID SystemArgument1,
418 PVOID SystemArgument2)
419 {
420 PKAPC Apc = (PKAPC)SystemArgument1;
421 PETHREAD Thread = PsGetCurrentThread();
422 NTSTATUS ExitStatus;
423
424 DPRINT1("PspExitNormalApc called: 0x%x (proc: 0x%x)\n",
425 PsGetCurrentThread(), PsGetCurrentProcess());
426
427 /* This should never happen */
428 ASSERT(!SystemArgument2);
429
430 /* If this is a system thread, we can safely kill it from Kernel-Mode */
431 if (PsIsSystemThread(Thread))
432 {
433 /* Get the Exit Status */
434 DPRINT1("Killing System Thread\n");
435 ExitStatus = (NTSTATUS)Apc->NormalContext;
436
437 /* Free the APC */
438 ExFreePool(Apc);
439
440 /* Exit the Thread */
441 PspExitThread(ExitStatus);
442 }
443
444 /* If we're here, this is not a System Thread, so kill it from User-Mode */
445 DPRINT1("Initializing User-Mode APC\n");
446 KeInitializeApc(Apc,
447 &Thread->Tcb,
448 OriginalApcEnvironment,
449 PsExitSpecialApc,
450 NULL,
451 PspExitNormalApc,
452 UserMode,
453 NormalContext);
454
455 /* Now insert the APC with the User-Mode Flag */
456 KeInsertQueueApc(Apc, Apc, (PVOID)UserMode, 2);
457
458 /* Forcefully resume the thread */
459 KeForceResumeThread(&Thread->Tcb);
460 }
461
462 /*
463 * See "Windows Internals" - Chapter 13, Page 49
464 */
465 VOID
466 STDCALL
467 PspTerminateThreadByPointer(PETHREAD Thread,
468 NTSTATUS ExitStatus)
469 {
470 PKAPC Apc;
471
472 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
473 Thread, ExitStatus);
474
475 /* Check if we are already in the right context */
476 if (PsGetCurrentThread() == Thread) {
477
478 /* Directly terminate the thread */
479 PspExitThread(ExitStatus);
480
481 /* we should never reach this point! */
482 KEBUGCHECK(0);
483 }
484
485 /* Allocate the APC */
486 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
487
488 /* Initialize a Kernel Mode APC to Kill the Thread */
489 KeInitializeApc(Apc,
490 &Thread->Tcb,
491 OriginalApcEnvironment,
492 PsExitSpecialApc,
493 NULL,
494 PspExitNormalApc,
495 KernelMode,
496 (PVOID)ExitStatus);
497
498 /* Insert it into the APC Queue */
499 KeInsertQueueApc(Apc,
500 Apc,
501 NULL,
502 2);
503
504 /* Forcefully resume the thread */
505 KeForceResumeThread(&Thread->Tcb);
506 }
507
508 NTSTATUS
509 STDCALL
510 PspExitProcess(PEPROCESS Process)
511 {
512 DPRINT("PspExitProcess 0x%x\n", Process);
513
514 PspRunCreateProcessNotifyRoutines(Process, FALSE);
515
516 /* close all handles associated with our process, this needs to be done
517 when the last thread still runs */
518 ObKillProcess(Process);
519
520 KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
521
522 return(STATUS_SUCCESS);
523 }
524
525 NTSTATUS
526 STDCALL
527 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
528 IN NTSTATUS ExitStatus)
529 {
530 NTSTATUS Status;
531 PEPROCESS Process;
532 PETHREAD CurrentThread;
533 BOOLEAN KillByHandle;
534
535 PAGED_CODE();
536
537 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
538 ProcessHandle, ExitStatus);
539
540 KillByHandle = (ProcessHandle != NULL);
541
542 /* Get the Process Object */
543 Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
544 PROCESS_TERMINATE,
545 PsProcessType,
546 KeGetPreviousMode(),
547 (PVOID*)&Process,
548 NULL);
549 if (!NT_SUCCESS(Status)) {
550
551 DPRINT1("Invalid handle to Process\n");
552 return(Status);
553 }
554
555 CurrentThread = PsGetCurrentThread();
556
557 PsLockProcess(Process, FALSE);
558
559 if(Process->ExitTime.QuadPart != 0)
560 {
561 PsUnlockProcess(Process);
562 ObDereferenceObject(Process);
563 return STATUS_PROCESS_IS_TERMINATING;
564 }
565
566 /* Terminate all the Process's Threads */
567 PspTerminateProcessThreads(Process, ExitStatus);
568
569 /* only kill the calling thread if it either passed a process handle or
570 NtCurrentProcess() */
571 if (KillByHandle) {
572
573 /* set the exit time as we're about to release the process lock before
574 we kill ourselves to prevent threads outside of our process trying
575 to kill us */
576 KeQuerySystemTime(&Process->ExitTime);
577
578 /* Only master thread remains... kill it off */
579 if (CurrentThread->ThreadsProcess == Process) {
580
581 /* mark our thread as terminating so attempts to terminate it, when
582 unlocking the process, fail */
583 CurrentThread->Terminated = TRUE;
584
585 PsUnlockProcess(Process);
586
587 /* we can safely dereference the process because the current thread
588 holds a reference to it until it gets reaped */
589 ObDereferenceObject(Process);
590
591 /* now the other threads get a chance to terminate, we don't wait but
592 just kill ourselves right now. The process will be run down when the
593 last thread terminates */
594
595 PspExitThread(ExitStatus);
596
597 /* we should never reach this point! */
598 KEBUGCHECK(0);
599 }
600 }
601
602 /* unlock and dereference the process so the threads can kill themselves */
603 PsUnlockProcess(Process);
604 ObDereferenceObject(Process);
605
606 return(STATUS_SUCCESS);
607 }
608
609 NTSTATUS
610 STDCALL
611 NtTerminateThread(IN HANDLE ThreadHandle,
612 IN NTSTATUS ExitStatus)
613 {
614 PETHREAD Thread;
615 NTSTATUS Status;
616
617 PAGED_CODE();
618
619 /* Handle the special NULL case */
620 if (!ThreadHandle)
621 {
622 /* Check if we're the only thread left */
623 if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
624 {
625 /* This is invalid */
626 DPRINT1("Can't terminate self\n");
627 return STATUS_CANT_TERMINATE_SELF;
628 }
629 else
630 {
631 /* Use current handle */
632 ThreadHandle = NtCurrentThread();
633 }
634 }
635
636 /* Get the Thread Object */
637 Status = ObReferenceObjectByHandle(ThreadHandle,
638 THREAD_TERMINATE,
639 PsThreadType,
640 KeGetPreviousMode(),
641 (PVOID*)&Thread,
642 NULL);
643 if (!NT_SUCCESS(Status)) {
644
645 DPRINT1("Could not reference thread object\n");
646 return(Status);
647 }
648
649 /* Make sure this is not a system thread */
650 if (PsIsSystemThread(Thread)) {
651
652 DPRINT1("Trying to Terminate a system thread!\n");
653 ObDereferenceObject(Thread);
654 return STATUS_INVALID_PARAMETER;
655 }
656
657 /* Check to see if we're running in the same thread */
658 if (Thread != PsGetCurrentThread()) {
659
660 /* we need to lock the process to make sure it's not already terminating */
661 PsLockProcess(Thread->ThreadsProcess, FALSE);
662
663 /* This isn't our thread, terminate it if not already done */
664 if (!Thread->Terminated) {
665
666 Thread->Terminated = TRUE;
667
668 /* Terminate it */
669 PspTerminateThreadByPointer(Thread, ExitStatus);
670 }
671
672 PsUnlockProcess(Thread->ThreadsProcess);
673
674 /* Dereference the Thread and return */
675 ObDereferenceObject(Thread);
676
677 } else {
678
679 Thread->Terminated = TRUE;
680
681 /* it's safe to dereference thread, there's at least the keep-alive
682 reference which will be removed by the thread reaper causing the
683 thread to be finally destroyed */
684 ObDereferenceObject(Thread);
685
686 /* Terminate him, he's ours */
687 PspExitThread(ExitStatus);
688
689 /* We do never reach this point */
690 KEBUGCHECK(0);
691 }
692
693 return(STATUS_SUCCESS);
694 }
695
696 /*
697 * @implemented
698 */
699 NTSTATUS
700 STDCALL
701 PsTerminateSystemThread(NTSTATUS ExitStatus)
702 {
703 PETHREAD Thread = PsGetCurrentThread();
704
705 /* Make sure this is a system thread */
706 if (!PsIsSystemThread(Thread)) {
707
708 DPRINT1("Trying to Terminate a non-system thread!\n");
709 return STATUS_INVALID_PARAMETER;
710 }
711
712 /* Terminate it for real */
713 PspExitThread(ExitStatus);
714
715 /* we should never reach this point! */
716 KEBUGCHECK(0);
717
718 return(STATUS_SUCCESS);
719 }
720
721 NTSTATUS
722 STDCALL
723 NtRegisterThreadTerminatePort(HANDLE PortHandle)
724 {
725 NTSTATUS Status;
726 PTERMINATION_PORT TerminationPort;
727 PVOID TerminationLpcPort;
728 PETHREAD Thread;
729
730 PAGED_CODE();
731
732 /* Get the Port */
733 Status = ObReferenceObjectByHandle(PortHandle,
734 PORT_ALL_ACCESS,
735 LpcPortObjectType,
736 KeGetPreviousMode(),
737 &TerminationLpcPort,
738 NULL);
739 if (!NT_SUCCESS(Status)) {
740
741 DPRINT1("Failed to reference Port\n");
742 return(Status);
743 }
744
745 /* Allocate the Port and make sure it suceeded */
746 if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
747 sizeof(PTERMINATION_PORT),
748 TAG('P', 's', 'T', '=')))) {
749
750 /* Associate the Port */
751 Thread = PsGetCurrentThread();
752 TerminationPort->Port = TerminationLpcPort;
753 DPRINT("TerminationPort: %p\n", TerminationPort);
754 TerminationPort->Next = Thread->TerminationPort;
755 Thread->TerminationPort = TerminationPort;
756 DPRINT("TerminationPort: %p\n", Thread->TerminationPort);
757
758 /* Return success */
759 return(STATUS_SUCCESS);
760
761 } else {
762
763 /* Dereference and Fail */
764 ObDereferenceObject(TerminationPort);
765 return(STATUS_INSUFFICIENT_RESOURCES);
766 }
767 }