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 *******************************************************************/
27 extern KSPIN_LOCK PiThreadListLock
;
29 VOID
PsTerminateCurrentThread(NTSTATUS ExitStatus
);
31 /* FUNCTIONS *****************************************************************/
33 VOID
KeCallKernelRoutineApc(PKAPC Apc
)
35 * FUNCTION: Call the kernel routine for an APC
38 DPRINT("KeCallKernelRoutineApc(Apc %x)\n",Apc
);
40 Apc
->KernelRoutine(Apc
,
43 &Apc
->SystemArgument1
,
44 &Apc
->SystemArgument2
);
45 DPRINT("Finished KeCallKernelRoutineApc()\n");
48 BOOLEAN
KiTestAlert(PKTHREAD Thread
,
51 * FUNCTION: Tests whether there are any pending APCs for the current thread
52 * and if so the APCs will be delivered on exit from kernel mode.
54 * Thread = Thread to test for alerts
55 * UserContext = The user context saved on entry to kernel mode
58 PLIST_ENTRY current_entry
;
66 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
69 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
70 current_entry
= Thread
->ApcState
.ApcListHead
[1].Flink
;
72 if (current_entry
== &Thread
->ApcState
.ApcListHead
[1])
74 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
78 current_entry
= RemoveHeadList(&Thread
->ApcState
.ApcListHead
[1]);
79 Apc
= CONTAINING_RECORD(current_entry
, KAPC
, ApcListEntry
);
81 DPRINT("Esp %x\n", Esp
);
82 DPRINT("Apc->NormalContext %x\n", Apc
->NormalContext
);
83 DPRINT("Apc->SystemArgument1 %x\n", Apc
->SystemArgument1
);
84 DPRINT("Apc->SystemArgument2 %x\n", Apc
->SystemArgument2
);
85 DPRINT("UserContext->Eip %x\n", UserContext
->Eip
);
87 Esp
= (PULONG
)UserContext
->Esp
;
89 memcpy(&SavedContext
, UserContext
, sizeof(CONTEXT
));
91 Esp
= Esp
- (sizeof(CONTEXT
) + (5 * sizeof(ULONG
)));
92 memcpy(Esp
, &SavedContext
, sizeof(CONTEXT
));
93 Top
= sizeof(CONTEXT
) / 4;
94 Esp
[Top
] = (ULONG
)Apc
->NormalRoutine
;
95 Esp
[Top
+ 1] = (ULONG
)Apc
->NormalContext
;
96 Esp
[Top
+ 2] = (ULONG
)Apc
->SystemArgument1
;
97 Esp
[Top
+ 3] = (ULONG
)Apc
->SystemArgument2
;
98 Esp
[Top
+ 4] = (ULONG
)Esp
- sizeof(CONTEXT
);
99 UserContext
->Eip
= 0; // KiUserApcDispatcher
101 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
104 * Now call for the kernel routine for the APC, which will free
105 * the APC data structure
107 KeCallKernelRoutineApc(Apc
);
109 KeAcquireSpinLock( &PiThreadListLock
, &oldlvl
);
110 if( (CONTAINING_RECORD( Thread
, ETHREAD
, Tcb
))->DeadThread
)
112 KeReleaseSpinLock( &PiThreadListLock
, oldlvl
);
113 PsTerminateCurrentThread( (CONTAINING_RECORD( Thread
, ETHREAD
, Tcb
))->ExitStatus
);
118 VOID
KeCallApcsThread(VOID
)
120 PETHREAD Thread
= PsGetCurrentThread();
125 DPRINT("KeCallApcsThread()\n");
126 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
127 while(!IsListEmpty(&(Thread
->Tcb
.ApcState
.ApcListHead
[0])))
129 DPRINT("Delivering APC\n");
130 current
= RemoveTailList(&(Thread
->Tcb
.ApcState
.ApcListHead
[0]));
131 Thread
->Tcb
.ApcState
.KernelApcInProgress
++;
132 Thread
->Tcb
.ApcState
.KernelApcPending
--;
133 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
135 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
136 KeCallKernelRoutineApc(Apc
);
138 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
139 DPRINT("Called kernel routine for APC\n");
140 // PsFreezeThread(Thread, NULL, FALSE, KernelMode);
141 DPRINT("Done frozen thread\n");
142 Thread
->Tcb
.ApcState
.KernelApcInProgress
--;
144 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
145 // Thread->Tcb.WaitStatus = STATUS_KERNEL_APC;
152 PVOID SystemArgument1
,
153 PVOID SystemArgument2
,
157 * FUNCTION: Queues an APC for execution
159 * Apc = APC to be queued
160 * SystemArgument[1-2] = TBD
165 PKTHREAD TargetThread
;
167 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
168 "SystemArgument2 %x, Mode %d)\n",Apc
,SystemArgument1
,
169 SystemArgument2
,Mode
);
171 KeAcquireSpinLock(&PiApcLock
, &oldlvl
);
173 Apc
->SystemArgument1
= SystemArgument1
;
174 Apc
->SystemArgument2
= SystemArgument2
;
178 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
182 TargetThread
= Apc
->Thread
;
183 if (Apc
->ApcMode
== KernelMode
)
185 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[0],
187 TargetThread
->ApcState
.KernelApcPending
++;
191 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[1],
193 TargetThread
->ApcState
.KernelApcPending
++;
194 TargetThread
->ApcState
.UserApcPending
++;
196 Apc
->Inserted
= TRUE
;
198 if (Apc
->ApcMode
== KernelMode
&& TargetThread
->KernelApcDisable
>= 1 &&
199 TargetThread
->WaitIrql
< APC_LEVEL
)
201 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
203 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
205 if (Apc
->ApcMode
== UserMode
&& TargetThread
->Alertable
== TRUE
&&
206 TargetThread
->WaitMode
== UserMode
)
210 DPRINT("Resuming thread for user APC\n");
212 Status
= STATUS_USER_APC
;
213 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread
, ETHREAD
, Tcb
),
215 KeReleaseSpinLock(&PiApcLock
, oldlvl
);
225 PKKERNEL_ROUTINE KernelRoutine
,
226 PKRUNDOWN_ROUTINE RundownRoutine
,
227 PKNORMAL_ROUTINE NormalRoutine
,
232 * FUNCTION: Initialize an APC object
234 * Apc = Pointer to the APC object to initialized
235 * Thread = Thread the APC is to be delivered to
237 * KernelRoutine = Routine to be called for a kernel-mode APC
238 * RundownRoutine = Routine to be called if the thread has exited with
239 * the APC being executed
240 * NormalRoutine = Routine to be called for a user-mode APC
242 * Context = Parameter to be passed to the APC routine
245 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
246 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
247 "Context %x)\n",Apc
,Thread
,StateIndex
,KernelRoutine
,RundownRoutine
,
248 NormalRoutine
,Mode
,Context
);
249 memset(Apc
, 0, sizeof(KAPC
));
250 Apc
->Thread
= Thread
;
251 Apc
->ApcListEntry
.Flink
= NULL
;
252 Apc
->ApcListEntry
.Blink
= NULL
;
253 Apc
->KernelRoutine
= KernelRoutine
;
254 Apc
->RundownRoutine
= RundownRoutine
;
255 Apc
->NormalRoutine
= NormalRoutine
;
256 Apc
->NormalContext
= Context
;
257 Apc
->Inserted
= FALSE
;
258 Apc
->ApcStateIndex
= StateIndex
;
259 if (Apc
->NormalRoutine
!= NULL
)
265 Apc
->ApcMode
= KernelMode
;
270 NTSTATUS STDCALL
NtQueueApcThread(HANDLE ThreadHandle
,
271 PKNORMAL_ROUTINE ApcRoutine
,
273 PVOID SystemArgument1
,
274 PVOID SystemArgument2
)
280 Status
= ObReferenceObjectByHandle(ThreadHandle
,
281 THREAD_ALL_ACCESS
, /* FIXME */
286 if (!NT_SUCCESS(Status
))
291 Apc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
294 ObDereferenceObject(Thread
);
295 return(STATUS_NO_MEMORY
);
306 KeInsertQueueApc(Apc
,
311 ObDereferenceObject(Thread
);
312 return(STATUS_SUCCESS
);
316 NTSTATUS STDCALL
NtTestAlert(VOID
)
318 KiTestAlert(KeGetCurrentThread(),NULL
);
319 return(STATUS_SUCCESS
);
322 VOID
PiInitApcManagement(VOID
)
324 KeInitializeSpinLock(&PiApcLock
);