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
;
57 PULONG Esp
= (PULONG
)UserContext
->Esp
;
60 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
61 KeAcquireSpinLock( &PiApcLock
, &oldlvl
);
62 current_entry
= Thread
->ApcState
.ApcListHead
[1].Flink
;
64 if (current_entry
== &Thread
->ApcState
.ApcListHead
[1])
66 KeReleaseSpinLock( &PiApcLock
, oldlvl
);
70 while (current_entry
!= &Thread
->ApcState
.ApcListHead
[1])
72 Apc
= CONTAINING_RECORD(current_entry
, KAPC
, ApcListEntry
);
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
);
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
;
87 current_entry
= current_entry
->Flink
;
90 * Now call for the kernel routine for the APC, which will free
91 * the APC data structure
93 KeCallKernelRoutineApc(Apc
);
95 UserContext
->Esp
= (ULONG
)Esp
;
96 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
100 VOID
KeCallApcsThread(VOID
)
102 PETHREAD Thread
= PsGetCurrentThread();
107 DPRINT("KeCallApcsThread()\n");
108 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
109 while(!IsListEmpty(&(Thread
->Tcb
.ApcState
.ApcListHead
[0])))
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
);
117 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
118 KeCallKernelRoutineApc(Apc
);
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
--;
126 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
127 // Thread->Tcb.WaitStatus = STATUS_KERNEL_APC;
130 VOID
KeInsertQueueApc(PKAPC Apc
,
131 PVOID SystemArgument1
,
132 PVOID SystemArgument2
,
135 * FUNCTION: Queues an APC for execution
137 * Apc = APC to be queued
138 * SystemArgument[1-2] = TBD
143 PKTHREAD TargetThread
;
145 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
146 "SystemArgument2 %x, Mode %d)\n",Apc
,SystemArgument1
,
147 SystemArgument2
,Mode
);
149 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
151 Apc
->SystemArgument1
= SystemArgument1
;
152 Apc
->SystemArgument2
= SystemArgument2
;
156 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
160 TargetThread
= Apc
->Thread
;
161 if (Apc
->ApcMode
== KernelMode
)
163 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[0],
165 TargetThread
->ApcState
.KernelApcPending
++;
169 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[1],
171 TargetThread
->ApcState
.KernelApcPending
++;
172 TargetThread
->ApcState
.UserApcPending
++;
174 Apc
->Inserted
= TRUE
;
176 if (Apc
->ApcMode
== KernelMode
&& TargetThread
->KernelApcDisable
>= 1 &&
177 TargetThread
->WaitIrql
< APC_LEVEL
)
179 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
181 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
183 if (Apc
->ApcMode
== UserMode
&& TargetThread
->Alertable
== TRUE
&&
184 TargetThread
->WaitMode
== UserMode
)
188 DPRINT("Resuming thread for user APC\n");
190 Status
= STATUS_USER_APC
;
191 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
193 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
197 VOID
KeInitializeApc(PKAPC Apc
,
200 PKKERNEL_ROUTINE KernelRoutine
,
201 PKRUNDOWN_ROUTINE RundownRoutine
,
202 PKNORMAL_ROUTINE NormalRoutine
,
206 * FUNCTION: Initialize an APC object
208 * Apc = Pointer to the APC object to initialized
209 * Thread = Thread the APC is to be delivered to
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
216 * Context = Parameter to be passed to the APC routine
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
)
239 Apc
->ApcMode
= KernelMode
;
244 NTSTATUS STDCALL
NtQueueApcThread(HANDLE ThreadHandle
,
245 PKNORMAL_ROUTINE ApcRoutine
,
247 PVOID SystemArgument1
,
248 PVOID SystemArgument2
)
254 Status
= ObReferenceObjectByHandle(ThreadHandle
,
255 THREAD_ALL_ACCESS
, /* FIXME */
260 if (!NT_SUCCESS(Status
))
265 Apc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
268 ObDereferenceObject(Thread
);
269 return(STATUS_NO_MEMORY
);
280 KeInsertQueueApc(Apc
,
285 ObDereferenceObject(Thread
);
286 return(STATUS_SUCCESS
);
290 NTSTATUS STDCALL
NtTestAlert(VOID
)
292 KiTestAlert(KeGetCurrentThread(),NULL
);
293 return(STATUS_SUCCESS
);
296 VOID
PiInitApcManagement(VOID
)
298 KeInitializeSpinLock(&PiApcLock
);