Windows-Server-2003/base/ntos/ke/apcobj.c

388 lines
10 KiB
C

/*++
Copyright (c) 1989-1994 Microsoft Corporation
Module Name:
apcobj.c
Abstract:
This module implements the kernel APC object. Functions are provided
to initialize, flush, insert, and remove APC objects.
Author:
David N. Cutler (davec) 5-Mar-1989
Environment:
Kernel mode only.
Revision History:
--*/
#include "ki.h"
//
// The following assert macro is used to check that an input apc is
// really a kapc and not something else, like deallocated pool.
//
#define ASSERT_APC(E) { \
ASSERT((E)->Type == ApcObject); \
}
VOID
KeInitializeApc (
IN PRKAPC Apc,
IN PRKTHREAD Thread,
IN KAPC_ENVIRONMENT Environment,
IN PKKERNEL_ROUTINE KernelRoutine,
IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
IN KPROCESSOR_MODE ApcMode OPTIONAL,
IN PVOID NormalContext OPTIONAL
)
/*++
Routine Description:
This function initializes a kernel APC object. The thread, kernel
routine, and optionally a normal routine, processor mode, and normal
context parameter are stored in the APC object.
Arguments:
Apc - Supplies a pointer to a control object of type APC.
Thread - Supplies a pointer to a dispatcher object of type thread.
Environment - Supplies the environment in which the APC will execute.
Valid values for this parameter are: OriginalApcEnvironment,
AttachedApcEnvironment, CurrentApcEnvironment, or InsertApcEnvironment
KernelRoutine - Supplies a pointer to a function that is to be
executed at IRQL APC_LEVEL in kernel mode.
RundownRoutine - Supplies an optional pointer to a function that is to be
called if the APC is in a thread's APC queue when the thread terminates.
NormalRoutine - Supplies an optional pointer to a function that is
to be executed at IRQL 0 in the specified processor mode. If this
parameter is not specified, then the ProcessorMode and NormalContext
parameters are ignored.
ApcMode - Supplies the processor mode in which the function specified
by the NormalRoutine parameter is to be executed.
NormalContext - Supplies a pointer to an arbitrary data structure which is
to be passed to the function specified by the NormalRoutine parameter.
Return Value:
None.
--*/
{
ASSERT(Environment <= InsertApcEnvironment);
//
// Initialize standard control object header.
//
Apc->Type = ApcObject;
Apc->Size = sizeof(KAPC);
//
// Initialize the APC environment, thread address, kernel routine address,
// rundown routine address, normal routine address, processor mode, and
// normal context parameter. If the normal routine address is null, then
// the processor mode is defaulted to KernelMode and the APC is a special
// APC. Otherwise, the processor mode is taken from the argument list.
//
if (Environment == CurrentApcEnvironment) {
Apc->ApcStateIndex = Thread->ApcStateIndex;
} else {
ASSERT((Environment <= Thread->ApcStateIndex) || (Environment == InsertApcEnvironment));
Apc->ApcStateIndex = (CCHAR)Environment;
}
Apc->Thread = Thread;
Apc->KernelRoutine = KernelRoutine;
Apc->RundownRoutine = RundownRoutine;
Apc->NormalRoutine = NormalRoutine;
if (ARGUMENT_PRESENT(NormalRoutine)) {
Apc->ApcMode = ApcMode;
Apc->NormalContext = NormalContext;
} else {
Apc->ApcMode = KernelMode;
Apc->NormalContext = NIL;
}
Apc->Inserted = FALSE;
return;
}
PLIST_ENTRY
KeFlushQueueApc (
IN PKTHREAD Thread,
IN KPROCESSOR_MODE ApcMode
)
/*++
Routine Description:
This function flushes the APC queue selected by the specified processor
mode for the specified thread. An APC queue is flushed by removing the
listhead from the list, scanning the APC entries in the list, setting
their inserted variables to FALSE, and then returning the address of the
doubly linked list as the function value.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
ApcMode - Supplies the processor mode of the APC queue that is to
be flushed.
Return Value:
The address of the first entry in the list of APC objects that were flushed
from the specified APC queue.
--*/
{
PKAPC Apc;
PLIST_ENTRY FirstEntry;
KLOCK_QUEUE_HANDLE LockHandle;
PLIST_ENTRY NextEntry;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// If the APC mode is user mode, then acquire the thread APC queue lock
// to ensure that no further APCs are queued after a possible setting of
// the thread APC queueable state.
//
if (ApcMode == UserMode) {
KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
if (IsListEmpty(&Thread->ApcState.ApcListHead[ApcMode])) {
KeReleaseInStackQueuedSpinLock(&LockHandle);
return NULL;
}
} else {
if (IsListEmpty(&Thread->ApcState.ApcListHead[ApcMode])) {
return NULL;
} else {
KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
}
}
//
// Get address of first APC in the list and check if the list is
// empty or contains entries that should be flushed. If entries
// should be flushed, then scan the list of APC objects and set their
// inserted state to FALSE.
//
FirstEntry = Thread->ApcState.ApcListHead[ApcMode].Flink;
if (FirstEntry == &Thread->ApcState.ApcListHead[ApcMode]) {
FirstEntry = (PLIST_ENTRY)NULL;
} else {
RemoveEntryList(&Thread->ApcState.ApcListHead[ApcMode]);
NextEntry = FirstEntry;
do {
Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
Apc->Inserted = FALSE;
NextEntry = NextEntry->Flink;
} while (NextEntry != FirstEntry);
//
// Reinitialize the header so the current thread may safely attach
// to another process.
//
InitializeListHead(&Thread->ApcState.ApcListHead[ApcMode]);
}
//
// Unlock the thread APC queue lock, lower IRQL to its previous value,
// and return address of the first entry in list of APC objects that
// were flushed.
//
KeReleaseInStackQueuedSpinLock(&LockHandle);
return FirstEntry;
}
BOOLEAN
KeInsertQueueApc (
IN PRKAPC Apc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2,
IN KPRIORITY Increment
)
/*++
Routine Description:
This function inserts an APC object into the APC queue specifed by the
thread and processor mode fields of the APC object. If the APC object
is already in an APC queue or APC queuing is disabled, then no operation
is performed. Otherwise the APC object is inserted in the specified queue
and appropriate scheduling decisions are made.
Arguments:
Apc - Supplies a pointer to a control object of type APC.
SystemArgument1, SystemArgument2 - Supply a set of two arguments that
contain untyped data provided by the executive.
Increment - Supplies the priority increment that is to be applied if
queuing the APC causes a thread wait to be satisfied.
Return Value:
If the APC object is already in an APC queue or APC queuing is disabled,
then a value of FALSE is returned. Otherwise a value of TRUE is returned.
--*/
{
BOOLEAN Inserted;
KLOCK_QUEUE_HANDLE LockHandle;
PRKTHREAD Thread;
ASSERT_APC(Apc);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to SYNCH_LEVEL and acquire the thread APC queue lock.
//
Thread = Apc->Thread;
KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
//
// If APC queuing is disabled or the APC is already inserted, then set
// inserted to FALSE. Otherwise, set the system parameter values in the
// APC object, inser the APC in the thread APC queue, and set inserted to
// true.
//
if ((Thread->ApcQueueable == FALSE) ||
(Apc->Inserted == TRUE)) {
Inserted = FALSE;
} else {
Apc->Inserted = TRUE;
Apc->SystemArgument1 = SystemArgument1;
Apc->SystemArgument2 = SystemArgument2;
KiInsertQueueApc(Apc, Increment);
Inserted = TRUE;
}
//
// Unlock the thread APC queue lock, exit the scheduler, and return
// whether the APC was inserted.
//
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
KiExitDispatcher(LockHandle.OldIrql);
return Inserted;
}
BOOLEAN
KeRemoveQueueApc (
IN PKAPC Apc
)
/*++
Routine Description:
This function removes an APC object from an APC queue. If the APC object
is not in an APC queue, then no operation is performed. Otherwise the
APC object is removed from its current queue and its inserted state is
set FALSE.
Arguments:
Apc - Supplies a pointer to a control object of type APC.
Return Value:
If the APC object is not in an APC queue, then a value of FALSE is returned.
Otherwise a value of TRUE is returned.
--*/
{
PKAPC_STATE ApcState;
BOOLEAN Inserted;
KLOCK_QUEUE_HANDLE LockHandle;
PRKTHREAD Thread;
ASSERT_APC(Apc);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to SYNCH_LEVEL and acquire the thread APC queue lock.
//
Thread = Apc->Thread;
KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
//
// If the APC object is in an APC queue, then remove it from the queue
// and set its inserted state to FALSE. If the queue becomes empty, set
// the APC pending state to FALSE.
//
Inserted = Apc->Inserted;
if (Inserted != FALSE) {
Apc->Inserted = FALSE;
ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];
KiLockDispatcherDatabaseAtSynchLevel();
if (RemoveEntryList(&Apc->ApcListEntry) != FALSE) {
if (Apc->ApcMode == KernelMode) {
ApcState->KernelApcPending = FALSE;
} else {
ApcState->UserApcPending = FALSE;
}
}
KiUnlockDispatcherDatabaseFromSynchLevel();
}
//
// Release the thread APC queue lock, lower IRQL to its previous value,
// and return whether an APC object was removed from the APC queue.
//
KeReleaseInStackQueuedSpinLock(&LockHandle);
return Inserted;
}