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>
21 #include <internal/debug.h>
23 extern VOID
KeApcProlog(VOID
);
24 static KSPIN_LOCK PiApcLock
;
26 /* PROTOTYPES ****************************************************************/
28 VOID
KeApcProlog3(PKAPC Apc
);
30 /* FUNCTIONS *****************************************************************/
32 BOOLEAN
KiTestAlert(PKTHREAD Thread
,
35 * FUNCTION: Tests whether there are any pending APCs for the current thread
36 * and if so the APCs will be delivered on exit from kernel mode.
38 * Thread = Thread to test for alerts
39 * UserContext = The user context saved on entry to kernel mode
42 PLIST_ENTRY current_entry
;
44 PULONG Esp
= (PULONG
)UserContext
->Esp
;
47 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
48 KeAcquireSpinLock( &PiApcLock
, &oldlvl
);
49 current_entry
= Thread
->ApcState
.ApcListHead
[1].Flink
;
51 if (current_entry
== &Thread
->ApcState
.ApcListHead
[1])
53 KeReleaseSpinLock( &PiApcLock
, oldlvl
);
57 while (current_entry
!= &Thread
->ApcState
.ApcListHead
[1])
59 Apc
= CONTAINING_RECORD(current_entry
, KAPC
, ApcListEntry
);
61 DPRINT("Esp %x\n", Esp
);
62 DPRINT("Apc->NormalContext %x\n", Apc
->NormalContext
);
63 DPRINT("Apc->SystemArgument1 %x\n", Apc
->SystemArgument1
);
64 DPRINT("Apc->SystemArgument2 %x\n", Apc
->SystemArgument2
);
65 DPRINT("UserContext->Eip %x\n", UserContext
->Eip
);
68 Esp
[3] = (ULONG
)Apc
->SystemArgument2
;
69 Esp
[2] = (ULONG
)Apc
->SystemArgument1
;
70 Esp
[1] = (ULONG
)Apc
->NormalContext
;
71 Esp
[0] = UserContext
->Eip
;
72 UserContext
->Eip
= (ULONG
)Apc
->NormalRoutine
;
74 current_entry
= current_entry
->Flink
;
77 * Now call for the kernel routine for the APC, which will free
78 * the APC data structure
82 UserContext
->Esp
= (ULONG
)Esp
;
83 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
89 PETHREAD Thread
= PsGetCurrentThread();
94 KeLowerIrql( APC_LEVEL
);
95 KeAcquireSpinLock( &PiApcLock
, &oldlvl
);
96 while( !IsListEmpty( current
) )
98 current
= RemoveTailList( &(Thread
->Tcb
.ApcState
.ApcListHead
[0]) );
99 KeReleaseSpinLock( &PiApcLock
, oldlvl
);
100 Apc
= CONTAINING_RECORD(current
, KAPC
, ApcListEntry
);
102 KeAcquireSpinLock( &PiApcLock
, &oldlvl
);
104 KeReleaseSpinLock( &PiApcLock
, oldlvl
);
105 Thread
->Tcb
.WaitStatus
= STATUS_ALERTED
;
106 KeLowerIrql( PASSIVE_LEVEL
);
109 VOID
KeApcProlog3(PKAPC Apc
)
111 * FUNCTION: This is called from the prolog proper (in assembly) to deliver
115 PKTHREAD Thread
= KeGetCurrentThread();
116 DPRINT("KeApcProlog2(Apc %x)\n",Apc
);
117 InterlockedIncrement( &(Thread
->ApcState
.KernelApcInProgress
) );
118 InterlockedDecrement( &(Thread
->ApcState
.KernelApcPending
) );
120 Apc
->KernelRoutine(Apc
,
123 &Apc
->SystemArgument1
,
124 &Apc
->SystemArgument2
);
125 InterlockedDecrement( &(Thread
->ApcState
.KernelApcInProgress
) );
128 VOID
KeDeliverKernelApc( PKTHREAD TargetThread
)
130 * FUNCTION: Simulates an interrupt on the target thread which will transfer
131 * control to a kernel mode routine
132 * Must be called while holding the PiApcLock
137 DPRINT("KeDeliverKernelApc(Apc %x)\n", Apc
);
138 if (TargetThread
== KeGetCurrentThread())
144 TargetThread
->Context
.esp
= TargetThread
->Context
.esp
- 12;
145 Stack
= (PULONG
)TargetThread
->Context
.esp
;
146 Stack
[0] = TargetThread
->Context
.eip
;
147 Stack
[1] = TargetThread
->Context
.cs
;
148 Stack
[2] = TargetThread
->Context
.eflags
;
149 TargetThread
->Context
.eip
= (ULONG
)KeApcProlog
;
151 PsResumeThread(CONTAINING_RECORD(TargetThread
,ETHREAD
,Tcb
),
155 VOID
KeInsertQueueApc(PKAPC Apc
,
156 PVOID SystemArgument1
,
157 PVOID SystemArgument2
,
160 * FUNCTION: Queues an APC for execution
162 * Apc = APC to be queued
163 * SystemArgument[1-2] = TBD
168 PKTHREAD TargetThread
;
170 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
171 "SystemArgument2 %x, Mode %d)\n",Apc
,SystemArgument1
,
172 SystemArgument2
,Mode
);
174 KeAcquireSpinLock( &PiApcLock
, &oldlvl
);
176 Apc
->SystemArgument1
= SystemArgument1
;
177 Apc
->SystemArgument2
= SystemArgument2
;
181 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
185 TargetThread
= Apc
->Thread
;
186 if (Apc
->ApcMode
== KernelMode
)
188 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[0],
190 TargetThread
->ApcState
.KernelApcPending
++;
194 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[1],
196 TargetThread
->ApcState
.KernelApcPending
++;
197 TargetThread
->ApcState
.UserApcPending
++;
199 Apc
->Inserted
= TRUE
;
201 DPRINT("TargetThread->KernelApcDisable %d\n",
202 TargetThread
->KernelApcDisable
);
203 DPRINT("Apc->KernelRoutine %x\n", Apc
->KernelRoutine
);
204 if (Apc
->ApcMode
== KernelMode
&& TargetThread
->KernelApcDisable
>= 1 )
205 if( TargetThread
!= PsGetCurrentThread() )
207 PsSuspendThread( CONTAINING_RECORD( TargetThread
, ETHREAD
, Tcb
), NULL
, TRUE
, KernelMode
);
208 KeReleaseSpinLock( &PiApcLock
, oldlvl
);
209 if( TargetThread
->Alertable
&& TargetThread
->WaitIrql
< APC_LEVEL
)
210 KeDeliverKernelApc( TargetThread
);
212 else if( TargetThread
->Alertable
&& TargetThread
->WaitIrql
< APC_LEVEL
)
213 KeDeliverKernelApc( TargetThread
);
216 DPRINT("Queuing APC for later delivery\n");
218 if (Apc
->ApcMode
== UserMode
&& TargetThread
->Alertable
== TRUE
&&
219 TargetThread
->WaitMode
== UserMode
)
223 DPRINT("Resuming thread for user APC\n");
225 Status
= STATUS_USER_APC
;
226 PsResumeThread((PETHREAD
)TargetThread
,
231 VOID
KeInitializeApc(PKAPC Apc
,
234 PKKERNEL_ROUTINE KernelRoutine
,
235 PKRUNDOWN_ROUTINE RundownRoutine
,
236 PKNORMAL_ROUTINE NormalRoutine
,
240 * FUNCTION: Initialize an APC object
242 * Apc = Pointer to the APC object to initialized
243 * Thread = Thread the APC is to be delivered to
245 * KernelRoutine = Routine to be called for a kernel-mode APC
246 * RundownRoutine = Routine to be called if the thread has exited with
247 * the APC being executed
248 * NormalRoutine = Routine to be called for a user-mode APC
250 * Context = Parameter to be passed to the APC routine
253 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
254 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
255 "Context %x)\n",Apc
,Thread
,StateIndex
,KernelRoutine
,RundownRoutine
,
256 NormalRoutine
,Mode
,Context
);
257 memset(Apc
, 0, sizeof(KAPC
));
258 Apc
->Thread
= Thread
;
259 Apc
->ApcListEntry
.Flink
= NULL
;
260 Apc
->ApcListEntry
.Blink
= NULL
;
261 Apc
->KernelRoutine
= KernelRoutine
;
262 Apc
->RundownRoutine
= RundownRoutine
;
263 Apc
->NormalRoutine
= NormalRoutine
;
264 Apc
->NormalContext
= Context
;
265 Apc
->Inserted
= FALSE
;
266 Apc
->ApcStateIndex
= StateIndex
;
267 if (Apc
->NormalRoutine
!= NULL
)
273 Apc
->ApcMode
= KernelMode
;
278 NTSTATUS STDCALL
NtQueueApcThread(HANDLE ThreadHandle
,
279 PKNORMAL_ROUTINE ApcRoutine
,
281 PVOID SystemArgument1
,
282 PVOID SystemArgument2
)
288 Status
= ObReferenceObjectByHandle(ThreadHandle
,
289 THREAD_ALL_ACCESS
, /* FIXME */
294 if (!NT_SUCCESS(Status
))
299 Apc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
302 ObDereferenceObject(Thread
);
303 return(STATUS_NO_MEMORY
);
314 KeInsertQueueApc(Apc
,
319 ObDereferenceObject(Thread
);
320 return(STATUS_SUCCESS
);
324 NTSTATUS STDCALL
NtTestAlert(VOID
)
326 KiTestAlert(KeGetCurrentThread(),NULL
);
327 return(STATUS_SUCCESS
);
330 VOID
PiInitApcManagement()
332 KeInitializeSpinLock( &PiApcLock
);