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>
16 #include <internal/string.h>
17 #include <internal/i386/segment.h>
18 #include <internal/ps.h>
19 #include <internal/ke.h>
22 #include <internal/debug.h>
24 /* GLOBALS *******************************************************************/
26 static KSPIN_LOCK PiApcLock
;
28 /* FUNCTIONS *****************************************************************/
30 VOID
KeCallKernelRoutineApc(PKAPC Apc
)
32 * FUNCTION: Call the kernel routine for an APC
35 DPRINT("KeCallKernelRoutineApc(Apc %x)\n",Apc
);
37 Apc
->KernelRoutine(Apc
,
40 &Apc
->SystemArgument1
,
41 &Apc
->SystemArgument2
);
42 DPRINT("Finished KeCallKernelRoutineApc()\n");
45 BOOLEAN
KiTestAlert(PKTHREAD Thread
,
48 * FUNCTION: Tests whether there are any pending APCs for the current thread
49 * and if so the APCs will be delivered on exit from kernel mode.
51 * Thread = Thread to test for alerts
52 * UserContext = The user context saved on entry to kernel mode
55 PLIST_ENTRY current_entry
;
62 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
63 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
64 current_entry
= Thread
->ApcState
.ApcListHead
[1].Flink
;
66 if (current_entry
== &Thread
->ApcState
.ApcListHead
[1])
68 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
72 current_entry
= RemoveHeadList(&Thread
->ApcState
.ApcListHead
[1]);
73 Apc
= CONTAINING_RECORD(current_entry
, KAPC
, ApcListEntry
);
75 DPRINT("Esp %x\n", Esp
);
76 DPRINT("Apc->NormalContext %x\n", Apc
->NormalContext
);
77 DPRINT("Apc->SystemArgument1 %x\n", Apc
->SystemArgument1
);
78 DPRINT("Apc->SystemArgument2 %x\n", Apc
->SystemArgument2
);
79 DPRINT("UserContext->Eip %x\n", UserContext
->Eip
);
81 Esp
= (PULONG
)UserContext
->Esp
;
83 memcpy(&SavedContext
, UserContext
, sizeof(CONTEXT
));
86 * Now call for the kernel routine for the APC, which will free
87 * the APC data structure
89 KeCallKernelRoutineApc(Apc
);
91 Esp
= Esp
- (sizeof(CONTEXT
) + (4 * sizeof(ULONG
)));
92 memcpy(Esp
, &SavedContext
, sizeof(CONTEXT
));
93 Top
= sizeof(CONTEXT
) / 4;
94 Esp
[Top
] = (ULONG
)Apc
->SystemArgument2
;
95 Esp
[Top
+ 1] = (ULONG
)Apc
->SystemArgument1
;
96 Esp
[Top
+ 2] = (ULONG
)Apc
->NormalContext
;
97 Esp
[Top
+ 3] = (ULONG
)Apc
->NormalRoutine
;
100 current_entry
= current_entry
->Flink
;
106 VOID
KeCallApcsThread(VOID
)
108 PETHREAD Thread
= PsGetCurrentThread();
113 DPRINT("KeCallApcsThread()\n");
114 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
115 while(!IsListEmpty(&(Thread
->Tcb
.ApcState
.ApcListHead
[0])))
117 DPRINT("Delivering APC\n");
118 current
= RemoveTailList(&(Thread
->Tcb
.ApcState
.ApcListHead
[0]));
119 Thread
->Tcb
.ApcState
.KernelApcInProgress
++;
120 Thread
->Tcb
.ApcState
.KernelApcPending
--;
121 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
123 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
124 KeCallKernelRoutineApc(Apc
);
126 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
127 DPRINT("Called kernel routine for APC\n");
128 // PsFreezeThread(Thread, NULL, FALSE, KernelMode);
129 DPRINT("Done frozen thread\n");
130 Thread
->Tcb
.ApcState
.KernelApcInProgress
--;
132 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
133 // Thread->Tcb.WaitStatus = STATUS_KERNEL_APC;
140 PVOID SystemArgument1
,
141 PVOID SystemArgument2
,
145 * FUNCTION: Queues an APC for execution
147 * Apc = APC to be queued
148 * SystemArgument[1-2] = TBD
153 PKTHREAD TargetThread
;
155 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
156 "SystemArgument2 %x, Mode %d)\n",Apc
,SystemArgument1
,
157 SystemArgument2
,Mode
);
159 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
161 Apc
->SystemArgument1
= SystemArgument1
;
162 Apc
->SystemArgument2
= SystemArgument2
;
166 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
170 TargetThread
= Apc
->Thread
;
171 if (Apc
->ApcMode
== KernelMode
)
173 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[0],
175 TargetThread
->ApcState
.KernelApcPending
++;
179 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[1],
181 TargetThread
->ApcState
.KernelApcPending
++;
182 TargetThread
->ApcState
.UserApcPending
++;
184 Apc
->Inserted
= TRUE
;
186 if (Apc
->ApcMode
== KernelMode
&& TargetThread
->KernelApcDisable
>= 1 &&
187 TargetThread
->WaitIrql
< APC_LEVEL
)
189 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
191 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
193 if (Apc
->ApcMode
== UserMode
&& TargetThread
->Alertable
== TRUE
&&
194 TargetThread
->WaitMode
== UserMode
)
198 DPRINT("Resuming thread for user APC\n");
200 Status
= STATUS_USER_APC
;
201 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
203 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
213 PKKERNEL_ROUTINE KernelRoutine
,
214 PKRUNDOWN_ROUTINE RundownRoutine
,
215 PKNORMAL_ROUTINE NormalRoutine
,
220 * FUNCTION: Initialize an APC object
222 * Apc = Pointer to the APC object to initialized
223 * Thread = Thread the APC is to be delivered to
225 * KernelRoutine = Routine to be called for a kernel-mode APC
226 * RundownRoutine = Routine to be called if the thread has exited with
227 * the APC being executed
228 * NormalRoutine = Routine to be called for a user-mode APC
230 * Context = Parameter to be passed to the APC routine
233 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
234 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
235 "Context %x)\n",Apc
,Thread
,StateIndex
,KernelRoutine
,RundownRoutine
,
236 NormalRoutine
,Mode
,Context
);
237 memset(Apc
, 0, sizeof(KAPC
));
238 Apc
->Thread
= Thread
;
239 Apc
->ApcListEntry
.Flink
= NULL
;
240 Apc
->ApcListEntry
.Blink
= NULL
;
241 Apc
->KernelRoutine
= KernelRoutine
;
242 Apc
->RundownRoutine
= RundownRoutine
;
243 Apc
->NormalRoutine
= NormalRoutine
;
244 Apc
->NormalContext
= Context
;
245 Apc
->Inserted
= FALSE
;
246 Apc
->ApcStateIndex
= StateIndex
;
247 if (Apc
->NormalRoutine
!= NULL
)
253 Apc
->ApcMode
= KernelMode
;
258 NTSTATUS STDCALL
NtQueueApcThread(HANDLE ThreadHandle
,
259 PKNORMAL_ROUTINE ApcRoutine
,
261 PVOID SystemArgument1
,
262 PVOID SystemArgument2
)
268 Status
= ObReferenceObjectByHandle(ThreadHandle
,
269 THREAD_ALL_ACCESS
, /* FIXME */
274 if (!NT_SUCCESS(Status
))
279 Apc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
282 ObDereferenceObject(Thread
);
283 return(STATUS_NO_MEMORY
);
294 KeInsertQueueApc(Apc
,
299 ObDereferenceObject(Thread
);
300 return(STATUS_SUCCESS
);
304 NTSTATUS STDCALL
NtTestAlert(VOID
)
306 KiTestAlert(KeGetCurrentThread(),NULL
);
307 return(STATUS_SUCCESS
);
310 VOID
PiInitApcManagement(VOID
)
312 KeInitializeSpinLock(&PiApcLock
);