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