ad41ffd106f2e30b492d133cfcbe87d4dc72678e
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /*
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)
7 * UPDATE HISTORY:
8 * Created 22/05/98
9 * 12/11/99: Phillip Susi: Reworked the APC code
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <string.h>
16 #include <internal/string.h>
17 #include <internal/i386/segment.h>
18 #include <internal/ps.h>
19 #include <internal/ke.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 /* GLOBALS *******************************************************************/
25
26 static KSPIN_LOCK PiApcLock;
27
28 /* FUNCTIONS *****************************************************************/
29
30 VOID KeCallKernelRoutineApc(PKAPC Apc)
31 /*
32 * FUNCTION: Call the kernel routine for an APC
33 */
34 {
35 DPRINT("KeCallKernelRoutineApc(Apc %x)\n",Apc);
36
37 Apc->KernelRoutine(Apc,
38 &Apc->NormalRoutine,
39 &Apc->NormalContext,
40 &Apc->SystemArgument1,
41 &Apc->SystemArgument2);
42 DPRINT("Finished KeCallKernelRoutineApc()\n");
43 }
44
45 BOOLEAN KiTestAlert(PKTHREAD Thread,
46 PCONTEXT UserContext)
47 /*
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.
50 * ARGUMENTS:
51 * Thread = Thread to test for alerts
52 * UserContext = The user context saved on entry to kernel mode
53 */
54 {
55 PLIST_ENTRY current_entry;
56 PKAPC Apc;
57 PULONG Esp = (PULONG)UserContext->Esp;
58 KIRQL oldlvl;
59
60 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
61 KeAcquireSpinLock( &PiApcLock, &oldlvl );
62 current_entry = Thread->ApcState.ApcListHead[1].Flink;
63
64 if (current_entry == &Thread->ApcState.ApcListHead[1])
65 {
66 KeReleaseSpinLock( &PiApcLock, oldlvl );
67 return(FALSE);
68 }
69
70 while (current_entry != &Thread->ApcState.ApcListHead[1])
71 {
72 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
73
74 DPRINT("Esp %x\n", Esp);
75 DPRINT("Apc->NormalContext %x\n", Apc->NormalContext);
76 DPRINT("Apc->SystemArgument1 %x\n", Apc->SystemArgument1);
77 DPRINT("Apc->SystemArgument2 %x\n", Apc->SystemArgument2);
78 DPRINT("UserContext->Eip %x\n", UserContext->Eip);
79
80 Esp = Esp - 16;
81 Esp[3] = (ULONG)Apc->SystemArgument2;
82 Esp[2] = (ULONG)Apc->SystemArgument1;
83 Esp[1] = (ULONG)Apc->NormalContext;
84 Esp[0] = UserContext->Eip;
85 UserContext->Eip = (ULONG)Apc->NormalRoutine;
86
87 current_entry = current_entry->Flink;
88
89 /*
90 * Now call for the kernel routine for the APC, which will free
91 * the APC data structure
92 */
93 KeCallKernelRoutineApc(Apc);
94 }
95 UserContext->Esp = (ULONG)Esp;
96 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
97 return(TRUE);
98 }
99
100 VOID KeCallApcsThread(VOID)
101 {
102 PETHREAD Thread = PsGetCurrentThread();
103 PLIST_ENTRY current;
104 PKAPC Apc;
105 KIRQL oldlvl;
106
107 DPRINT("KeCallApcsThread()\n");
108 KeAcquireSpinLock(&PiApcLock, &oldlvl);
109 while(!IsListEmpty(&(Thread->Tcb.ApcState.ApcListHead[0])))
110 {
111 DPRINT("Delivering APC\n");
112 current = RemoveTailList(&(Thread->Tcb.ApcState.ApcListHead[0]));
113 Thread->Tcb.ApcState.KernelApcInProgress++;
114 Thread->Tcb.ApcState.KernelApcPending--;
115 KeReleaseSpinLock(&PiApcLock, oldlvl);
116
117 Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
118 KeCallKernelRoutineApc(Apc);
119
120 KeAcquireSpinLock(&PiApcLock, &oldlvl);
121 DPRINT("Called kernel routine for APC\n");
122 // PsFreezeThread(Thread, NULL, FALSE, KernelMode);
123 DPRINT("Done frozen thread\n");
124 Thread->Tcb.ApcState.KernelApcInProgress--;
125 }
126 KeReleaseSpinLock(&PiApcLock, oldlvl);
127 // Thread->Tcb.WaitStatus = STATUS_KERNEL_APC;
128 }
129
130 VOID KeInsertQueueApc(PKAPC Apc,
131 PVOID SystemArgument1,
132 PVOID SystemArgument2,
133 UCHAR Mode)
134 /*
135 * FUNCTION: Queues an APC for execution
136 * ARGUMENTS:
137 * Apc = APC to be queued
138 * SystemArgument[1-2] = TBD
139 * Mode = TBD
140 */
141 {
142 KIRQL oldlvl;
143 PKTHREAD TargetThread;
144
145 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
146 "SystemArgument2 %x, Mode %d)\n",Apc,SystemArgument1,
147 SystemArgument2,Mode);
148
149 KeAcquireSpinLock(&PiApcLock, &oldlvl);
150
151 Apc->SystemArgument1 = SystemArgument1;
152 Apc->SystemArgument2 = SystemArgument2;
153
154 if (Apc->Inserted)
155 {
156 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
157 KeBugCheck(0);
158 }
159
160 TargetThread = Apc->Thread;
161 if (Apc->ApcMode == KernelMode)
162 {
163 InsertTailList(&TargetThread->ApcState.ApcListHead[0],
164 &Apc->ApcListEntry);
165 TargetThread->ApcState.KernelApcPending++;
166 }
167 else
168 {
169 InsertTailList(&TargetThread->ApcState.ApcListHead[1],
170 &Apc->ApcListEntry);
171 TargetThread->ApcState.KernelApcPending++;
172 TargetThread->ApcState.UserApcPending++;
173 }
174 Apc->Inserted = TRUE;
175
176 if (Apc->ApcMode == KernelMode && TargetThread->KernelApcDisable >= 1 &&
177 TargetThread->WaitIrql < APC_LEVEL)
178 {
179 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
180 STATUS_KERNEL_APC);
181 KeReleaseSpinLock(&PiApcLock, oldlvl);
182 }
183 if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
184 TargetThread->WaitMode == UserMode)
185 {
186 NTSTATUS Status;
187
188 DPRINT("Resuming thread for user APC\n");
189
190 Status = STATUS_USER_APC;
191 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
192 STATUS_USER_APC);
193 KeReleaseSpinLock(&PiApcLock, oldlvl);
194 }
195 }
196
197 VOID KeInitializeApc(PKAPC Apc,
198 PKTHREAD Thread,
199 UCHAR StateIndex,
200 PKKERNEL_ROUTINE KernelRoutine,
201 PKRUNDOWN_ROUTINE RundownRoutine,
202 PKNORMAL_ROUTINE NormalRoutine,
203 UCHAR Mode,
204 PVOID Context)
205 /*
206 * FUNCTION: Initialize an APC object
207 * ARGUMENTS:
208 * Apc = Pointer to the APC object to initialized
209 * Thread = Thread the APC is to be delivered to
210 * StateIndex = TBD
211 * KernelRoutine = Routine to be called for a kernel-mode APC
212 * RundownRoutine = Routine to be called if the thread has exited with
213 * the APC being executed
214 * NormalRoutine = Routine to be called for a user-mode APC
215 * Mode = APC mode
216 * Context = Parameter to be passed to the APC routine
217 */
218 {
219 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
220 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
221 "Context %x)\n",Apc,Thread,StateIndex,KernelRoutine,RundownRoutine,
222 NormalRoutine,Mode,Context);
223 memset(Apc, 0, sizeof(KAPC));
224 Apc->Thread = Thread;
225 Apc->ApcListEntry.Flink = NULL;
226 Apc->ApcListEntry.Blink = NULL;
227 Apc->KernelRoutine = KernelRoutine;
228 Apc->RundownRoutine = RundownRoutine;
229 Apc->NormalRoutine = NormalRoutine;
230 Apc->NormalContext = Context;
231 Apc->Inserted = FALSE;
232 Apc->ApcStateIndex = StateIndex;
233 if (Apc->NormalRoutine != NULL)
234 {
235 Apc->ApcMode = Mode;
236 }
237 else
238 {
239 Apc->ApcMode = KernelMode;
240 }
241 }
242
243
244 NTSTATUS STDCALL NtQueueApcThread(HANDLE ThreadHandle,
245 PKNORMAL_ROUTINE ApcRoutine,
246 PVOID NormalContext,
247 PVOID SystemArgument1,
248 PVOID SystemArgument2)
249 {
250 PKAPC Apc;
251 PETHREAD Thread;
252 NTSTATUS Status;
253
254 Status = ObReferenceObjectByHandle(ThreadHandle,
255 THREAD_ALL_ACCESS, /* FIXME */
256 PsThreadType,
257 UserMode,
258 (PVOID*)&Thread,
259 NULL);
260 if (!NT_SUCCESS(Status))
261 {
262 return(Status);
263 }
264
265 Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
266 if (Apc == NULL)
267 {
268 ObDereferenceObject(Thread);
269 return(STATUS_NO_MEMORY);
270 }
271
272 KeInitializeApc(Apc,
273 &Thread->Tcb,
274 0,
275 NULL,
276 NULL,
277 ApcRoutine,
278 UserMode,
279 NormalContext);
280 KeInsertQueueApc(Apc,
281 SystemArgument1,
282 SystemArgument2,
283 UserMode);
284
285 ObDereferenceObject(Thread);
286 return(STATUS_SUCCESS);
287 }
288
289
290 NTSTATUS STDCALL NtTestAlert(VOID)
291 {
292 KiTestAlert(KeGetCurrentThread(),NULL);
293 return(STATUS_SUCCESS);
294 }
295
296 VOID PiInitApcManagement(VOID)
297 {
298 KeInitializeSpinLock(&PiApcLock);
299 }
300