Reverting to 13775. Sorry for the mess. This is dedicated to Jane! 19934415.
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/apc.c
6 * PURPOSE: NT Implementation of APCs
7 *
8 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
9 * Phillip Susi
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS *******************************************************************/
19
20 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
21
22 #define TAG_KAPC TAG('K', 'A', 'P', 'C')
23
24 /* FUNCTIONS *****************************************************************/
25
26 /*
27 * @implemented
28 */
29 VOID
30 STDCALL
31 KeInitializeApc(
32 IN PKAPC Apc,
33 IN PKTHREAD Thread,
34 IN KAPC_ENVIRONMENT TargetEnvironment,
35 IN PKKERNEL_ROUTINE KernelRoutine,
36 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
37 IN PKNORMAL_ROUTINE NormalRoutine,
38 IN KPROCESSOR_MODE Mode,
39 IN PVOID Context)
40 /*
41 * FUNCTION: Initialize an APC object
42 * ARGUMENTS:
43 * Apc = Pointer to the APC object to initialized
44 * Thread = Thread the APC is to be delivered to
45 * TargetEnvironment = APC environment to use
46 * KernelRoutine = Routine to be called for a kernel-mode APC
47 * RundownRoutine = Routine to be called if the thread has exited with
48 * the APC being executed
49 * NormalRoutine = Routine to be called for a user-mode APC
50 * Mode = APC mode
51 * Context = Parameter to be passed to the APC routine
52 */
53 {
54 DPRINT ("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
55 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
56 "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
57 NormalRoutine,Mode,Context);
58
59 /* Set up the basic APC Structure Data */
60 RtlZeroMemory(Apc, sizeof(KAPC));
61 Apc->Type = KApc;
62 Apc->Size = sizeof(KAPC);
63
64 /* Set the Environment */
65 if (TargetEnvironment == CurrentApcEnvironment) {
66 Apc->ApcStateIndex = Thread->ApcStateIndex;
67 } else {
68 Apc->ApcStateIndex = TargetEnvironment;
69 }
70
71 /* Set the Thread and Routines */
72 Apc->Thread = Thread;
73 Apc->KernelRoutine = KernelRoutine;
74 Apc->RundownRoutine = RundownRoutine;
75 Apc->NormalRoutine = NormalRoutine;
76
77 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
78 if (ARGUMENT_PRESENT(NormalRoutine)) {
79 Apc->ApcMode = Mode;
80 Apc->NormalContext = Context;
81 } else {
82 Apc->ApcMode = KernelMode;
83 }
84 }
85
86 /*
87 * @implemented
88 */
89 BOOLEAN
90 STDCALL
91 KeInsertQueueApc (PKAPC Apc,
92 PVOID SystemArgument1,
93 PVOID SystemArgument2,
94 KPRIORITY PriorityBoost)
95 /*
96 * FUNCTION: Queues an APC for execution
97 * ARGUMENTS:
98 * Apc = APC to be queued
99 * SystemArgument[1-2] = Arguments we ignore and simply pass on.
100 * PriorityBoost = Priority Boost to give to the Thread
101 */
102 {
103 KIRQL OldIrql;
104 PKTHREAD Thread;
105 PLIST_ENTRY ApcListEntry;
106 PKAPC QueuedApc;
107
108 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
109 DPRINT ("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
110 "SystemArgument2 %x)\n",Apc,SystemArgument1,
111 SystemArgument2);
112
113 OldIrql = KeAcquireDispatcherDatabaseLock();
114
115 /* Get the Thread specified in the APC */
116 Thread = Apc->Thread;
117
118 /* Make sure the thread allows APC Queues.
119 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
120 */
121 if (Thread->ApcQueueable == FALSE) {
122 DPRINT("Thread doesn't allow APC Queues\n");
123 KeReleaseDispatcherDatabaseLock(OldIrql);
124 return FALSE;
125 }
126
127 /* Set the System Arguments */
128 Apc->SystemArgument1 = SystemArgument1;
129 Apc->SystemArgument2 = SystemArgument2;
130
131 /* Don't do anything if the APC is already inserted */
132 if (Apc->Inserted) {
133 KeReleaseDispatcherDatabaseLock(OldIrql);
134 return FALSE;
135 }
136
137 /* Three scenarios:
138 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
139 2) User APC which is PsExitSpecialApc = Put it at the front of the List
140 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
141 */
142 if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
143 DPRINT ("Inserting the Process Exit APC into the Queue\n");
144 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE;
145 InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
146 &Apc->ApcListEntry);
147 } else if (Apc->NormalRoutine == NULL) {
148 DPRINT ("Inserting Special APC %x into the Queue\n", Apc);
149 for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink;
150 ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode];
151 ApcListEntry = ApcListEntry->Flink) {
152
153 QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
154 if (Apc->NormalRoutine != NULL) break;
155 }
156
157 /* We found the first "Normal" APC, so write right before it */
158 ApcListEntry = ApcListEntry->Blink;
159 InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
160 } else {
161 DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode);
162 InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
163 &Apc->ApcListEntry);
164 }
165
166 /* Confirm Insertion */
167 Apc->Inserted = TRUE;
168
169 /* Three possibilites here again:
170 1) Kernel APC, The thread is Running: Request an Interrupt
171 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
172 3) User APC, Unwait the Thread if it is alertable
173 */
174 if (Apc->ApcMode == KernelMode) {
175 Thread->ApcState.KernelApcPending = TRUE;
176 if (Thread->State == THREAD_STATE_RUNNING) {
177 /* FIXME: Use IPI */
178 DPRINT ("Requesting APC Interrupt for Running Thread \n");
179 HalRequestSoftwareInterrupt(APC_LEVEL);
180 } else if ((Thread->State == THREAD_STATE_BLOCKED) &&
181 (Thread->WaitIrql < APC_LEVEL) &&
182 (Apc->NormalRoutine == NULL))
183 {
184 DPRINT ("Waking up Thread for Kernel-Mode APC Delivery \n");
185 KiAbortWaitThread(Thread, STATUS_KERNEL_APC);
186 }
187 } else if ((Thread->State == THREAD_STATE_BLOCKED) &&
188 (Thread->WaitMode == UserMode) &&
189 (Thread->Alertable))
190 {
191 DPRINT ("Waking up Thread for User-Mode APC Delivery \n");
192 Thread->ApcState.UserApcPending = TRUE;
193 KiAbortWaitThread(Thread, STATUS_USER_APC);
194 }
195
196 /* Return Sucess if we are here */
197 KeReleaseDispatcherDatabaseLock(OldIrql);
198 return TRUE;
199 }
200
201 BOOLEAN STDCALL
202 KeRemoveQueueApc (PKAPC Apc)
203 /*
204 * FUNCTION: Removes APC object from the apc queue
205 * ARGUMENTS:
206 * Apc = APC to remove
207 * RETURNS: TRUE if the APC was in the queue
208 * FALSE otherwise
209 * NOTE: This function is not exported.
210 */
211 {
212 KIRQL OldIrql;
213 PKTHREAD Thread = Apc->Thread;
214
215 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
216 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
217
218 OldIrql = KeAcquireDispatcherDatabaseLock();
219 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
220
221 /* Remove it from the Queue if it's inserted */
222 if (!Apc->Inserted == FALSE) {
223 RemoveEntryList(&Apc->ApcListEntry);
224 Apc->Inserted = FALSE;
225
226 /* If the Queue is completely empty, then no more APCs are pending */
227 if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) {
228 if (Apc->ApcMode == KernelMode) {
229 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
230 } else {
231 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
232 }
233 }
234 } else {
235 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
236 KeReleaseDispatcherDatabaseLock(OldIrql);
237 return(FALSE);
238 }
239
240 /* Restore IRQL and Return */
241 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
242 KeReleaseDispatcherDatabaseLock(OldIrql);
243 return(TRUE);
244 }
245
246
247 /*
248 * @implemented
249 */
250 VOID
251 STDCALL
252 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
253 PVOID Reserved,
254 PKTRAP_FRAME TrapFrame)
255 /*
256 * FUNCTION: Deliver an APC to the current thread.
257 * NOTES: This is called from the IRQL switching code if the current thread
258 * is returning from an IRQL greater than or equal to APC_LEVEL to
259 * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
260 * pending APCs will be delivered after a thread gets a new quantum and
261 * after it wakes from a wait. Note that the TrapFrame is only valid if
262 * the previous mode is User.
263 */
264 {
265 PKTHREAD Thread = KeGetCurrentThread();
266 PLIST_ENTRY ApcListEntry;
267 PKAPC Apc;
268 KIRQL OldIrql;
269 PKKERNEL_ROUTINE KernelRoutine;
270 PVOID NormalContext;
271 PKNORMAL_ROUTINE NormalRoutine;
272 PVOID SystemArgument1;
273 PVOID SystemArgument2;
274
275 ASSERT_IRQL_EQUAL(APC_LEVEL);
276
277 /* Lock the APC Queue and Raise IRQL to Synch */
278 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
279
280 /* Clear APC Pending */
281 Thread->ApcState.KernelApcPending = FALSE;
282
283 /* Do the Kernel APCs first */
284 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
285
286 /* Get the next Entry */
287 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
288 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
289
290 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
291 NormalRoutine = Apc->NormalRoutine;
292 KernelRoutine = Apc->KernelRoutine;
293 NormalContext = Apc->NormalContext;
294 SystemArgument1 = Apc->SystemArgument1;
295 SystemArgument2 = Apc->SystemArgument2;
296
297 /* Special APC */
298 if (NormalRoutine == NULL) {
299 /* Remove the APC from the list */
300 Apc->Inserted = FALSE;
301 RemoveEntryList(ApcListEntry);
302
303 /* Go back to APC_LEVEL */
304 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
305
306 /* Call the Special APC */
307 DPRINT("Delivering a Special APC: %x\n", Apc);
308 KernelRoutine(Apc,
309 &NormalRoutine,
310 &NormalContext,
311 &SystemArgument1,
312 &SystemArgument2);
313
314 /* Raise IRQL and Lock again */
315 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
316 } else {
317 /* Normal Kernel APC */
318 if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
319
320 /*
321 * DeliveryMode must be KernelMode in this case, since one may not
322 * return to umode while being inside a critical section or while
323 * a regular kmode apc is running (the latter should be impossible btw).
324 * -Gunnar
325 */
326 ASSERT(DeliveryMode == KernelMode);
327
328 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
329 return;
330 }
331
332 /* Dequeue the APC */
333 RemoveEntryList(ApcListEntry);
334 Apc->Inserted = FALSE;
335
336 /* Go back to APC_LEVEL */
337 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
338
339 /* Call the Kernel APC */
340 DPRINT("Delivering a Normal APC: %x\n", Apc);
341 KernelRoutine(Apc,
342 &NormalRoutine,
343 &NormalContext,
344 &SystemArgument1,
345 &SystemArgument2);
346
347 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
348 if (NormalRoutine != NULL) {
349 /* At Passive Level, this APC can be prempted by a Special APC */
350 Thread->ApcState.KernelApcInProgress = TRUE;
351 KeLowerIrql(PASSIVE_LEVEL);
352
353 /* Call and Raise IRQ back to APC_LEVEL */
354 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc);
355 NormalRoutine(&NormalContext, &SystemArgument1, &SystemArgument2);
356 KeRaiseIrql(APC_LEVEL, &OldIrql);
357 }
358
359 /* Raise IRQL and Lock again */
360 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
361 Thread->ApcState.KernelApcInProgress = FALSE;
362 }
363 }
364
365 /* Now we do the User APCs */
366 if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
367 (DeliveryMode == UserMode) &&
368 (Thread->ApcState.UserApcPending == TRUE)) {
369
370 /* It's not pending anymore */
371 Thread->ApcState.UserApcPending = FALSE;
372
373 /* Get the APC Object */
374 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
375 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
376
377 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
378 NormalRoutine = Apc->NormalRoutine;
379 KernelRoutine = Apc->KernelRoutine;
380 NormalContext = Apc->NormalContext;
381 SystemArgument1 = Apc->SystemArgument1;
382 SystemArgument2 = Apc->SystemArgument2;
383
384 /* Remove the APC from Queue, restore IRQL and call the APC */
385 RemoveEntryList(ApcListEntry);
386 Apc->Inserted = FALSE;
387
388 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
389 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
390 KernelRoutine(Apc,
391 &NormalRoutine,
392 &NormalContext,
393 &SystemArgument1,
394 &SystemArgument2);
395
396 if (NormalRoutine == NULL) {
397 /* Check if more User APCs are Pending */
398 KeTestAlertThread(UserMode);
399 } else {
400 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
401 DPRINT("Delivering a User APC: %x\n", Apc);
402 KiInitializeUserApc(Reserved,
403 TrapFrame,
404 NormalRoutine,
405 NormalContext,
406 SystemArgument1,
407 SystemArgument2);
408 }
409 } else {
410 /* Go back to APC_LEVEL */
411 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
412 }
413 }
414
415 VOID
416 STDCALL
417 KiFreeApcRoutine(PKAPC Apc,
418 PKNORMAL_ROUTINE* NormalRoutine,
419 PVOID* NormalContext,
420 PVOID* SystemArgument1,
421 PVOID* SystemArgument2)
422 {
423 /* Free the APC and do nothing else */
424 ExFreePool(Apc);
425 }
426
427 VOID
428 KiInitializeUserApc(IN PVOID Reserved,
429 IN PKTRAP_FRAME TrapFrame,
430 IN PKNORMAL_ROUTINE NormalRoutine,
431 IN PVOID NormalContext,
432 IN PVOID SystemArgument1,
433 IN PVOID SystemArgument2)
434 /*
435 * FUNCTION: Prepares the Context for a user mode APC through ntdll.dll
436 */
437 {
438 PCONTEXT Context;
439 PULONG Esp;
440
441 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame);
442
443 /*
444 * Save the thread's current context (in other words the registers
445 * that will be restored when it returns to user mode) so the
446 * APC dispatcher can restore them later
447 */
448 Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
449 RtlZeroMemory(Context, sizeof(CONTEXT));
450 Context->ContextFlags = CONTEXT_FULL;
451 Context->SegGs = TrapFrame->Gs;
452 Context->SegFs = TrapFrame->Fs;
453 Context->SegEs = TrapFrame->Es;
454 Context->SegDs = TrapFrame->Ds;
455 Context->Edi = TrapFrame->Edi;
456 Context->Esi = TrapFrame->Esi;
457 Context->Ebx = TrapFrame->Ebx;
458 Context->Edx = TrapFrame->Edx;
459 Context->Ecx = TrapFrame->Ecx;
460 Context->Eax = TrapFrame->Eax;
461 Context->Ebp = TrapFrame->Ebp;
462 Context->Eip = TrapFrame->Eip;
463 Context->SegCs = TrapFrame->Cs;
464 Context->EFlags = TrapFrame->Eflags;
465 Context->Esp = TrapFrame->Esp;
466 Context->SegSs = TrapFrame->Ss;
467
468 /*
469 * Setup the trap frame so the thread will start executing at the
470 * APC Dispatcher when it returns to user-mode
471 */
472 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
473 Esp[0] = 0xdeadbeef;
474 Esp[1] = (ULONG)NormalRoutine;
475 Esp[2] = (ULONG)NormalContext;
476 Esp[3] = (ULONG)SystemArgument1;
477 Esp[4] = (ULONG)SystemArgument2;
478 Esp[5] = (ULONG)Context;
479 TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
480 TrapFrame->Esp = (ULONG)Esp;
481 }
482
483 /*
484 * @implemented
485 */
486 BOOLEAN
487 STDCALL
488 KeAreApcsDisabled(
489 VOID
490 )
491 {
492 return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
493 }
494
495 NTSTATUS
496 STDCALL
497 NtQueueApcThread(HANDLE ThreadHandle,
498 PKNORMAL_ROUTINE ApcRoutine,
499 PVOID NormalContext,
500 PVOID SystemArgument1,
501 PVOID SystemArgument2)
502 /*
503 * FUNCTION:
504 * This function is used to queue an APC from user-mode for the specified thread.
505 * The thread must enter an alertable wait before the APC will be delivered.
506 *
507 * ARGUMENTS:
508 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
509 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
510 * NormalContext - User-defined value to pass to the APC Routine
511 * SystemArgument1 - User-defined value to pass to the APC Routine
512 * SystemArgument2 - User-defined value to pass to the APC Routine
513 *
514 * RETURNS: NTSTATUS SUCCESS or Failure Code from included calls.
515 */
516 {
517
518 PKAPC Apc;
519 PETHREAD Thread;
520 KPROCESSOR_MODE PreviousMode;
521 NTSTATUS Status;
522
523 PreviousMode = ExGetPreviousMode();
524
525 /* Get ETHREAD from Handle */
526 Status = ObReferenceObjectByHandle(ThreadHandle,
527 THREAD_SET_CONTEXT,
528 PsThreadType,
529 PreviousMode,
530 (PVOID)&Thread,
531 NULL);
532
533 /* Fail if the Handle is invalid for some reason */
534 if (!NT_SUCCESS(Status)) {
535 return(Status);
536 }
537
538 /* If this is a Kernel or System Thread, then fail */
539 if (Thread->Tcb.Teb == NULL) {
540 ObDereferenceObject(Thread);
541 return STATUS_INVALID_HANDLE;
542 }
543
544 /* Allocate an APC */
545 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC);
546 if (Apc == NULL) {
547 ObDereferenceObject(Thread);
548 return(STATUS_NO_MEMORY);
549 }
550
551 /* Initialize and Queue a user mode apc (always!) */
552 KeInitializeApc(Apc,
553 &Thread->Tcb,
554 OriginalApcEnvironment,
555 KiFreeApcRoutine,
556 NULL,
557 ApcRoutine,
558 UserMode,
559 NormalContext);
560 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
561 Status = STATUS_UNSUCCESSFUL;
562 } else {
563 Status = STATUS_SUCCESS;
564 }
565
566 /* Dereference Thread and Return */
567 ObDereferenceObject(Thread);
568 return Status;
569 }
570
571
572 static inline VOID RepairList(PLIST_ENTRY Original,
573 PLIST_ENTRY Copy,
574 KPROCESSOR_MODE Mode)
575 {
576 /* Copy Source to Desination */
577 if (IsListEmpty(&Original[(int)Mode])) {
578 InitializeListHead(&Copy[(int)Mode]);
579 } else {
580 Copy[(int)Mode].Flink = Original[(int)Mode].Flink;
581 Copy[(int)Mode].Blink = Original[(int)Mode].Blink;
582 Original[(int)Mode].Flink->Blink = &Copy[(int)Mode];
583 Original[(int)Mode].Blink->Flink = &Copy[(int)Mode];
584 }
585 }
586
587 VOID
588 STDCALL
589 KiMoveApcState (PKAPC_STATE OldState,
590 PKAPC_STATE NewState)
591 {
592 /* Restore backup of Original Environment */
593 *NewState = *OldState;
594
595 /* Repair Lists */
596 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
597 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
598 }
599