2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/apc.c
5 * PURPOSE: Possible implementation of APCs
6 * PROGRAMMER: David Welch (welch@cwcom.net)
9 * 12/11/99: Phillip Susi: Reworked the APC code
12 /* INCLUDES *****************************************************************/
14 #include <ddk/ntddk.h>
15 #include <internal/i386/segment.h>
16 #include <internal/ps.h>
17 #include <internal/ke.h>
18 #include <internal/ldr.h>
21 #include <internal/debug.h>
23 /* NOTES *********************************************************************/
27 /* GLOBALS *******************************************************************/
30 extern KSPIN_LOCK PiThreadListLock
;
32 VOID
PsTerminateCurrentThread(NTSTATUS ExitStatus
);
34 /* FUNCTIONS *****************************************************************/
36 VOID
KiRundownThread(VOID
)
43 BOOLEAN
KiTestAlert(VOID
)
45 * FUNCTION: Tests whether there are any pending APCs for the current thread
46 * and if so the APCs will be delivered on exit from kernel mode
51 KeAcquireSpinLock(&PiApcLock
, &oldIrql
);
52 if (KeGetCurrentThread()->ApcState
.UserApcPending
== 0)
54 KeReleaseSpinLock(&PiApcLock
, oldIrql
);
57 KeGetCurrentThread()->Alerted
[0] = 1;
58 KeReleaseSpinLock(&PiApcLock
, oldIrql
);
63 KiDeliverNormalApc(VOID
)
65 PETHREAD Thread
= PsGetCurrentThread();
69 PKNORMAL_ROUTINE NormalRoutine
;
71 PVOID SystemArgument1
;
72 PVOID SystemArgument2
;
74 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
75 while(!IsListEmpty(&(Thread
->Tcb
.ApcState
.ApcListHead
[0])))
77 current
= Thread
->Tcb
.ApcState
.ApcListHead
[0].Blink
;
78 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
79 if (Apc
->NormalRoutine
!= NULL
)
81 (void)RemoveTailList(&Thread
->Tcb
.ApcState
.ApcListHead
[0]);
82 Thread
->Tcb
.ApcState
.KernelApcInProgress
++;
83 Thread
->Tcb
.ApcState
.KernelApcPending
--;
85 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
87 NormalRoutine
= Apc
->NormalRoutine
;
88 NormalContext
= Apc
->NormalContext
;
89 SystemArgument1
= Apc
->SystemArgument1
;
90 SystemArgument2
= Apc
->SystemArgument2
;
91 Apc
->KernelRoutine(Apc
,
96 NormalRoutine(NormalContext
, SystemArgument1
, SystemArgument2
);
98 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
99 Thread
->Tcb
.ApcState
.KernelApcInProgress
--;
102 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
106 KiDeliverUserApc(PKTRAP_FRAME TrapFrame
)
108 * FUNCTION: Tests whether there are any pending APCs for the current thread
109 * and if so the APCs will be delivered on exit from kernel mode.
111 * Thread = Thread to test for alerts
112 * UserContext = The user context saved on entry to kernel mode
115 PLIST_ENTRY current_entry
;
122 DPRINT("KiDeliverUserApc(TrapFrame %x/%x)\n", TrapFrame
,
123 KeGetCurrentThread()->TrapFrame
);
124 Thread
= KeGetCurrentThread();
127 * Check for thread termination
130 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
132 current_entry
= Thread
->ApcState
.ApcListHead
[1].Flink
;
135 * Shouldn't happen but check anyway.
137 if (current_entry
== &Thread
->ApcState
.ApcListHead
[1])
139 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
140 DbgPrint("KiDeliverUserApc called but no APC was pending\n");
144 current_entry
= RemoveHeadList(&Thread
->ApcState
.ApcListHead
[1]);
145 Apc
= CONTAINING_RECORD(current_entry
, KAPC
, ApcListEntry
);
148 * Save the thread's current context (in other words the registers
149 * that will be restored when it returns to user mode) so the
150 * APC dispatcher can restore them later
152 Context
= (PCONTEXT
)(((PUCHAR
)TrapFrame
->Esp
) - sizeof(CONTEXT
));
153 memset(Context
, 0, sizeof(CONTEXT
));
154 Context
->ContextFlags
= CONTEXT_CONTROL
| CONTEXT_INTEGER
|
155 CONTEXT_SEGMENTS
| CONTEXT_i386
;
156 Context
->SegGs
= TrapFrame
->Gs
;
157 Context
->SegFs
= TrapFrame
->Fs
;
158 Context
->SegEs
= TrapFrame
->Es
;
159 Context
->SegDs
= TrapFrame
->Ds
;
160 Context
->Edi
= TrapFrame
->Edi
;
161 Context
->Esi
= TrapFrame
->Esi
;
162 Context
->Ebx
= TrapFrame
->Ebx
;
163 Context
->Edx
= TrapFrame
->Edx
;
164 Context
->Ecx
= TrapFrame
->Ecx
;
165 Context
->Eax
= TrapFrame
->Eax
;
166 Context
->Ebp
= TrapFrame
->Ebp
;
167 Context
->Eip
= TrapFrame
->Eip
;
168 Context
->SegCs
= TrapFrame
->Cs
;
169 Context
->EFlags
= TrapFrame
->Eflags
;
170 Context
->Esp
= TrapFrame
->Esp
;
171 Context
->SegSs
= TrapFrame
->Ss
;
174 * Setup the trap frame so the thread will start executing at the
175 * APC Dispatcher when it returns to user-mode
177 Esp
= (PULONG
)(((PUCHAR
)TrapFrame
->Esp
) -
178 (sizeof(CONTEXT
) + (6 * sizeof(ULONG
))));
181 Esp
[1] = (ULONG
)Apc
->NormalRoutine
;
182 Esp
[2] = (ULONG
)Apc
->NormalContext
;
183 Esp
[3] = (ULONG
)Apc
->SystemArgument1
;
184 Esp
[4] = (ULONG
)Apc
->SystemArgument2
;
185 Esp
[5] = (ULONG
)Context
;
186 TrapFrame
->Eip
= (ULONG
)LdrpGetSystemDllApcDispatcher();
187 TrapFrame
->Esp
= (ULONG
)Esp
;
190 * We've dealt with one pending user-mode APC
192 Thread
->ApcState
.UserApcPending
--;
195 * FIXME: Give some justification for this
197 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
200 * Now call for the kernel routine for the APC, which will free
201 * the APC data structure, we can't do this ourselves because
202 * the APC may be embedded in some larger structure e.g. an IRP
203 * We also give the kernel routine a last chance to modify the arguments to
204 * the user APC routine.
206 Apc
->KernelRoutine(Apc
,
207 (PKNORMAL_ROUTINE
*)&Esp
[1],
212 Thread
->Alerted
[0] = 0;
216 VOID STDCALL
KiDeliverApc(ULONG Unknown1
,
220 * FUNCTION: Deliver an APC to the current thread.
221 * NOTES: This is called from the IRQL switching code if the current thread
222 * is returning from an IRQL greater than or equal to APC_LEVEL to
223 * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
224 * pending APCs will be delivered after a thread gets a new quantum and
225 * after it wakes from a wait.
228 PETHREAD Thread
= PsGetCurrentThread();
233 DPRINT("KiDeliverApc()\n");
234 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
235 while(!IsListEmpty(&(Thread
->Tcb
.ApcState
.ApcListHead
[0])))
237 current
= Thread
->Tcb
.ApcState
.ApcListHead
[0].Blink
;
238 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
239 if (Apc
->NormalRoutine
== NULL
)
241 (void)RemoveTailList(&Thread
->Tcb
.ApcState
.ApcListHead
[0]);
242 Thread
->Tcb
.ApcState
.KernelApcInProgress
++;
243 Thread
->Tcb
.ApcState
.KernelApcPending
--;
245 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
247 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
248 Apc
->KernelRoutine(Apc
,
251 &Apc
->SystemArgument1
,
252 &Apc
->SystemArgument2
);
254 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
255 Thread
->Tcb
.ApcState
.KernelApcInProgress
--;
258 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
262 KeInsertQueueApc (PKAPC Apc
,
263 PVOID SystemArgument1
,
264 PVOID SystemArgument2
,
267 * FUNCTION: Queues an APC for execution
269 * Apc = APC to be queued
270 * SystemArgument[1-2] = TBD
275 PKTHREAD TargetThread
;
277 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
278 "SystemArgument2 %x, Mode %d)\n",Apc
,SystemArgument1
,
279 SystemArgument2
,Mode
);
281 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
283 Apc
->SystemArgument1
= SystemArgument1
;
284 Apc
->SystemArgument2
= SystemArgument2
;
288 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
292 TargetThread
= Apc
->Thread
;
293 if (Apc
->ApcMode
== KernelMode
)
295 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[0],
297 TargetThread
->ApcState
.KernelApcPending
++;
301 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[1],
303 TargetThread
->ApcState
.UserApcPending
++;
305 Apc
->Inserted
= TRUE
;
307 if (Apc
->ApcMode
== KernelMode
&& TargetThread
->KernelApcDisable
>= 1 &&
308 TargetThread
->WaitIrql
< APC_LEVEL
&& Apc
->NormalRoutine
== NULL
)
310 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
313 if (Apc
->ApcMode
== KernelMode
&& Apc
->NormalRoutine
!= NULL
)
315 TargetThread
->Alerted
[1] = 1;
316 if (TargetThread
->Alertable
== TRUE
&&
317 TargetThread
->WaitMode
== UserMode
)
321 Thread
= CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
);
322 KeRemoveAllWaitsThread(Thread
, STATUS_USER_APC
);
327 * For user mode APCs if the thread is already waiting then we wait it
328 * up and increment UserApcPending so it will deliver the APC on exit
329 * from kernel mode. If the thread isn't waiting then before it
330 * enters an alertable, user mode wait then it will check for
331 * user mode APCs and if there are any pending then return immediately
332 * and they will be delivered on exit from kernel mode
334 if (Apc
->ApcMode
== UserMode
&& TargetThread
->Alertable
== TRUE
&&
335 TargetThread
->WaitMode
== UserMode
)
339 DPRINT("Resuming thread for user APC\n");
341 Status
= STATUS_USER_APC
;
342 TargetThread
->Alerted
[0] = 1;
343 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
346 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
350 KeRemoveQueueApc (PKAPC Apc
)
352 * FUNCTION: Removes APC object from the apc queue
354 * Apc = APC to remove
355 * RETURNS: TRUE if the APC was in the queue
357 * NOTE: This function is not exported.
361 PKTHREAD TargetThread
;
363 KeAcquireSpinLockAtDpcLevel(&PiApcLock
);
364 KeRaiseIrql(HIGH_LEVEL
, &oldIrql
);
365 if (Apc
->Inserted
== FALSE
)
367 KeReleaseSpinLock(&PiApcLock
, oldIrql
);
371 TargetThread
= Apc
->Thread
;
372 RemoveEntryList(&Apc
->ApcListEntry
);
373 if (Apc
->ApcMode
== KernelMode
)
375 TargetThread
->ApcState
.KernelApcPending
--;
379 TargetThread
->ApcState
.UserApcPending
--;
381 Apc
->Inserted
= FALSE
;
383 KeReleaseSpinLock(&PiApcLock
, oldIrql
);
389 KeInitializeApc (PKAPC Apc
,
392 PKKERNEL_ROUTINE KernelRoutine
,
393 PKRUNDOWN_ROUTINE RundownRoutine
,
394 PKNORMAL_ROUTINE NormalRoutine
,
398 * FUNCTION: Initialize an APC object
400 * Apc = Pointer to the APC object to initialized
401 * Thread = Thread the APC is to be delivered to
403 * KernelRoutine = Routine to be called for a kernel-mode APC
404 * RundownRoutine = Routine to be called if the thread has exited with
405 * the APC being executed
406 * NormalRoutine = Routine to be called for a user-mode APC
408 * Context = Parameter to be passed to the APC routine
411 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
412 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
413 "Context %x)\n",Apc
,Thread
,StateIndex
,KernelRoutine
,RundownRoutine
,
414 NormalRoutine
,Mode
,Context
);
415 memset(Apc
, 0, sizeof(KAPC
));
416 Apc
->Thread
= Thread
;
417 Apc
->ApcListEntry
.Flink
= NULL
;
418 Apc
->ApcListEntry
.Blink
= NULL
;
419 Apc
->KernelRoutine
= KernelRoutine
;
420 Apc
->RundownRoutine
= RundownRoutine
;
421 Apc
->NormalRoutine
= NormalRoutine
;
422 Apc
->NormalContext
= Context
;
423 Apc
->Inserted
= FALSE
;
424 Apc
->ApcStateIndex
= StateIndex
;
425 if (Apc
->NormalRoutine
!= NULL
)
431 Apc
->ApcMode
= KernelMode
;
435 VOID
NtQueueApcRundownRoutine(PKAPC Apc
)
440 VOID
NtQueueApcKernelRoutine(PKAPC Apc
,
441 PKNORMAL_ROUTINE
* NormalRoutine
,
442 PVOID
* NormalContext
,
443 PVOID
* SystemArgument1
,
444 PVOID
* SystemArgument2
)
449 NTSTATUS STDCALL
NtQueueApcThread(HANDLE ThreadHandle
,
450 PKNORMAL_ROUTINE ApcRoutine
,
452 PVOID SystemArgument1
,
453 PVOID SystemArgument2
)
459 Status
= ObReferenceObjectByHandle(ThreadHandle
,
460 THREAD_ALL_ACCESS
, /* FIXME */
465 if (!NT_SUCCESS(Status
))
470 Apc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
473 ObDereferenceObject(Thread
);
474 return(STATUS_NO_MEMORY
);
480 NtQueueApcKernelRoutine
,
481 NtQueueApcRundownRoutine
,
485 KeInsertQueueApc(Apc
,
490 ObDereferenceObject(Thread
);
491 return(STATUS_SUCCESS
);
495 NTSTATUS STDCALL
NtTestAlert(VOID
)
498 return(STATUS_SUCCESS
);
501 VOID
PiInitApcManagement(VOID
)
503 KeInitializeSpinLock(&PiApcLock
);