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