Implemented KeAreApcsDisabled, KeFlushEntireTb, KeIsExecutingDpc, KeSetKernelStackSwa...
[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 KSPIN_LOCK PiApcLock;
39
40 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
41
42 #define TAG_KAPC TAG('K', 'A', 'P', 'C')
43
44 /* FUNCTIONS *****************************************************************/
45
46 /*
47 * @implemented
48 */
49 BOOLEAN
50 STDCALL
51 KeAreApcsDisabled(
52 VOID
53 )
54 {
55 return KeGetCurrentThread()->KernelApcDisable ? FALSE : TRUE;
56 }
57
58 VOID KiRundownThread(VOID)
59 /*
60 * FUNCTION:
61 */
62 {
63 }
64
65 BOOLEAN KiTestAlert(VOID)
66 /*
67 * FUNCTION: Tests whether there are any pending APCs for the current thread
68 * and if so the APCs will be delivered on exit from kernel mode
69 */
70 {
71 KIRQL oldIrql;
72
73 KeAcquireSpinLock(&PiApcLock, &oldIrql);
74 if (KeGetCurrentThread()->ApcState.UserApcPending == 0)
75 {
76 KeReleaseSpinLock(&PiApcLock, oldIrql);
77 return(FALSE);
78 }
79 KeGetCurrentThread()->Alerted[0] = 1;
80 KeReleaseSpinLock(&PiApcLock, oldIrql);
81 return(TRUE);
82 }
83
84 VOID
85 KiDeliverNormalApc(VOID)
86 {
87 PETHREAD Thread = PsGetCurrentThread();
88 PLIST_ENTRY current;
89 PKAPC Apc;
90 KIRQL oldlvl;
91 PKNORMAL_ROUTINE NormalRoutine;
92 PVOID NormalContext;
93 PVOID SystemArgument1;
94 PVOID SystemArgument2;
95
96 KeAcquireSpinLock(&PiApcLock, &oldlvl);
97 while(!IsListEmpty(&(Thread->Tcb.ApcState.ApcListHead[0])))
98 {
99 current = RemoveTailList(&Thread->Tcb.ApcState.ApcListHead[0]);
100 Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
101 if (Apc->NormalRoutine == NULL)
102 {
103 DbgPrint("Exiting kernel with kernel APCs pending.\n");
104 KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT, (ULONG)Apc,
105 Thread->Tcb.KernelApcDisable, oldlvl, 0);
106 }
107 Apc->Inserted = FALSE;
108 Thread->Tcb.ApcState.KernelApcInProgress++;
109 Thread->Tcb.ApcState.KernelApcPending--;
110
111 KeReleaseSpinLock(&PiApcLock, oldlvl);
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 KeAcquireSpinLock(&PiApcLock, &oldlvl);
125 Thread->Tcb.ApcState.KernelApcInProgress--;
126 }
127 KeReleaseSpinLock(&PiApcLock, 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 KeAcquireSpinLock(&PiApcLock, &oldlvl);
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 KeReleaseSpinLock(&PiApcLock, 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 KeReleaseSpinLock(&PiApcLock, 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 Apc->KernelRoutine(Apc,
232 (PKNORMAL_ROUTINE*)&Esp[1],
233 (PVOID*)&Esp[2],
234 (PVOID*)&Esp[3],
235 (PVOID*)&Esp[4]);
236
237 KeAcquireSpinLock(&PiApcLock, &oldlvl);
238 }
239 Thread->Alerted[0] = 0;
240 KeReleaseSpinLock(&PiApcLock, oldlvl);
241
242 return(TRUE);
243 }
244
245 /*
246 * @implemented
247 */
248 VOID STDCALL
249 KiDeliverApc(ULONG Unknown1,
250 ULONG Unknown2,
251 ULONG Unknown3)
252 /*
253 * FUNCTION: Deliver an APC to the current thread.
254 * NOTES: This is called from the IRQL switching code if the current thread
255 * is returning from an IRQL greater than or equal to APC_LEVEL to
256 * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
257 * pending APCs will be delivered after a thread gets a new quantum and
258 * after it wakes from a wait.
259 */
260 {
261 PETHREAD Thread = PsGetCurrentThread();
262 PLIST_ENTRY current_entry;
263 PKAPC Apc;
264 KIRQL oldlvl;
265
266 DPRINT("KiDeliverApc()\n");
267 KeAcquireSpinLock(&PiApcLock, &oldlvl);
268 current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
269 while(current_entry != &Thread->Tcb.ApcState.ApcListHead[0])
270 {
271 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
272 if (Apc->NormalRoutine == NULL)
273 {
274 Apc->Inserted = FALSE;
275 RemoveEntryList(&Apc->ApcListEntry);
276 Thread->Tcb.ApcState.KernelApcInProgress++;
277 Thread->Tcb.ApcState.KernelApcPending--;
278
279 KeReleaseSpinLock(&PiApcLock, oldlvl);
280
281 Apc->KernelRoutine(Apc,
282 &Apc->NormalRoutine,
283 &Apc->NormalContext,
284 &Apc->SystemArgument1,
285 &Apc->SystemArgument2);
286
287 KeAcquireSpinLock(&PiApcLock, &oldlvl);
288 Thread->Tcb.ApcState.KernelApcInProgress--;
289 current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
290 }
291 else
292 {
293 current_entry = current_entry->Flink;
294 }
295 }
296 KeReleaseSpinLock(&PiApcLock, oldlvl);
297 }
298
299 /*
300 * @implemented
301 */
302 BOOLEAN STDCALL
303 KeInsertQueueApc (PKAPC Apc,
304 PVOID SystemArgument1,
305 PVOID SystemArgument2,
306 KPRIORITY PriorityBoost)
307 /*
308 * FUNCTION: Queues an APC for execution
309 * ARGUMENTS:
310 * Apc = APC to be queued
311 * SystemArgument[1-2] = TBD
312 * Mode = TBD
313 */
314 {
315 //FIXME: return FALSE if APC can't be queued to target thread (thread has ended)
316 KIRQL oldlvl;
317 PKTHREAD TargetThread;
318
319 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
320 "SystemArgument2 %x)\n",Apc,SystemArgument1,
321 SystemArgument2);
322
323 Apc->SystemArgument1 = SystemArgument1;
324 Apc->SystemArgument2 = SystemArgument2;
325
326 if (Apc->Inserted)
327 {
328 #if 0
329 /* TMN: This code is in error */
330 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
331 KEBUGCHECK(0);
332 #else
333 return FALSE;
334 #endif
335 }
336
337 KeAcquireSpinLock(&PiApcLock, &oldlvl);
338
339 TargetThread = Apc->Thread;
340
341 if (TargetThread->State == THREAD_STATE_TERMINATED_1 ||
342 TargetThread->State == THREAD_STATE_TERMINATED_2)
343 {
344 KeReleaseSpinLock(&PiApcLock, oldlvl);
345 return(FALSE);
346 }
347
348 if (Apc->ApcMode == KernelMode)
349 {
350 InsertTailList(&TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->ApcListHead[0],
351 &Apc->ApcListEntry);
352 TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->KernelApcPending++;
353 }
354 else
355 {
356 InsertTailList(&TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->ApcListHead[1],
357 &Apc->ApcListEntry);
358 TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->UserApcPending++;
359 }
360 Apc->Inserted = TRUE;
361
362 /*
363 * If this is a kernel-mode APC for the current thread and we are not
364 * inside a critical section or at APC level then call it, in fact we
365 * rely on the side effects of dropping the IRQL level when we release
366 * the spinlock
367 */
368 if (Apc->ApcMode == KernelMode && TargetThread == KeGetCurrentThread() &&
369 Apc->NormalRoutine == NULL)
370 {
371 KeReleaseSpinLock(&PiApcLock, oldlvl);
372 return TRUE;
373 }
374
375 /*
376 * If this is a kernel-mode APC and it is waiting at PASSIVE_LEVEL and
377 * not inside a critical section then wake it up. Otherwise it will
378 * execute the APC as soon as it returns to PASSIVE_LEVEL.
379 * FIXME: If the thread is running on another processor then send an
380 * IPI.
381 * FIXME: Check if the thread is terminating.
382 */
383 if (Apc->ApcMode == KernelMode && TargetThread->WaitIrql < APC_LEVEL &&
384 Apc->NormalRoutine == NULL)
385 {
386 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
387 STATUS_KERNEL_APC, TRUE);
388 }
389
390 /*
391 * If this is a 'funny' user-mode APC then mark the thread as
392 * alerted so it will execute the APC on exit from kernel mode. If it
393 * is waiting alertably then wake it up so it can return to user mode.
394 */
395 if (Apc->ApcMode == KernelMode && Apc->NormalRoutine != NULL)
396 {
397 TargetThread->Alerted[1] = 1;
398 if (TargetThread->Alertable == TRUE &&
399 TargetThread->WaitMode == UserMode)
400 {
401 PETHREAD Thread;
402
403 Thread = CONTAINING_RECORD(TargetThread, ETHREAD, Tcb);
404 KeRemoveAllWaitsThread(Thread, STATUS_USER_APC, TRUE);
405 }
406 }
407
408 /*
409 * If the thread is waiting alertably then wake it up and it will
410 * return to to user-mode executing the APC in the process. Otherwise the
411 * thread will execute the APC next time it enters an alertable wait.
412 */
413 if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
414 TargetThread->WaitMode == UserMode)
415 {
416 NTSTATUS Status;
417
418 DPRINT("Resuming thread for user APC\n");
419
420 Status = STATUS_USER_APC;
421 TargetThread->Alerted[0] = 1;
422 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
423 STATUS_USER_APC, TRUE);
424 }
425 KeReleaseSpinLock(&PiApcLock, oldlvl);
426 return TRUE;
427 }
428
429 BOOLEAN STDCALL
430 KeRemoveQueueApc (PKAPC Apc)
431 /*
432 * FUNCTION: Removes APC object from the apc queue
433 * ARGUMENTS:
434 * Apc = APC to remove
435 * RETURNS: TRUE if the APC was in the queue
436 * FALSE otherwise
437 * NOTE: This function is not exported.
438 */
439 {
440 KIRQL oldIrql;
441 PKTHREAD TargetThread;
442
443 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
444 KiAcquireSpinLock(&PiApcLock);
445 if (Apc->Inserted == FALSE)
446 {
447 KiReleaseSpinLock(&PiApcLock);
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 KiReleaseSpinLock(&PiApcLock);
465 KeLowerIrql(oldIrql);
466 return(TRUE);
467 }
468
469
470 /*
471 * @implemented
472 */
473 VOID STDCALL
474 KeInitializeApc(
475 IN PKAPC Apc,
476 IN PKTHREAD Thread,
477 IN KAPC_ENVIRONMENT TargetEnvironment,
478 IN PKKERNEL_ROUTINE KernelRoutine,
479 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
480 IN PKNORMAL_ROUTINE NormalRoutine,
481 IN KPROCESSOR_MODE Mode,
482 IN PVOID Context)
483 /*
484 * FUNCTION: Initialize an APC object
485 * ARGUMENTS:
486 * Apc = Pointer to the APC object to initialized
487 * Thread = Thread the APC is to be delivered to
488 * StateIndex = KAPC_ENVIRONMENT
489 * KernelRoutine = Routine to be called for a kernel-mode APC
490 * RundownRoutine = Routine to be called if the thread has exited with
491 * the APC being executed
492 * NormalRoutine = Routine to be called for a user-mode APC
493 * Mode = APC mode
494 * Context = Parameter to be passed to the APC routine
495 */
496 {
497 DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
498 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
499 "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
500 NormalRoutine,Mode,Context);
501
502 memset(Apc, 0, sizeof(KAPC));
503 Apc->Thread = Thread;
504 Apc->ApcListEntry.Flink = NULL;
505 Apc->ApcListEntry.Blink = NULL;
506 Apc->KernelRoutine = KernelRoutine;
507 Apc->RundownRoutine = RundownRoutine;
508 Apc->NormalRoutine = NormalRoutine;
509 Apc->NormalContext = Context;
510 Apc->Inserted = FALSE;
511
512 if (TargetEnvironment == CurrentApcEnvironment)
513 {
514 Apc->ApcStateIndex = Thread->ApcStateIndex;
515 }
516 else
517 {
518 Apc->ApcStateIndex = TargetEnvironment;
519 }
520
521 if (Apc->NormalRoutine != NULL)
522 {
523 Apc->ApcMode = Mode;
524 }
525 else
526 {
527 Apc->ApcMode = KernelMode;
528 }
529 }
530
531 VOID STDCALL
532 NtQueueApcRundownRoutine(PKAPC Apc)
533 {
534 ExFreePool(Apc);
535 }
536
537 VOID STDCALL
538 NtQueueApcKernelRoutine(PKAPC Apc,
539 PKNORMAL_ROUTINE* NormalRoutine,
540 PVOID* NormalContext,
541 PVOID* SystemArgument1,
542 PVOID* SystemArgument2)
543 {
544 ExFreePool(Apc);
545 }
546
547 NTSTATUS STDCALL
548 NtQueueApcThread(HANDLE ThreadHandle,
549 PKNORMAL_ROUTINE ApcRoutine,
550 PVOID NormalContext,
551 PVOID SystemArgument1,
552 PVOID SystemArgument2)
553 {
554 PKAPC Apc;
555 PETHREAD Thread;
556 NTSTATUS Status;
557 PVOID ThreadVar;
558
559 ThreadVar = &Thread;
560
561 Status = ObReferenceObjectByHandle(ThreadHandle,
562 THREAD_ALL_ACCESS, /* FIXME */
563 PsThreadType,
564 UserMode,
565 &ThreadVar,
566 NULL);
567 if (!NT_SUCCESS(Status))
568 {
569 return(Status);
570 }
571
572 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC);
573 if (Apc == NULL)
574 {
575 ObDereferenceObject(Thread);
576 return(STATUS_NO_MEMORY);
577 }
578
579 KeInitializeApc(Apc,
580 &Thread->Tcb,
581 OriginalApcEnvironment,
582 NtQueueApcKernelRoutine,
583 NtQueueApcRundownRoutine,
584 ApcRoutine,
585 UserMode,
586 NormalContext);
587 KeInsertQueueApc(Apc,
588 SystemArgument1,
589 SystemArgument2,
590 IO_NO_INCREMENT);
591
592 ObDereferenceObject(Thread);
593 return(STATUS_SUCCESS);
594 }
595
596
597 NTSTATUS STDCALL NtTestAlert(VOID)
598 {
599 KiTestAlert();
600 return(STATUS_SUCCESS);
601 }
602
603 VOID INIT_FUNCTION
604 PiInitApcManagement(VOID)
605 {
606 KeInitializeSpinLock(&PiApcLock);
607 }
608
609 static VOID FASTCALL
610 RepairList(PLIST_ENTRY Original, PLIST_ENTRY Copy, int Mode)
611 {
612 if (IsListEmpty(&Original[Mode]))
613 {
614 InitializeListHead(&Copy[Mode]);
615 }
616 else
617 {
618 Copy[Mode].Flink->Blink = &Copy[Mode];
619 Copy[Mode].Blink->Flink = &Copy[Mode];
620 }
621 }
622
623
624 VOID FASTCALL
625 KiSwapApcEnvironment(
626 PKTHREAD Thread,
627 PKPROCESS NewProcess)
628 {
629 /* FIXME: Grab the process apc lock or the PiApcLock? And why does both exist? */
630
631 if (Thread->ApcStateIndex == AttachedApcEnvironment)
632 {
633 /* NewProcess must be the same as in the original-environment */
634 assert(NewProcess == Thread->ApcStatePointer[OriginalApcEnvironment]->Process);
635
636 /*
637 FIXME: Deliver any pending apc's queued to the attached environment before
638 switching back to the original environment.
639 This is not crucial thou, since i don't think we'll ever target apc's
640 to attached environments (or?)...
641 Remove the following asserts if implementing this.
642 -Gunnar
643 */
644
645 /* we don't support targeting apc's at attached-environments (yet)... */
646 assert(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]));
647 assert(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]));
648 assert(Thread->ApcState.KernelApcInProgress == FALSE);
649 assert(Thread->ApcState.KernelApcPending == FALSE);
650 assert(Thread->ApcState.UserApcPending == FALSE);
651
652 /* restore backup of original environment */
653 Thread->ApcState = Thread->SavedApcState;
654 /* repair lists */
655 RepairList(Thread->SavedApcState.ApcListHead, Thread->ApcState.ApcListHead,
656 KernelMode);
657 RepairList(Thread->SavedApcState.ApcListHead, Thread->ApcState.ApcListHead,
658 UserMode);
659
660 /* update environment pointers */
661 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
662 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
663
664 /* update current-environment index */
665 Thread->ApcStateIndex = OriginalApcEnvironment;
666 }
667 else if (Thread->ApcStateIndex == OriginalApcEnvironment)
668 {
669 /* backup original environment */
670 Thread->SavedApcState = Thread->ApcState;
671 /* repair lists */
672 RepairList(Thread->ApcState.ApcListHead, Thread->SavedApcState.ApcListHead,
673 KernelMode);
674 RepairList(Thread->ApcState.ApcListHead, Thread->SavedApcState.ApcListHead,
675 UserMode);
676
677 /*
678 FIXME: Is it possible to target an apc to an attached environment even if the
679 thread is not currently attached???? If so, then this is bougus since it
680 reinitializes the attached apc environment then located in SavedApcState.
681 -Gunnar
682 */
683
684 /* setup a fresh new attached environment */
685 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
686 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
687 Thread->ApcState.Process = NewProcess;
688 Thread->ApcState.KernelApcInProgress = FALSE;
689 Thread->ApcState.KernelApcPending = FALSE;
690 Thread->ApcState.UserApcPending = FALSE;
691
692 /* update environment pointers */
693 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
694 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
695
696 /* update current-environment index */
697 Thread->ApcStateIndex = AttachedApcEnvironment;
698 }
699 else
700 {
701 /* FIXME: Is this the correct bug code? */
702 KEBUGCHECK(APC_INDEX_MISMATCH);
703 }
704 }
705