- Removed PiApcLock, because apc's are thread local and not global.
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/ke/apc.c
22 * PURPOSE: Possible implementation of APCs
23 * PROGRAMMER: David Welch (welch@cwcom.net)
24 * PORTABILITY: Unchecked
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 * 12/11/99: Phillip Susi: Reworked the APC code
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ntoskrnl.h>
33 #define NDEBUG
34 #include <internal/debug.h>
35
36 /* GLOBALS *******************************************************************/
37
38 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
39
40 #define TAG_KAPC TAG('K', 'A', 'P', 'C')
41
42 /* FUNCTIONS *****************************************************************/
43
44 /*
45 * @implemented
46 */
47 BOOLEAN
48 STDCALL
49 KeAreApcsDisabled(
50 VOID
51 )
52 {
53 return KeGetCurrentThread()->KernelApcDisable ? FALSE : TRUE;
54 }
55
56 VOID KiRundownThread(VOID)
57 /*
58 * FUNCTION:
59 */
60 {
61 }
62
63 BOOLEAN KiTestAlert(VOID)
64 /*
65 * FUNCTION: Tests whether there are any pending APCs for the current thread
66 * and if so the APCs will be delivered on exit from kernel mode
67 */
68 {
69 KIRQL oldIrql;
70
71 oldIrql = KeRaiseIrqlToDpcLevel();
72 if (KeGetCurrentThread()->ApcState.UserApcPending == 0)
73 {
74 KeLowerIrql(oldIrql);
75 return(FALSE);
76 }
77 KeGetCurrentThread()->Alerted[0] = 1;
78 KeLowerIrql(oldIrql);
79 return(TRUE);
80 }
81
82 VOID
83 KiDeliverNormalApc(VOID)
84 {
85 PETHREAD Thread = PsGetCurrentThread();
86 PLIST_ENTRY current;
87 PKAPC Apc;
88 KIRQL oldlvl;
89 PKNORMAL_ROUTINE NormalRoutine;
90 PVOID NormalContext;
91 PVOID SystemArgument1;
92 PVOID SystemArgument2;
93
94 oldlvl = KeRaiseIrqlToDpcLevel();
95 while(!IsListEmpty(&(Thread->Tcb.ApcState.ApcListHead[0])))
96 {
97 current = RemoveTailList(&Thread->Tcb.ApcState.ApcListHead[0]);
98 Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
99 if (Apc->NormalRoutine == NULL)
100 {
101 DbgPrint("Exiting kernel with kernel APCs pending.\n");
102 KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT, (ULONG)Apc,
103 Thread->Tcb.KernelApcDisable, oldlvl, 0);
104 }
105 Apc->Inserted = FALSE;
106 Thread->Tcb.ApcState.KernelApcInProgress++;
107 Thread->Tcb.ApcState.KernelApcPending--;
108
109 KeLowerIrql(oldlvl);
110
111 ASSERT(Apc->KernelRoutine);
112
113 NormalRoutine = Apc->NormalRoutine;
114 NormalContext = Apc->NormalContext;
115 SystemArgument1 = Apc->SystemArgument1;
116 SystemArgument2 = Apc->SystemArgument2;
117 Apc->KernelRoutine(Apc,
118 &NormalRoutine,
119 &NormalContext,
120 &SystemArgument1,
121 &SystemArgument2);
122 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
123
124 oldlvl = KeRaiseIrqlToDpcLevel();
125 Thread->Tcb.ApcState.KernelApcInProgress--;
126 }
127 KeLowerIrql(oldlvl);
128 }
129
130 BOOLEAN
131 KiDeliverUserApc(PKTRAP_FRAME TrapFrame)
132 /*
133 * FUNCTION: Tests whether there are any pending APCs for the current thread
134 * and if so the APCs will be delivered on exit from kernel mode.
135 * ARGUMENTS:
136 * Thread = Thread to test for alerts
137 * UserContext = The user context saved on entry to kernel mode
138 */
139 {
140 PLIST_ENTRY current_entry;
141 PKAPC Apc;
142 PULONG Esp;
143 PCONTEXT Context;
144 KIRQL oldlvl;
145 PKTHREAD Thread;
146
147 DPRINT("KiDeliverUserApc(TrapFrame %x/%x)\n", TrapFrame,
148 KeGetCurrentThread()->TrapFrame);
149 Thread = KeGetCurrentThread();
150
151 /*
152 * Check for thread termination
153 */
154
155 oldlvl = KeRaiseIrqlToDpcLevel();
156
157 current_entry = Thread->ApcState.ApcListHead[1].Flink;
158
159 /*
160 * Shouldn't happen but check anyway.
161 */
162 if (current_entry == &Thread->ApcState.ApcListHead[1])
163 {
164 KeLowerIrql(oldlvl);
165 DbgPrint("KiDeliverUserApc called but no APC was pending\n");
166 return(FALSE);
167 }
168
169 while (!IsListEmpty(&Thread->ApcState.ApcListHead[1]))
170 {
171 current_entry = RemoveHeadList(&Thread->ApcState.ApcListHead[1]);
172 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
173 Apc->Inserted = FALSE;
174
175 /*
176 * We've dealt with one pending user-mode APC
177 */
178 Thread->ApcState.UserApcPending--;
179 KeLowerIrql(oldlvl);
180
181 /*
182 * Save the thread's current context (in other words the registers
183 * that will be restored when it returns to user mode) so the
184 * APC dispatcher can restore them later
185 */
186 Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
187 memset(Context, 0, sizeof(CONTEXT));
188 Context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER |
189 CONTEXT_SEGMENTS | CONTEXT_i386;
190 Context->SegGs = TrapFrame->Gs;
191 Context->SegFs = TrapFrame->Fs;
192 Context->SegEs = TrapFrame->Es;
193 Context->SegDs = TrapFrame->Ds;
194 Context->Edi = TrapFrame->Edi;
195 Context->Esi = TrapFrame->Esi;
196 Context->Ebx = TrapFrame->Ebx;
197 Context->Edx = TrapFrame->Edx;
198 Context->Ecx = TrapFrame->Ecx;
199 Context->Eax = TrapFrame->Eax;
200 Context->Ebp = TrapFrame->Ebp;
201 Context->Eip = TrapFrame->Eip;
202 Context->SegCs = TrapFrame->Cs;
203 Context->EFlags = TrapFrame->Eflags;
204 Context->Esp = TrapFrame->Esp;
205 Context->SegSs = TrapFrame->Ss;
206
207 /*
208 * Setup the trap frame so the thread will start executing at the
209 * APC Dispatcher when it returns to user-mode
210 */
211 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) -
212 (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
213
214 Esp[0] = 0xdeadbeef;
215 Esp[1] = (ULONG)Apc->NormalRoutine;
216 Esp[2] = (ULONG)Apc->NormalContext;
217 Esp[3] = (ULONG)Apc->SystemArgument1;
218 Esp[4] = (ULONG)Apc->SystemArgument2;
219 Esp[5] = (ULONG)Context;
220 TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
221 TrapFrame->Esp = (ULONG)Esp;
222
223
224 /*
225 * Now call for the kernel routine for the APC, which will free
226 * the APC data structure, we can't do this ourselves because
227 * the APC may be embedded in some larger structure e.g. an IRP
228 * We also give the kernel routine a last chance to modify the
229 * arguments to the user APC routine.
230 */
231 ASSERT(Apc->KernelRoutine);
232 Apc->KernelRoutine(Apc,
233 (PKNORMAL_ROUTINE*)&Esp[1],
234 (PVOID*)&Esp[2],
235 (PVOID*)&Esp[3],
236 (PVOID*)&Esp[4]);
237
238 oldlvl = KeRaiseIrqlToDpcLevel();
239 }
240 Thread->Alerted[0] = 0;
241 KeLowerIrql(oldlvl);
242
243 return(TRUE);
244 }
245
246 /*
247 * @implemented
248 */
249 VOID STDCALL
250 KiDeliverApc(ULONG Unknown1,
251 ULONG Unknown2,
252 ULONG Unknown3)
253 /*
254 * FUNCTION: Deliver an APC to the current thread.
255 * NOTES: This is called from the IRQL switching code if the current thread
256 * is returning from an IRQL greater than or equal to APC_LEVEL to
257 * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
258 * pending APCs will be delivered after a thread gets a new quantum and
259 * after it wakes from a wait.
260 */
261 {
262 PETHREAD Thread = PsGetCurrentThread();
263 PLIST_ENTRY current_entry;
264 PKAPC Apc;
265 KIRQL oldlvl;
266
267 DPRINT("KiDeliverApc()\n");
268 oldlvl = KeRaiseIrqlToDpcLevel();
269 current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
270 while(current_entry != &Thread->Tcb.ApcState.ApcListHead[0])
271 {
272 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
273 if (Apc->NormalRoutine == NULL)
274 {
275 Apc->Inserted = FALSE;
276 RemoveEntryList(&Apc->ApcListEntry);
277 Thread->Tcb.ApcState.KernelApcInProgress++;
278 Thread->Tcb.ApcState.KernelApcPending--;
279
280 KeLowerIrql(oldlvl);
281
282 ASSERT(Apc->KernelRoutine);
283 Apc->KernelRoutine(Apc,
284 &Apc->NormalRoutine,
285 &Apc->NormalContext,
286 &Apc->SystemArgument1,
287 &Apc->SystemArgument2);
288
289 oldlvl = KeRaiseIrqlToDpcLevel();
290 Thread->Tcb.ApcState.KernelApcInProgress--;
291 current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
292 }
293 else
294 {
295 current_entry = current_entry->Flink;
296 }
297 }
298 KeLowerIrql(oldlvl);
299 }
300
301 /*
302 * @implemented
303 */
304 BOOLEAN STDCALL
305 KeInsertQueueApc (PKAPC Apc,
306 PVOID SystemArgument1,
307 PVOID SystemArgument2,
308 KPRIORITY PriorityBoost)
309 /*
310 * FUNCTION: Queues an APC for execution
311 * ARGUMENTS:
312 * Apc = APC to be queued
313 * SystemArgument[1-2] = TBD
314 * Mode = TBD
315 */
316 {
317 //FIXME: return FALSE if APC can't be queued to target thread (thread has ended)
318 KIRQL oldlvl;
319 PKTHREAD TargetThread;
320
321 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
322 "SystemArgument2 %x)\n",Apc,SystemArgument1,
323 SystemArgument2);
324
325 Apc->SystemArgument1 = SystemArgument1;
326 Apc->SystemArgument2 = SystemArgument2;
327
328 if (Apc->Inserted)
329 {
330 #if 0
331 /* TMN: This code is in error */
332 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
333 KEBUGCHECK(0);
334 #else
335 return FALSE;
336 #endif
337 }
338
339 oldlvl = KeRaiseIrqlToDpcLevel();
340
341 TargetThread = Apc->Thread;
342
343 if (TargetThread->State == THREAD_STATE_TERMINATED_1 ||
344 TargetThread->State == THREAD_STATE_TERMINATED_2)
345 {
346 KeLowerIrql(oldlvl);
347 return(FALSE);
348 }
349
350 if (Apc->ApcMode == KernelMode)
351 {
352 InsertTailList(&TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->ApcListHead[0],
353 &Apc->ApcListEntry);
354 TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->KernelApcPending++;
355 }
356 else
357 {
358 InsertTailList(&TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->ApcListHead[1],
359 &Apc->ApcListEntry);
360 TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->UserApcPending++;
361 }
362 Apc->Inserted = TRUE;
363
364 /*
365 * If this is a kernel-mode APC for the current thread and we are not
366 * inside a critical section or at APC level then call it, in fact we
367 * rely on the side effects of dropping the IRQL level when we release
368 * the spinlock
369 */
370 if (Apc->ApcMode == KernelMode && TargetThread == KeGetCurrentThread() &&
371 Apc->NormalRoutine == NULL)
372 {
373 KeLowerIrql(oldlvl);
374 return TRUE;
375 }
376
377 /*
378 * If this is a kernel-mode APC and it is waiting at PASSIVE_LEVEL and
379 * not inside a critical section then wake it up. Otherwise it will
380 * execute the APC as soon as it returns to PASSIVE_LEVEL.
381 * FIXME: If the thread is running on another processor then send an
382 * IPI.
383 * FIXME: Check if the thread is terminating.
384 */
385 if (Apc->ApcMode == KernelMode && TargetThread->WaitIrql < APC_LEVEL &&
386 Apc->NormalRoutine == NULL)
387 {
388 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
389 STATUS_KERNEL_APC, TRUE);
390 }
391
392 /*
393 * If this is a 'funny' user-mode APC then mark the thread as
394 * alerted so it will execute the APC on exit from kernel mode. If it
395 * is waiting alertably then wake it up so it can return to user mode.
396 */
397 if (Apc->ApcMode == KernelMode && Apc->NormalRoutine != NULL)
398 {
399 TargetThread->Alerted[1] = 1;
400 if (TargetThread->Alertable == TRUE &&
401 TargetThread->WaitMode == UserMode)
402 {
403 PETHREAD Thread;
404
405 Thread = CONTAINING_RECORD(TargetThread, ETHREAD, Tcb);
406 KeRemoveAllWaitsThread(Thread, STATUS_USER_APC, TRUE);
407 }
408 }
409
410 /*
411 * If the thread is waiting alertably then wake it up and it will
412 * return to to user-mode executing the APC in the process. Otherwise the
413 * thread will execute the APC next time it enters an alertable wait.
414 */
415 if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
416 TargetThread->WaitMode == UserMode)
417 {
418 NTSTATUS Status;
419
420 DPRINT("Resuming thread for user APC\n");
421
422 Status = STATUS_USER_APC;
423 TargetThread->Alerted[0] = 1;
424 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
425 STATUS_USER_APC, TRUE);
426 }
427 KeLowerIrql(oldlvl);
428 return TRUE;
429 }
430
431 BOOLEAN STDCALL
432 KeRemoveQueueApc (PKAPC Apc)
433 /*
434 * FUNCTION: Removes APC object from the apc queue
435 * ARGUMENTS:
436 * Apc = APC to remove
437 * RETURNS: TRUE if the APC was in the queue
438 * FALSE otherwise
439 * NOTE: This function is not exported.
440 */
441 {
442 KIRQL oldIrql;
443 PKTHREAD TargetThread;
444
445 oldIrql = KeRaiseIrqlToDpcLevel();
446 if (Apc->Inserted == FALSE)
447 {
448 KeLowerIrql(oldIrql);
449 return FALSE;
450 }
451
452 TargetThread = Apc->Thread;
453 RemoveEntryList(&Apc->ApcListEntry);
454 if (Apc->ApcMode == KernelMode)
455 {
456 TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->KernelApcPending--;
457 }
458 else
459 {
460 TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->UserApcPending--;
461 }
462 Apc->Inserted = FALSE;
463
464 KeLowerIrql(oldIrql);
465 return(TRUE);
466 }
467
468
469 /*
470 * @implemented
471 */
472 VOID STDCALL
473 KeInitializeApc(
474 IN PKAPC Apc,
475 IN PKTHREAD Thread,
476 IN KAPC_ENVIRONMENT TargetEnvironment,
477 IN PKKERNEL_ROUTINE KernelRoutine,
478 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
479 IN PKNORMAL_ROUTINE NormalRoutine,
480 IN KPROCESSOR_MODE Mode,
481 IN PVOID Context)
482 /*
483 * FUNCTION: Initialize an APC object
484 * ARGUMENTS:
485 * Apc = Pointer to the APC object to initialized
486 * Thread = Thread the APC is to be delivered to
487 * StateIndex = KAPC_ENVIRONMENT
488 * KernelRoutine = Routine to be called for a kernel-mode APC
489 * RundownRoutine = Routine to be called if the thread has exited with
490 * the APC being executed
491 * NormalRoutine = Routine to be called for a user-mode APC
492 * Mode = APC mode
493 * Context = Parameter to be passed to the APC routine
494 */
495 {
496 DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
497 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
498 "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
499 NormalRoutine,Mode,Context);
500
501 memset(Apc, 0, sizeof(KAPC));
502 Apc->Thread = Thread;
503 Apc->ApcListEntry.Flink = NULL;
504 Apc->ApcListEntry.Blink = NULL;
505 Apc->KernelRoutine = KernelRoutine;
506 Apc->RundownRoutine = RundownRoutine;
507 Apc->NormalRoutine = NormalRoutine;
508 Apc->NormalContext = Context;
509 Apc->Inserted = FALSE;
510
511 if (TargetEnvironment == CurrentApcEnvironment)
512 {
513 Apc->ApcStateIndex = Thread->ApcStateIndex;
514 }
515 else
516 {
517 Apc->ApcStateIndex = TargetEnvironment;
518 }
519
520 if (Apc->NormalRoutine != NULL)
521 {
522 Apc->ApcMode = Mode;
523 }
524 else
525 {
526 Apc->ApcMode = KernelMode;
527 }
528 }
529
530 VOID STDCALL
531 NtQueueApcRundownRoutine(PKAPC Apc)
532 {
533 ExFreePool(Apc);
534 }
535
536 VOID STDCALL
537 NtQueueApcKernelRoutine(PKAPC Apc,
538 PKNORMAL_ROUTINE* NormalRoutine,
539 PVOID* NormalContext,
540 PVOID* SystemArgument1,
541 PVOID* SystemArgument2)
542 {
543 ExFreePool(Apc);
544 }
545
546 NTSTATUS STDCALL
547 NtQueueApcThread(HANDLE ThreadHandle,
548 PKNORMAL_ROUTINE ApcRoutine,
549 PVOID NormalContext,
550 PVOID SystemArgument1,
551 PVOID SystemArgument2)
552 {
553 PKAPC Apc;
554 PETHREAD Thread;
555 NTSTATUS Status;
556 PVOID ThreadVar;
557
558 ThreadVar = &Thread;
559
560 Status = ObReferenceObjectByHandle(ThreadHandle,
561 THREAD_ALL_ACCESS, /* FIXME */
562 PsThreadType,
563 UserMode,
564 &ThreadVar,
565 NULL);
566 if (!NT_SUCCESS(Status))
567 {
568 return(Status);
569 }
570
571 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC);
572 if (Apc == NULL)
573 {
574 ObDereferenceObject(Thread);
575 return(STATUS_NO_MEMORY);
576 }
577
578 KeInitializeApc(Apc,
579 &Thread->Tcb,
580 OriginalApcEnvironment,
581 NtQueueApcKernelRoutine,
582 NtQueueApcRundownRoutine,
583 ApcRoutine,
584 UserMode,
585 NormalContext);
586 KeInsertQueueApc(Apc,
587 SystemArgument1,
588 SystemArgument2,
589 IO_NO_INCREMENT);
590
591 ObDereferenceObject(Thread);
592 return(STATUS_SUCCESS);
593 }
594
595
596 NTSTATUS STDCALL NtTestAlert(VOID)
597 {
598 KiTestAlert();
599 return(STATUS_SUCCESS);
600 }
601
602 VOID INIT_FUNCTION
603 PiInitApcManagement(VOID)
604 {
605 }
606
607 static VOID FASTCALL
608 RepairList(PLIST_ENTRY Original, PLIST_ENTRY Copy, int Mode)
609 {
610 if (IsListEmpty(&Original[Mode]))
611 {
612 InitializeListHead(&Copy[Mode]);
613 }
614 else
615 {
616 Copy[Mode].Flink->Blink = &Copy[Mode];
617 Copy[Mode].Blink->Flink = &Copy[Mode];
618 }
619 }
620
621
622 VOID FASTCALL
623 KiSwapApcEnvironment(
624 PKTHREAD Thread,
625 PKPROCESS NewProcess)
626 {
627 if (Thread->ApcStateIndex == AttachedApcEnvironment)
628 {
629 /* NewProcess must be the same as in the original-environment */
630 ASSERT(NewProcess == Thread->ApcStatePointer[OriginalApcEnvironment]->Process);
631
632 /*
633 FIXME: Deliver any pending apc's queued to the attached environment before
634 switching back to the original environment.
635 This is not crucial thou, since i don't think we'll ever target apc's
636 to attached environments (or?)...
637 Remove the following asserts if implementing this.
638 -Gunnar
639 */
640
641 /* we don't support targeting apc's at attached-environments (yet)... */
642 ASSERT(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]));
643 ASSERT(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]));
644 ASSERT(Thread->ApcState.KernelApcInProgress == FALSE);
645 ASSERT(Thread->ApcState.KernelApcPending == FALSE);
646 ASSERT(Thread->ApcState.UserApcPending == FALSE);
647
648 /* restore backup of original environment */
649 Thread->ApcState = Thread->SavedApcState;
650 /* repair lists */
651 RepairList(Thread->SavedApcState.ApcListHead, Thread->ApcState.ApcListHead,
652 KernelMode);
653 RepairList(Thread->SavedApcState.ApcListHead, Thread->ApcState.ApcListHead,
654 UserMode);
655
656 /* update environment pointers */
657 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
658 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
659
660 /* update current-environment index */
661 Thread->ApcStateIndex = OriginalApcEnvironment;
662 }
663 else if (Thread->ApcStateIndex == OriginalApcEnvironment)
664 {
665 /* backup original environment */
666 Thread->SavedApcState = Thread->ApcState;
667 /* repair lists */
668 RepairList(Thread->ApcState.ApcListHead, Thread->SavedApcState.ApcListHead,
669 KernelMode);
670 RepairList(Thread->ApcState.ApcListHead, Thread->SavedApcState.ApcListHead,
671 UserMode);
672
673 /*
674 FIXME: Is it possible to target an apc to an attached environment even if the
675 thread is not currently attached???? If so, then this is bougus since it
676 reinitializes the attached apc environment then located in SavedApcState.
677 -Gunnar
678 */
679
680 /* setup a fresh new attached environment */
681 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
682 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
683 Thread->ApcState.Process = NewProcess;
684 Thread->ApcState.KernelApcInProgress = FALSE;
685 Thread->ApcState.KernelApcPending = FALSE;
686 Thread->ApcState.UserApcPending = FALSE;
687
688 /* update environment pointers */
689 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
690 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
691
692 /* update current-environment index */
693 Thread->ApcStateIndex = AttachedApcEnvironment;
694 }
695 else
696 {
697 /* FIXME: Is this the correct bug code? */
698 KEBUGCHECK(APC_INDEX_MISMATCH);
699 }
700 }
701