Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenapc.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: GPL - See COPYING in the top level directory 00004 * FILE: ntoskrnl/ke/apc.c 00005 * PURPOSE: Implements the Asynchronous Procedure Call mechanism 00006 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 00007 */ 00008 00009 /* INCLUDES *****************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 /* PRIVATE FUNCTIONS *********************************************************/ 00016 00017 /*++ 00018 * @name KiCheckForKernelApcDelivery 00019 * @implemented NT 5.2 00020 * 00021 * The KiCheckForKernelApcDelivery routine is called whenever APCs have 00022 * just been re-enabled in Kernel Mode, such as after leaving a Critical or 00023 * Guarded Region. It delivers APCs if the environment is right. 00024 * 00025 * @param None. 00026 * 00027 * @return None. 00028 * 00029 * @remarks This routine allows KeLeave/EnterCritical/GuardedRegion to be used 00030 * as macros from inside WIN32K or other Drivers, which will then only 00031 * have to do an Import API call in the case where APCs are enabled again. 00032 * 00033 *--*/ 00034 VOID 00035 NTAPI 00036 KiCheckForKernelApcDelivery(VOID) 00037 { 00038 KIRQL OldIrql; 00039 00040 /* We should only deliver at passive */ 00041 if (KeGetCurrentIrql() == PASSIVE_LEVEL) 00042 { 00043 /* Raise to APC and Deliver APCs, then lower back to Passive */ 00044 KeRaiseIrql(APC_LEVEL, &OldIrql); 00045 KiDeliverApc(KernelMode, 0, 0); 00046 KeLowerIrql(PASSIVE_LEVEL); 00047 } 00048 else 00049 { 00050 /* 00051 * If we're not at passive level it means someone raised IRQL 00052 * to APC level before the critical or guarded section was entered 00053 * (e.g) by a fast mutex). This implies that the APCs shouldn't 00054 * be delivered now, but after the IRQL is lowered to passive 00055 * level again. 00056 */ 00057 KeGetCurrentThread()->ApcState.KernelApcPending = TRUE; 00058 HalRequestSoftwareInterrupt(APC_LEVEL); 00059 } 00060 } 00061 00062 /*++ 00063 * @name KiInsertQueueApc 00064 * 00065 * The KiInsertQueueApc routine queues a APC for execution when the right 00066 * scheduler environment exists. 00067 * 00068 * @param Apc 00069 * Pointer to an initialized control object of type APC for which the 00070 * caller provides the storage. 00071 * 00072 * @param PriorityBoost 00073 * Priority Boost to apply to the Thread. 00074 * 00075 * @return None 00076 * 00077 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered, 00078 * and at PASSIVE_LEVEL for the NormalRoutine registered. 00079 * 00080 * Callers of this routine must have locked the dipatcher database. 00081 * 00082 *--*/ 00083 VOID 00084 FASTCALL 00085 KiInsertQueueApc(IN PKAPC Apc, 00086 IN KPRIORITY PriorityBoost) 00087 { 00088 PKTHREAD Thread = Apc->Thread; 00089 PKAPC_STATE ApcState; 00090 KPROCESSOR_MODE ApcMode; 00091 PLIST_ENTRY ListHead, NextEntry; 00092 PKAPC QueuedApc; 00093 PKGATE Gate; 00094 NTSTATUS Status; 00095 BOOLEAN RequestInterrupt = FALSE; 00096 00097 /* 00098 * Check if the caller wanted this APC to use the thread's environment at 00099 * insertion time. 00100 */ 00101 if (Apc->ApcStateIndex == InsertApcEnvironment) 00102 { 00103 /* Copy it over */ 00104 Apc->ApcStateIndex = Thread->ApcStateIndex; 00105 } 00106 00107 /* Get the APC State for this Index, and the mode too */ 00108 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex]; 00109 ApcMode = Apc->ApcMode; 00110 00111 /* The APC must be "inserted" already */ 00112 ASSERT(Apc->Inserted == TRUE); 00113 00114 /* Three scenarios: 00115 * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List 00116 * 2) User APC which is PsExitSpecialApc = Put it at the front of the List 00117 * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list 00118 */ 00119 if (Apc->NormalRoutine) 00120 { 00121 /* Normal APC; is it the Thread Termination APC? */ 00122 if ((ApcMode != KernelMode) && 00123 (Apc->KernelRoutine == PsExitSpecialApc)) 00124 { 00125 /* Set User APC pending to true */ 00126 Thread->ApcState.UserApcPending = TRUE; 00127 00128 /* Insert it at the top of the list */ 00129 InsertHeadList(&ApcState->ApcListHead[ApcMode], 00130 &Apc->ApcListEntry); 00131 } 00132 else 00133 { 00134 /* Regular user or kernel Normal APC */ 00135 InsertTailList(&ApcState->ApcListHead[ApcMode], 00136 &Apc->ApcListEntry); 00137 } 00138 } 00139 else 00140 { 00141 /* Special APC, find the last one in the list */ 00142 ListHead = &ApcState->ApcListHead[ApcMode]; 00143 NextEntry = ListHead->Blink; 00144 while (NextEntry != ListHead) 00145 { 00146 /* Get the APC */ 00147 QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry); 00148 00149 /* Is this a No-Normal APC? If so, break */ 00150 if (!QueuedApc->NormalRoutine) break; 00151 00152 /* Move to the previous APC in the Queue */ 00153 NextEntry = NextEntry->Blink; 00154 } 00155 00156 /* Insert us here */ 00157 InsertHeadList(NextEntry, &Apc->ApcListEntry); 00158 } 00159 00160 /* Now check if the Apc State Indexes match */ 00161 if (Thread->ApcStateIndex == Apc->ApcStateIndex) 00162 { 00163 /* Check that the thread matches */ 00164 if (Thread == KeGetCurrentThread()) 00165 { 00166 /* Sanity check */ 00167 ASSERT(Thread->State == Running); 00168 00169 /* Check if this is kernel mode */ 00170 if (ApcMode == KernelMode) 00171 { 00172 /* All valid, a Kernel APC is pending now */ 00173 Thread->ApcState.KernelApcPending = TRUE; 00174 00175 /* Check if Special APCs are disabled */ 00176 if (!Thread->SpecialApcDisable) 00177 { 00178 /* They're not, so request the interrupt */ 00179 HalRequestSoftwareInterrupt(APC_LEVEL); 00180 } 00181 } 00182 } 00183 else 00184 { 00185 /* Acquire the dispatcher lock */ 00186 KiAcquireDispatcherLock(); 00187 00188 /* Check if this is a kernel-mode APC */ 00189 if (ApcMode == KernelMode) 00190 { 00191 /* Kernel-mode APC, set us pending */ 00192 Thread->ApcState.KernelApcPending = TRUE; 00193 00194 /* Are we currently running? */ 00195 if (Thread->State == Running) 00196 { 00197 /* The thread is running, so remember to send a request */ 00198 RequestInterrupt = TRUE; 00199 } 00200 else if ((Thread->State == Waiting) && 00201 (Thread->WaitIrql == PASSIVE_LEVEL) && 00202 !(Thread->SpecialApcDisable) && 00203 (!(Apc->NormalRoutine) || 00204 (!(Thread->KernelApcDisable) && 00205 !(Thread->ApcState.KernelApcInProgress)))) 00206 { 00207 /* We'll unwait with this status */ 00208 Status = STATUS_KERNEL_APC; 00209 00210 /* Wake up the thread */ 00211 KiUnwaitThread(Thread, Status, PriorityBoost); 00212 } 00213 else if (Thread->State == GateWait) 00214 { 00215 /* Lock the thread */ 00216 KiAcquireThreadLock(Thread); 00217 00218 /* Essentially do the same check as above */ 00219 if ((Thread->State == GateWait) && 00220 (Thread->WaitIrql == PASSIVE_LEVEL) && 00221 !(Thread->SpecialApcDisable) && 00222 (!(Apc->NormalRoutine) || 00223 (!(Thread->KernelApcDisable) && 00224 !(Thread->ApcState.KernelApcInProgress)))) 00225 { 00226 /* We were in a gate wait. Handle this. */ 00227 DPRINT1("A thread was in a gate wait\n"); 00228 00229 /* Get the gate */ 00230 Gate = Thread->GateObject; 00231 00232 /* Lock the gate */ 00233 KiAcquireDispatcherObject(&Gate->Header); 00234 00235 /* Remove it from the waiters list */ 00236 RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry); 00237 00238 /* Unlock the gate */ 00239 KiReleaseDispatcherObject(&Gate->Header); 00240 00241 /* Increase the queue counter if needed */ 00242 if (Thread->Queue) Thread->Queue->CurrentCount++; 00243 00244 /* Put into deferred ready list with this status */ 00245 Thread->WaitStatus = STATUS_KERNEL_APC; 00246 KiInsertDeferredReadyList(Thread); 00247 } 00248 00249 /* Release the thread lock */ 00250 KiReleaseThreadLock(Thread); 00251 } 00252 } 00253 else if ((Thread->State == Waiting) && 00254 (Thread->WaitMode == UserMode) && 00255 ((Thread->Alertable) || 00256 (Thread->ApcState.UserApcPending))) 00257 { 00258 /* Set user-mode APC pending */ 00259 Thread->ApcState.UserApcPending = TRUE; 00260 Status = STATUS_USER_APC; 00261 00262 /* Wake up the thread */ 00263 KiUnwaitThread(Thread, Status, PriorityBoost); 00264 } 00265 00266 /* Release dispatcher lock */ 00267 KiReleaseDispatcherLockFromDpcLevel(); 00268 00269 /* Check if an interrupt was requested */ 00270 KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor); 00271 } 00272 } 00273 } 00274 00275 /*++ 00276 * @name KiDeliverApc 00277 * @implemented @NT4 00278 * 00279 * The KiDeliverApc routine is called from IRQL switching code if the 00280 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are 00281 * pending. 00282 * 00283 * @param DeliveryMode 00284 * Specifies the current processor mode. 00285 * 00286 * @param ExceptionFrame 00287 * Pointer to the Exception Frame on non-i386 builds. 00288 * 00289 * @param TrapFrame 00290 * Pointer to the Trap Frame. 00291 * 00292 * @return None. 00293 * 00294 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and 00295 * User-Mode APCs. Note that the TrapFrame is only valid if the 00296 * delivery mode is User-Mode. 00297 * Upon entry, this routine executes at APC_LEVEL. 00298 * 00299 *--*/ 00300 VOID 00301 NTAPI 00302 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode, 00303 IN PKEXCEPTION_FRAME ExceptionFrame, 00304 IN PKTRAP_FRAME TrapFrame) 00305 { 00306 PKTHREAD Thread = KeGetCurrentThread(); 00307 PKPROCESS Process = Thread->ApcState.Process; 00308 PKTRAP_FRAME OldTrapFrame; 00309 PLIST_ENTRY ApcListEntry; 00310 PKAPC Apc; 00311 KLOCK_QUEUE_HANDLE ApcLock; 00312 PKKERNEL_ROUTINE KernelRoutine; 00313 PVOID NormalContext; 00314 PKNORMAL_ROUTINE NormalRoutine; 00315 PVOID SystemArgument1; 00316 PVOID SystemArgument2; 00317 ASSERT_IRQL_EQUAL(APC_LEVEL); 00318 00319 /* Save the old trap frame and set current one */ 00320 OldTrapFrame = Thread->TrapFrame; 00321 Thread->TrapFrame = TrapFrame; 00322 00323 /* Clear Kernel APC Pending */ 00324 Thread->ApcState.KernelApcPending = FALSE; 00325 00326 /* Check if Special APCs are disabled */ 00327 if (Thread->SpecialApcDisable) goto Quickie; 00328 00329 /* Do the Kernel APCs first */ 00330 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) 00331 { 00332 /* Lock the APC Queue */ 00333 KiAcquireApcLockAtApcLevel(Thread, &ApcLock); 00334 00335 /* Check if the list became empty now */ 00336 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) 00337 { 00338 /* It is, release the lock and break out */ 00339 KiReleaseApcLock(&ApcLock); 00340 break; 00341 } 00342 00343 /* Kernel APC is not pending anymore */ 00344 Thread->ApcState.KernelApcPending = FALSE; 00345 00346 /* Get the next Entry */ 00347 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; 00348 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); 00349 00350 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/ 00351 NormalRoutine = Apc->NormalRoutine; 00352 KernelRoutine = Apc->KernelRoutine; 00353 NormalContext = Apc->NormalContext; 00354 SystemArgument1 = Apc->SystemArgument1; 00355 SystemArgument2 = Apc->SystemArgument2; 00356 00357 /* Special APC */ 00358 if (!NormalRoutine) 00359 { 00360 /* Remove the APC from the list */ 00361 RemoveEntryList(ApcListEntry); 00362 Apc->Inserted = FALSE; 00363 00364 /* Release the APC lock */ 00365 KiReleaseApcLock(&ApcLock); 00366 00367 /* Call the Special APC */ 00368 KernelRoutine(Apc, 00369 &NormalRoutine, 00370 &NormalContext, 00371 &SystemArgument1, 00372 &SystemArgument2); 00373 00374 /* Make sure it returned correctly */ 00375 if (KeGetCurrentIrql() != ApcLock.OldIrql) 00376 { 00377 KeBugCheckEx(IRQL_UNEXPECTED_VALUE, 00378 (KeGetCurrentIrql() << 16) | 00379 (ApcLock.OldIrql << 8), 00380 (ULONG_PTR)KernelRoutine, 00381 (ULONG_PTR)Apc, 00382 (ULONG_PTR)NormalRoutine); 00383 } 00384 } 00385 else 00386 { 00387 /* Normal Kernel APC, make sure it's safe to deliver */ 00388 if ((Thread->ApcState.KernelApcInProgress) || 00389 (Thread->KernelApcDisable)) 00390 { 00391 /* Release lock and return */ 00392 KiReleaseApcLock(&ApcLock); 00393 goto Quickie; 00394 } 00395 00396 /* Dequeue the APC */ 00397 RemoveEntryList(ApcListEntry); 00398 Apc->Inserted = FALSE; 00399 00400 /* Go back to APC_LEVEL */ 00401 KiReleaseApcLock(&ApcLock); 00402 00403 /* Call the Kernel APC */ 00404 KernelRoutine(Apc, 00405 &NormalRoutine, 00406 &NormalContext, 00407 &SystemArgument1, 00408 &SystemArgument2); 00409 00410 /* Make sure it returned correctly */ 00411 if (KeGetCurrentIrql() != ApcLock.OldIrql) 00412 { 00413 KeBugCheckEx(IRQL_UNEXPECTED_VALUE, 00414 (KeGetCurrentIrql() << 16) | 00415 (ApcLock.OldIrql << 8), 00416 (ULONG_PTR)KernelRoutine, 00417 (ULONG_PTR)Apc, 00418 (ULONG_PTR)NormalRoutine); 00419 } 00420 00421 /* Check if there still is a Normal Routine */ 00422 if (NormalRoutine) 00423 { 00424 /* At Passive Level, an APC can be prempted by a Special APC */ 00425 Thread->ApcState.KernelApcInProgress = TRUE; 00426 KeLowerIrql(PASSIVE_LEVEL); 00427 00428 /* Call and Raise IRQL back to APC_LEVEL */ 00429 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2); 00430 KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql); 00431 } 00432 00433 /* Set Kernel APC in progress to false and loop again */ 00434 Thread->ApcState.KernelApcInProgress = FALSE; 00435 } 00436 } 00437 00438 /* Now we do the User APCs */ 00439 if ((DeliveryMode == UserMode) && 00440 !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) && 00441 (Thread->ApcState.UserApcPending)) 00442 { 00443 /* Lock the APC Queue */ 00444 KiAcquireApcLockAtApcLevel(Thread, &ApcLock); 00445 00446 /* It's not pending anymore */ 00447 Thread->ApcState.UserApcPending = FALSE; 00448 00449 /* Check if the list became empty now */ 00450 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) 00451 { 00452 /* It is, release the lock and break out */ 00453 KiReleaseApcLock(&ApcLock); 00454 goto Quickie; 00455 } 00456 00457 /* Get the actual APC object */ 00458 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink; 00459 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); 00460 00461 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/ 00462 NormalRoutine = Apc->NormalRoutine; 00463 KernelRoutine = Apc->KernelRoutine; 00464 NormalContext = Apc->NormalContext; 00465 SystemArgument1 = Apc->SystemArgument1; 00466 SystemArgument2 = Apc->SystemArgument2; 00467 00468 /* Remove the APC from Queue, and release the lock */ 00469 RemoveEntryList(ApcListEntry); 00470 Apc->Inserted = FALSE; 00471 KiReleaseApcLock(&ApcLock); 00472 00473 /* Call the kernel routine */ 00474 KernelRoutine(Apc, 00475 &NormalRoutine, 00476 &NormalContext, 00477 &SystemArgument1, 00478 &SystemArgument2); 00479 00480 /* Check if there's no normal routine */ 00481 if (!NormalRoutine) 00482 { 00483 /* Check if more User APCs are Pending */ 00484 KeTestAlertThread(UserMode); 00485 } 00486 else 00487 { 00488 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ 00489 KiInitializeUserApc(ExceptionFrame, 00490 TrapFrame, 00491 NormalRoutine, 00492 NormalContext, 00493 SystemArgument1, 00494 SystemArgument2); 00495 } 00496 } 00497 00498 Quickie: 00499 /* Make sure we're still in the same process */ 00500 if (Process != Thread->ApcState.Process) 00501 { 00502 /* Erm, we got attached or something! BAD! */ 00503 KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT, 00504 (ULONG_PTR)Process, 00505 (ULONG_PTR)Thread->ApcState.Process, 00506 Thread->ApcStateIndex, 00507 KeGetCurrentPrcb()->DpcRoutineActive); 00508 } 00509 00510 /* Restore the trap frame */ 00511 Thread->TrapFrame = OldTrapFrame; 00512 } 00513 00514 FORCEINLINE 00515 VOID 00516 RepairList(IN PLIST_ENTRY Original, 00517 IN PLIST_ENTRY Copy, 00518 IN KPROCESSOR_MODE Mode) 00519 { 00520 /* Check if the list for this mode is empty */ 00521 if (IsListEmpty(&Original[Mode])) 00522 { 00523 /* It is, all we need to do is initialize it */ 00524 InitializeListHead(&Copy[Mode]); 00525 } 00526 else 00527 { 00528 /* Copy the lists */ 00529 Copy[Mode].Flink = Original[Mode].Flink; 00530 Copy[Mode].Blink = Original[Mode].Blink; 00531 Original[Mode].Flink->Blink = &Copy[Mode]; 00532 Original[Mode].Blink->Flink = &Copy[Mode]; 00533 } 00534 } 00535 00536 VOID 00537 NTAPI 00538 KiMoveApcState(PKAPC_STATE OldState, 00539 PKAPC_STATE NewState) 00540 { 00541 /* Restore backup of Original Environment */ 00542 RtlCopyMemory(NewState, OldState, KAPC_STATE_ACTUAL_LENGTH); 00543 00544 /* Repair Lists */ 00545 RepairList(OldState->ApcListHead, NewState->ApcListHead, KernelMode); 00546 RepairList(OldState->ApcListHead, NewState->ApcListHead, UserMode); 00547 } 00548 00549 /* PUBLIC FUNCTIONS **********************************************************/ 00550 00551 /*++ 00552 * @name KeEnterCriticalRegion 00553 * @implemented NT4 00554 * 00555 * The KeEnterCriticalRegion routine temporarily disables the delivery of 00556 * normal kernel APCs; special kernel-mode APCs are still delivered. 00557 * 00558 * @param None. 00559 * 00560 * @return None. 00561 * 00562 * @remarks Highest-level drivers can call this routine while running in the 00563 * context of the thread that requested the current I/O operation. 00564 * Any caller of this routine should call KeLeaveCriticalRegion as 00565 * quickly as possible. 00566 * 00567 * Callers of KeEnterCriticalRegion must be running at IRQL <= 00568 * APC_LEVEL. 00569 * 00570 *--*/ 00571 VOID 00572 NTAPI 00573 _KeEnterCriticalRegion(VOID) 00574 { 00575 /* Use inlined function */ 00576 KeEnterCriticalRegion(); 00577 } 00578 00579 /*++ 00580 * KeLeaveCriticalRegion 00581 * @implemented NT4 00582 * 00583 * The KeLeaveCriticalRegion routine reenables the delivery of normal 00584 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion. 00585 * 00586 * @param None. 00587 * 00588 * @return None. 00589 * 00590 * @remarks Highest-level drivers can call this routine while running in the 00591 * context of the thread that requested the current I/O operation. 00592 * 00593 * Callers of KeLeaveCriticalRegion must be running at IRQL <= 00594 * DISPATCH_LEVEL. 00595 * 00596 *--*/ 00597 VOID 00598 NTAPI 00599 _KeLeaveCriticalRegion(VOID) 00600 { 00601 /* Use inlined version */ 00602 KeLeaveCriticalRegion(); 00603 } 00604 00605 /*++ 00606 * KeInitializeApc 00607 * @implemented NT4 00608 * 00609 * The KeInitializeApc routine initializes an APC object, and registers 00610 * the Kernel, Rundown and Normal routines for that object. 00611 * 00612 * @param Apc 00613 * Pointer to a KAPC structure that represents the APC object to 00614 * initialize. The caller must allocate storage for the structure 00615 * from resident memory. 00616 * 00617 * @param Thread 00618 * Thread to which to deliver the APC. 00619 * 00620 * @param TargetEnvironment 00621 * APC Environment to be used. 00622 * 00623 * @param KernelRoutine 00624 * Points to the KernelRoutine to associate with the APC. 00625 * This routine is executed for all APCs. 00626 * 00627 * @param RundownRoutine 00628 * Points to the RundownRoutine to associate with the APC. 00629 * This routine is executed when the Thread exits during APC execution. 00630 * 00631 * @param NormalRoutine 00632 * Points to the NormalRoutine to associate with the APC. 00633 * This routine is executed at PASSIVE_LEVEL. If this is not specifed, 00634 * the APC becomes a Special APC and the Mode and Context parameters are 00635 * ignored. 00636 * 00637 * @param Mode 00638 * Specifies the processor mode at which to run the Normal Routine. 00639 * 00640 * @param Context 00641 * Specifices the value to pass as Context parameter to the registered 00642 * routines. 00643 * 00644 * @return None. 00645 * 00646 * @remarks The caller can queue an initialized APC with KeInsertQueueApc. 00647 * 00648 *--*/ 00649 VOID 00650 NTAPI 00651 KeInitializeApc(IN PKAPC Apc, 00652 IN PKTHREAD Thread, 00653 IN KAPC_ENVIRONMENT TargetEnvironment, 00654 IN PKKERNEL_ROUTINE KernelRoutine, 00655 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, 00656 IN PKNORMAL_ROUTINE NormalRoutine, 00657 IN KPROCESSOR_MODE Mode, 00658 IN PVOID Context) 00659 { 00660 /* Sanity check */ 00661 ASSERT(TargetEnvironment <= InsertApcEnvironment); 00662 00663 /* Set up the basic APC Structure Data */ 00664 Apc->Type = ApcObject; 00665 Apc->Size = sizeof(KAPC); 00666 00667 /* Set the Environment */ 00668 if (TargetEnvironment == CurrentApcEnvironment) 00669 { 00670 /* Use the current one for the thread */ 00671 Apc->ApcStateIndex = Thread->ApcStateIndex; 00672 } 00673 else 00674 { 00675 /* Sanity check */ 00676 ASSERT((TargetEnvironment <= Thread->ApcStateIndex) || 00677 (TargetEnvironment == InsertApcEnvironment)); 00678 00679 /* Use the one that was given */ 00680 Apc->ApcStateIndex = TargetEnvironment; 00681 } 00682 00683 /* Set the Thread and Routines */ 00684 Apc->Thread = Thread; 00685 Apc->KernelRoutine = KernelRoutine; 00686 Apc->RundownRoutine = RundownRoutine; 00687 Apc->NormalRoutine = NormalRoutine; 00688 00689 /* Check if this is a special APC */ 00690 if (NormalRoutine) 00691 { 00692 /* It's a normal one. Set the context and mode */ 00693 Apc->ApcMode = Mode; 00694 Apc->NormalContext = Context; 00695 } 00696 else 00697 { 00698 /* It's a special APC, which can only be kernel mode */ 00699 Apc->ApcMode = KernelMode; 00700 Apc->NormalContext = NULL; 00701 } 00702 00703 /* The APC is not inserted */ 00704 Apc->Inserted = FALSE; 00705 } 00706 00707 /*++ 00708 * @name KeInsertQueueApc 00709 * @implemented NT4 00710 * 00711 * The KeInsertQueueApc routine queues a APC for execution when the right 00712 * scheduler environment exists. 00713 * 00714 * @param Apc 00715 * Pointer to an initialized control object of type APC for which the 00716 * caller provides the storage. 00717 * 00718 * @param SystemArgument[1,2] 00719 * Pointer to a set of two parameters that contain untyped data. 00720 * 00721 * @param PriorityBoost 00722 * Priority Boost to apply to the Thread. 00723 * 00724 * @return If the APC is already inserted or APC queueing is disabled, FALSE. 00725 * Otherwise, TRUE. 00726 * 00727 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered, 00728 * and at PASSIVE_LEVEL for the NormalRoutine registered. 00729 * 00730 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 00731 * 00732 *--*/ 00733 BOOLEAN 00734 NTAPI 00735 KeInsertQueueApc(IN PKAPC Apc, 00736 IN PVOID SystemArgument1, 00737 IN PVOID SystemArgument2, 00738 IN KPRIORITY PriorityBoost) 00739 { 00740 PKTHREAD Thread = Apc->Thread; 00741 KLOCK_QUEUE_HANDLE ApcLock; 00742 BOOLEAN State = TRUE; 00743 ASSERT_APC(Apc); 00744 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 00745 00746 /* Get the APC lock */ 00747 KiAcquireApcLock(Thread, &ApcLock); 00748 00749 /* Make sure we can Queue APCs and that this one isn't already inserted */ 00750 if (!(Thread->ApcQueueable) || (Apc->Inserted)) 00751 { 00752 /* Fail */ 00753 State = FALSE; 00754 } 00755 else 00756 { 00757 /* Set the System Arguments and set it as inserted */ 00758 Apc->SystemArgument1 = SystemArgument1; 00759 Apc->SystemArgument2 = SystemArgument2; 00760 Apc->Inserted = TRUE; 00761 00762 /* Call the Internal Function */ 00763 KiInsertQueueApc(Apc, PriorityBoost); 00764 } 00765 00766 /* Release the APC lock and return success */ 00767 KiReleaseApcLockFromDpcLevel(&ApcLock); 00768 KiExitDispatcher(ApcLock.OldIrql); 00769 return State; 00770 } 00771 00772 /*++ 00773 * @name KeFlushQueueApc 00774 * @implemented NT4 00775 * 00776 * The KeFlushQueueApc routine flushes all APCs of the given processor mode 00777 * from the specified Thread's APC queue. 00778 * 00779 * @param Thread 00780 * Pointer to the thread whose APC queue will be flushed. 00781 * 00782 * @param PreviousMode 00783 * Specifies which APC Queue to flush. 00784 * 00785 * @return A pointer to the first entry in the flushed APC queue. 00786 * 00787 * @remarks If the routine returns NULL, it means that no APCs were flushed. 00788 * Callers of this routine must be running at DISPATCH_LEVEL or lower. 00789 * 00790 *--*/ 00791 PLIST_ENTRY 00792 NTAPI 00793 KeFlushQueueApc(IN PKTHREAD Thread, 00794 IN KPROCESSOR_MODE PreviousMode) 00795 { 00796 PKAPC Apc; 00797 PLIST_ENTRY FirstEntry, CurrentEntry; 00798 KLOCK_QUEUE_HANDLE ApcLock; 00799 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 00800 00801 /* Check if this was user mode */ 00802 if (PreviousMode == UserMode) 00803 { 00804 /* Get the APC lock */ 00805 KiAcquireApcLock(Thread, &ApcLock); 00806 00807 /* Select user list and check if it's empty */ 00808 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) 00809 { 00810 /* Don't return anything */ 00811 FirstEntry = NULL; 00812 goto FlushDone; 00813 } 00814 } 00815 else 00816 { 00817 /* Select kernel list and check if it's empty */ 00818 if (IsListEmpty( &Thread->ApcState.ApcListHead[KernelMode])) 00819 { 00820 /* Don't return anything */ 00821 return NULL; 00822 } 00823 00824 /* Otherwise, acquire the APC lock */ 00825 KiAcquireApcLock(Thread, &ApcLock); 00826 } 00827 00828 /* Get the first entry and check if the list is empty now */ 00829 FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink; 00830 if (FirstEntry == &Thread->ApcState.ApcListHead[PreviousMode]) 00831 { 00832 /* It is, clear the returned entry */ 00833 FirstEntry = NULL; 00834 } 00835 else 00836 { 00837 /* It's not, remove the first entry */ 00838 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]); 00839 00840 /* Loop all the entries */ 00841 CurrentEntry = FirstEntry; 00842 do 00843 { 00844 /* Get the APC and make it un-inserted */ 00845 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry); 00846 Apc->Inserted = FALSE; 00847 00848 /* Get the next entry */ 00849 CurrentEntry = CurrentEntry->Flink; 00850 } while (CurrentEntry != FirstEntry); 00851 00852 /* Re-initialize the list */ 00853 InitializeListHead(&Thread->ApcState.ApcListHead[PreviousMode]); 00854 } 00855 00856 /* Release the lock */ 00857 FlushDone: 00858 KiReleaseApcLock(&ApcLock); 00859 00860 /* Return the first entry */ 00861 return FirstEntry; 00862 } 00863 00864 /*++ 00865 * @name KeRemoveQueueApc 00866 * @implemented NT4 00867 * 00868 * The KeRemoveQueueApc routine removes a given APC object from the system 00869 * APC queue. 00870 * 00871 * @param Apc 00872 * Pointer to an initialized APC object that was queued by calling 00873 * KeInsertQueueApc. 00874 * 00875 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation 00876 * is performed and FALSE is returned. 00877 * 00878 * @remarks If the given APC Object is currently queued, it is removed from the 00879 * queue and any calls to the registered routines are cancelled. 00880 * 00881 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 00882 * 00883 *--*/ 00884 BOOLEAN 00885 NTAPI 00886 KeRemoveQueueApc(IN PKAPC Apc) 00887 { 00888 PKTHREAD Thread = Apc->Thread; 00889 PKAPC_STATE ApcState; 00890 BOOLEAN Inserted; 00891 KLOCK_QUEUE_HANDLE ApcLock; 00892 ASSERT_APC(Apc); 00893 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 00894 00895 /* Get the APC lock */ 00896 KiAcquireApcLock(Thread, &ApcLock); 00897 00898 /* Check if it's inserted */ 00899 Inserted = Apc->Inserted; 00900 if (Inserted) 00901 { 00902 /* Set it as non-inserted and get the APC state */ 00903 Apc->Inserted = FALSE; 00904 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex]; 00905 00906 /* Acquire the dispatcher lock and remove it from the list */ 00907 KiAcquireDispatcherLockAtDpcLevel(); 00908 if (RemoveEntryList(&Apc->ApcListEntry)) 00909 { 00910 /* Set the correct state based on the APC Mode */ 00911 if (Apc->ApcMode == KernelMode) 00912 { 00913 /* No more pending kernel APCs */ 00914 ApcState->KernelApcPending = FALSE; 00915 } 00916 else 00917 { 00918 /* No more pending user APCs */ 00919 ApcState->UserApcPending = FALSE; 00920 } 00921 } 00922 00923 /* Release dispatcher lock */ 00924 KiReleaseDispatcherLockFromDpcLevel(); 00925 } 00926 00927 /* Release the lock and return */ 00928 KiReleaseApcLock(&ApcLock); 00929 return Inserted; 00930 } 00931 00932 /*++ 00933 * @name KeAreApcsDisabled 00934 * @implemented NT4 00935 * 00936 * The KeAreApcsDisabled routine returns whether kernel APC delivery is 00937 * disabled for the current thread. 00938 * 00939 * @param None. 00940 * 00941 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical 00942 * region or a guarded region, and FALSE otherwise. 00943 * 00944 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled 00945 * to determine if normal kernel APCs are disabled. 00946 * 00947 * A thread that is inside critical region has both user APCs and 00948 * normal kernel APCs disabled, but not special kernel APCs. 00949 * 00950 * A thread that is inside a guarded region has all APCs disabled, 00951 * including special kernel APCs. 00952 * 00953 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 00954 * 00955 *--*/ 00956 BOOLEAN 00957 NTAPI 00958 KeAreApcsDisabled(VOID) 00959 { 00960 /* Return the Kernel APC State */ 00961 return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE; 00962 } 00963 00964 /*++ 00965 * @name KeAreAllApcsDisabled 00966 * @implemented NT5.1 00967 * 00968 * The KeAreAllApcsDisabled routine returns whether the calling thread is 00969 * inside a guarded region or running at IRQL >= APC_LEVEL, which disables 00970 * all APC delivery. 00971 * 00972 * @param None. 00973 * 00974 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded 00975 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise. 00976 * 00977 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to 00978 * determine if all APC delivery is disabled. 00979 * 00980 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. 00981 * 00982 *--*/ 00983 BOOLEAN 00984 NTAPI 00985 KeAreAllApcsDisabled(VOID) 00986 { 00987 /* Return the Special APC State */ 00988 return ((KeGetCurrentThread()->SpecialApcDisable) || 00989 (KeGetCurrentIrql() >= APC_LEVEL)) ? TRUE : FALSE; 00990 } Generated on Sun May 27 2012 04:27:11 for ReactOS by
1.7.6.1
|