Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenwait.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/wait.c 00005 * PURPOSE: Manages waiting for Dispatcher Objects 00006 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 00007 * Gunnar Dalsnes 00008 */ 00009 00010 /* INCLUDES ******************************************************************/ 00011 00012 #include <ntoskrnl.h> 00013 #define NDEBUG 00014 #include <debug.h> 00015 00016 /* PRIVATE FUNCTIONS *********************************************************/ 00017 00018 VOID 00019 FASTCALL 00020 KiWaitTest(IN PVOID ObjectPointer, 00021 IN KPRIORITY Increment) 00022 { 00023 PLIST_ENTRY WaitEntry, WaitList; 00024 PKWAIT_BLOCK WaitBlock; 00025 PKTHREAD WaitThread; 00026 PKMUTANT FirstObject = ObjectPointer; 00027 NTSTATUS WaitStatus; 00028 00029 /* Loop the Wait Entries */ 00030 WaitList = &FirstObject->Header.WaitListHead; 00031 WaitEntry = WaitList->Flink; 00032 while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList)) 00033 { 00034 /* Get the current wait block */ 00035 WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); 00036 WaitThread = WaitBlock->Thread; 00037 WaitStatus = STATUS_KERNEL_APC; 00038 00039 /* Check the current Wait Mode */ 00040 if (WaitBlock->WaitType == WaitAny) 00041 { 00042 /* Easy case, satisfy only this wait */ 00043 WaitStatus = (NTSTATUS)WaitBlock->WaitKey; 00044 KiSatisfyObjectWait(FirstObject, WaitThread); 00045 } 00046 00047 /* Now do the rest of the unwait */ 00048 KiUnwaitThread(WaitThread, WaitStatus, Increment); 00049 WaitEntry = WaitList->Flink; 00050 } 00051 } 00052 00053 VOID 00054 FASTCALL 00055 KiUnlinkThread(IN PKTHREAD Thread, 00056 IN LONG_PTR WaitStatus) 00057 { 00058 PKWAIT_BLOCK WaitBlock; 00059 PKTIMER Timer; 00060 00061 /* Update wait status */ 00062 Thread->WaitStatus |= WaitStatus; 00063 00064 /* Remove the Wait Blocks from the list */ 00065 WaitBlock = Thread->WaitBlockList; 00066 do 00067 { 00068 /* Remove it */ 00069 RemoveEntryList(&WaitBlock->WaitListEntry); 00070 00071 /* Go to the next one */ 00072 WaitBlock = WaitBlock->NextWaitBlock; 00073 } while (WaitBlock != Thread->WaitBlockList); 00074 00075 /* Remove the thread from the wait list! */ 00076 if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry); 00077 00078 /* Check if there's a Thread Timer */ 00079 Timer = &Thread->Timer; 00080 if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer); 00081 00082 /* Increment the Queue's active threads */ 00083 if (Thread->Queue) Thread->Queue->CurrentCount++; 00084 } 00085 00086 /* Must be called with the dispatcher lock held */ 00087 VOID 00088 FASTCALL 00089 KiUnwaitThread(IN PKTHREAD Thread, 00090 IN LONG_PTR WaitStatus, 00091 IN KPRIORITY Increment) 00092 { 00093 /* Unlink the thread */ 00094 KiUnlinkThread(Thread, WaitStatus); 00095 00096 /* Tell the scheduler do to the increment when it readies the thread */ 00097 ASSERT(Increment >= 0); 00098 Thread->AdjustIncrement = (SCHAR)Increment; 00099 Thread->AdjustReason = AdjustUnwait; 00100 00101 /* Reschedule the Thread */ 00102 KiReadyThread(Thread); 00103 } 00104 00105 VOID 00106 FASTCALL 00107 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex) 00108 { 00109 /* Increase contention count */ 00110 FastMutex->Contention++; 00111 00112 /* Wait for the event */ 00113 KeWaitForSingleObject(&FastMutex->Event, 00114 WrMutex, 00115 KernelMode, 00116 FALSE, 00117 NULL); 00118 } 00119 00120 VOID 00121 FASTCALL 00122 KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex) 00123 { 00124 ULONG BitsToRemove, BitsToAdd; 00125 LONG OldValue, NewValue; 00126 00127 /* We depend on these bits being just right */ 00128 C_ASSERT((GM_LOCK_WAITER_WOKEN * 2) == GM_LOCK_WAITER_INC); 00129 00130 /* Increase the contention count */ 00131 GuardedMutex->Contention++; 00132 00133 /* Start by unlocking the Guarded Mutex */ 00134 BitsToRemove = GM_LOCK_BIT; 00135 BitsToAdd = GM_LOCK_WAITER_INC; 00136 00137 /* Start change loop */ 00138 for (;;) 00139 { 00140 /* Loop sanity checks */ 00141 ASSERT((BitsToRemove == GM_LOCK_BIT) || 00142 (BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN))); 00143 ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) || 00144 (BitsToAdd == GM_LOCK_WAITER_WOKEN)); 00145 00146 /* Get the Count Bits */ 00147 OldValue = GuardedMutex->Count; 00148 00149 /* Start internal bit change loop */ 00150 for (;;) 00151 { 00152 /* Check if the Guarded Mutex is locked */ 00153 if (OldValue & GM_LOCK_BIT) 00154 { 00155 /* Sanity check */ 00156 ASSERT((BitsToRemove == GM_LOCK_BIT) || 00157 ((OldValue & GM_LOCK_WAITER_WOKEN) != 0)); 00158 00159 /* Unlock it by removing the Lock Bit */ 00160 NewValue = OldValue ^ BitsToRemove; 00161 NewValue = InterlockedCompareExchange(&GuardedMutex->Count, 00162 NewValue, 00163 OldValue); 00164 if (NewValue == OldValue) return; 00165 } 00166 else 00167 { 00168 /* The Guarded Mutex isn't locked, so simply set the bits */ 00169 NewValue = OldValue + BitsToAdd; 00170 NewValue = InterlockedCompareExchange(&GuardedMutex->Count, 00171 NewValue, 00172 OldValue); 00173 if (NewValue == OldValue) break; 00174 } 00175 00176 /* Old value changed, loop again */ 00177 OldValue = NewValue; 00178 } 00179 00180 /* Now we have to wait for it */ 00181 KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode); 00182 ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0); 00183 00184 /* Ok, the wait is done, so set the new bits */ 00185 BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN; 00186 BitsToAdd = GM_LOCK_WAITER_WOKEN; 00187 } 00188 } 00189 00190 // 00191 // This routine exits the dispatcher after a compatible operation and 00192 // swaps the context to the next scheduled thread on the current CPU if 00193 // one is available. 00194 // 00195 // It does NOT attempt to scan for a new thread to schedule. 00196 // 00197 VOID 00198 FASTCALL 00199 KiExitDispatcher(IN KIRQL OldIrql) 00200 { 00201 PKPRCB Prcb = KeGetCurrentPrcb(); 00202 PKTHREAD Thread, NextThread; 00203 BOOLEAN PendingApc; 00204 00205 /* Make sure we're at synchronization level */ 00206 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL); 00207 00208 /* Check if we have deferred threads */ 00209 KiCheckDeferredReadyList(Prcb); 00210 00211 /* Check if we were called at dispatcher level or higher */ 00212 if (OldIrql >= DISPATCH_LEVEL) 00213 { 00214 /* Check if we have a thread to schedule, and that no DPC is active */ 00215 if ((Prcb->NextThread) && !(Prcb->DpcRoutineActive)) 00216 { 00217 /* Request DPC interrupt */ 00218 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 00219 } 00220 00221 /* Lower IRQL and exit */ 00222 goto Quickie; 00223 } 00224 00225 /* Make sure there's a new thread scheduled */ 00226 if (!Prcb->NextThread) goto Quickie; 00227 00228 /* Lock the PRCB */ 00229 KiAcquirePrcbLock(Prcb); 00230 00231 /* Get the next and current threads now */ 00232 NextThread = Prcb->NextThread; 00233 Thread = Prcb->CurrentThread; 00234 00235 /* Set current thread's swap busy to true */ 00236 KiSetThreadSwapBusy(Thread); 00237 00238 /* Switch threads in PRCB */ 00239 Prcb->NextThread = NULL; 00240 Prcb->CurrentThread = NextThread; 00241 00242 /* Set thread to running */ 00243 NextThread->State = Running; 00244 00245 /* Queue it on the ready lists */ 00246 KxQueueReadyThread(Thread, Prcb); 00247 00248 /* Set wait IRQL */ 00249 Thread->WaitIrql = OldIrql; 00250 00251 /* Swap threads and check if APCs were pending */ 00252 PendingApc = KiSwapContext(OldIrql, Thread); 00253 if (PendingApc) 00254 { 00255 /* Lower only to APC */ 00256 KeLowerIrql(APC_LEVEL); 00257 00258 /* Deliver APCs */ 00259 KiDeliverApc(KernelMode, NULL, NULL); 00260 ASSERT(OldIrql == PASSIVE_LEVEL); 00261 } 00262 00263 /* Lower IRQl back */ 00264 Quickie: 00265 KeLowerIrql(OldIrql); 00266 } 00267 00268 /* PUBLIC FUNCTIONS **********************************************************/ 00269 00270 /* 00271 * @implemented 00272 */ 00273 NTSTATUS 00274 NTAPI 00275 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode, 00276 IN BOOLEAN Alertable, 00277 IN PLARGE_INTEGER Interval OPTIONAL) 00278 { 00279 PKTIMER Timer; 00280 PKWAIT_BLOCK TimerBlock; 00281 PKTHREAD Thread = KeGetCurrentThread(); 00282 NTSTATUS WaitStatus; 00283 BOOLEAN Swappable; 00284 PLARGE_INTEGER OriginalDueTime; 00285 LARGE_INTEGER DueTime, NewDueTime, InterruptTime; 00286 ULONG Hand = 0; 00287 00288 /* If this is a user-mode wait of 0 seconds, yield execution */ 00289 if (!(Interval->QuadPart) && (WaitMode != KernelMode)) 00290 { 00291 /* Make sure the wait isn't alertable or interrupting an APC */ 00292 if (!(Alertable) && !(Thread->ApcState.UserApcPending)) 00293 { 00294 /* Yield execution */ 00295 NtYieldExecution(); 00296 } 00297 } 00298 00299 /* Setup the original time and timer/wait blocks */ 00300 OriginalDueTime = Interval; 00301 Timer = &Thread->Timer; 00302 TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; 00303 00304 /* Check if the lock is already held */ 00305 if (!Thread->WaitNext) goto WaitStart; 00306 00307 /* Otherwise, we already have the lock, so initialize the wait */ 00308 Thread->WaitNext = FALSE; 00309 KxDelayThreadWait(); 00310 00311 /* Start wait loop */ 00312 for (;;) 00313 { 00314 /* Disable pre-emption */ 00315 Thread->Preempted = FALSE; 00316 00317 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 00318 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 00319 (Thread->WaitIrql < APC_LEVEL)) 00320 { 00321 /* Unlock the dispatcher */ 00322 KiReleaseDispatcherLock(Thread->WaitIrql); 00323 } 00324 else 00325 { 00326 /* Check if we have to bail out due to an alerted state */ 00327 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 00328 if (WaitStatus != STATUS_WAIT_0) break; 00329 00330 /* Check if the timer expired */ 00331 InterruptTime.QuadPart = KeQueryInterruptTime(); 00332 if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart) 00333 { 00334 /* It did, so we don't need to wait */ 00335 goto NoWait; 00336 } 00337 00338 /* It didn't, so activate it */ 00339 Timer->Header.Inserted = TRUE; 00340 00341 /* Handle Kernel Queues */ 00342 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 00343 00344 /* Setup the wait information */ 00345 Thread->State = Waiting; 00346 00347 /* Add the thread to the wait list */ 00348 KiAddThreadToWaitList(Thread, Swappable); 00349 00350 /* Insert the timer and swap the thread */ 00351 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 00352 KiSetThreadSwapBusy(Thread); 00353 KxInsertTimer(Timer, Hand); 00354 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 00355 00356 /* Check if were swapped ok */ 00357 if (WaitStatus != STATUS_KERNEL_APC) 00358 { 00359 /* This is a good thing */ 00360 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS; 00361 00362 /* Return Status */ 00363 return WaitStatus; 00364 } 00365 00366 /* Recalculate due times */ 00367 Interval = KiRecalculateDueTime(OriginalDueTime, 00368 &DueTime, 00369 &NewDueTime); 00370 } 00371 00372 WaitStart: 00373 /* Setup a new wait */ 00374 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 00375 KxDelayThreadWait(); 00376 KiAcquireDispatcherLockAtDpcLevel(); 00377 } 00378 00379 /* We're done! */ 00380 KiReleaseDispatcherLock(Thread->WaitIrql); 00381 return WaitStatus; 00382 00383 NoWait: 00384 /* There was nothing to wait for. Did we have a wait interval? */ 00385 if (!Interval->QuadPart) 00386 { 00387 /* Unlock the dispatcher and do a yield */ 00388 KiReleaseDispatcherLock(Thread->WaitIrql); 00389 return NtYieldExecution(); 00390 } 00391 00392 /* Unlock the dispatcher and adjust the quantum for a no-wait */ 00393 KiReleaseDispatcherLockFromDpcLevel(); 00394 KiAdjustQuantumThread(Thread); 00395 return STATUS_SUCCESS; 00396 } 00397 00398 /* 00399 * @implemented 00400 */ 00401 NTSTATUS 00402 NTAPI 00403 KeWaitForSingleObject(IN PVOID Object, 00404 IN KWAIT_REASON WaitReason, 00405 IN KPROCESSOR_MODE WaitMode, 00406 IN BOOLEAN Alertable, 00407 IN PLARGE_INTEGER Timeout OPTIONAL) 00408 { 00409 PKTHREAD Thread = KeGetCurrentThread(); 00410 PKMUTANT CurrentObject = (PKMUTANT)Object; 00411 PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0]; 00412 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; 00413 PKTIMER Timer = &Thread->Timer; 00414 NTSTATUS WaitStatus; 00415 BOOLEAN Swappable; 00416 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime; 00417 PLARGE_INTEGER OriginalDueTime = Timeout; 00418 ULONG Hand = 0; 00419 00420 /* Check if the lock is already held */ 00421 if (!Thread->WaitNext) goto WaitStart; 00422 00423 /* Otherwise, we already have the lock, so initialize the wait */ 00424 Thread->WaitNext = FALSE; 00425 KxSingleThreadWait(); 00426 00427 /* Start wait loop */ 00428 for (;;) 00429 { 00430 /* Disable pre-emption */ 00431 Thread->Preempted = FALSE; 00432 00433 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 00434 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 00435 (Thread->WaitIrql < APC_LEVEL)) 00436 { 00437 /* Unlock the dispatcher */ 00438 KiReleaseDispatcherLock(Thread->WaitIrql); 00439 } 00440 else 00441 { 00442 /* Sanity check */ 00443 ASSERT(CurrentObject->Header.Type != QueueObject); 00444 00445 /* Check if it's a mutant */ 00446 if (CurrentObject->Header.Type == MutantObject) 00447 { 00448 /* Check its signal state or if we own it */ 00449 if ((CurrentObject->Header.SignalState > 0) || 00450 (Thread == CurrentObject->OwnerThread)) 00451 { 00452 /* Just unwait this guy and exit */ 00453 if (CurrentObject->Header.SignalState != (LONG)MINLONG) 00454 { 00455 /* It has a normal signal state. Unwait and return */ 00456 KiSatisfyMutantWait(CurrentObject, Thread); 00457 WaitStatus = (NTSTATUS)Thread->WaitStatus; 00458 goto DontWait; 00459 } 00460 else 00461 { 00462 /* Raise an exception */ 00463 KiReleaseDispatcherLock(Thread->WaitIrql); 00464 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 00465 } 00466 } 00467 } 00468 else if (CurrentObject->Header.SignalState > 0) 00469 { 00470 /* Another satisfied object */ 00471 KiSatisfyNonMutantWait(CurrentObject); 00472 WaitStatus = STATUS_WAIT_0; 00473 goto DontWait; 00474 } 00475 00476 /* Make sure we can satisfy the Alertable request */ 00477 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 00478 if (WaitStatus != STATUS_WAIT_0) break; 00479 00480 /* Enable the Timeout Timer if there was any specified */ 00481 if (Timeout) 00482 { 00483 /* Check if the timer expired */ 00484 InterruptTime.QuadPart = KeQueryInterruptTime(); 00485 if ((ULONGLONG)InterruptTime.QuadPart >= 00486 Timer->DueTime.QuadPart) 00487 { 00488 /* It did, so we don't need to wait */ 00489 WaitStatus = STATUS_TIMEOUT; 00490 goto DontWait; 00491 } 00492 00493 /* It didn't, so activate it */ 00494 Timer->Header.Inserted = TRUE; 00495 } 00496 00497 /* Link the Object to this Wait Block */ 00498 InsertTailList(&CurrentObject->Header.WaitListHead, 00499 &WaitBlock->WaitListEntry); 00500 00501 /* Handle Kernel Queues */ 00502 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 00503 00504 /* Setup the wait information */ 00505 Thread->State = Waiting; 00506 00507 /* Add the thread to the wait list */ 00508 KiAddThreadToWaitList(Thread, Swappable); 00509 00510 /* Activate thread swap */ 00511 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 00512 KiSetThreadSwapBusy(Thread); 00513 00514 /* Check if we have a timer */ 00515 if (Timeout) 00516 { 00517 /* Insert it */ 00518 KxInsertTimer(Timer, Hand); 00519 } 00520 else 00521 { 00522 /* Otherwise, unlock the dispatcher */ 00523 KiReleaseDispatcherLockFromDpcLevel(); 00524 } 00525 00526 /* Do the actual swap */ 00527 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 00528 00529 /* Check if we were executing an APC */ 00530 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; 00531 00532 /* Check if we had a timeout */ 00533 if (Timeout) 00534 { 00535 /* Recalculate due times */ 00536 Timeout = KiRecalculateDueTime(OriginalDueTime, 00537 &DueTime, 00538 &NewDueTime); 00539 } 00540 } 00541 WaitStart: 00542 /* Setup a new wait */ 00543 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 00544 KxSingleThreadWait(); 00545 KiAcquireDispatcherLockAtDpcLevel(); 00546 } 00547 00548 /* Wait complete */ 00549 KiReleaseDispatcherLock(Thread->WaitIrql); 00550 return WaitStatus; 00551 00552 DontWait: 00553 /* Release dispatcher lock but maintain high IRQL */ 00554 KiReleaseDispatcherLockFromDpcLevel(); 00555 00556 /* Adjust the Quantum and return the wait status */ 00557 KiAdjustQuantumThread(Thread); 00558 return WaitStatus; 00559 } 00560 00561 /* 00562 * @implemented 00563 */ 00564 NTSTATUS 00565 NTAPI 00566 KeWaitForMultipleObjects(IN ULONG Count, 00567 IN PVOID Object[], 00568 IN WAIT_TYPE WaitType, 00569 IN KWAIT_REASON WaitReason, 00570 IN KPROCESSOR_MODE WaitMode, 00571 IN BOOLEAN Alertable, 00572 IN PLARGE_INTEGER Timeout OPTIONAL, 00573 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL) 00574 { 00575 PKMUTANT CurrentObject; 00576 PKWAIT_BLOCK WaitBlock; 00577 PKTHREAD Thread = KeGetCurrentThread(); 00578 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; 00579 PKTIMER Timer = &Thread->Timer; 00580 NTSTATUS WaitStatus = STATUS_SUCCESS; 00581 BOOLEAN Swappable; 00582 PLARGE_INTEGER OriginalDueTime = Timeout; 00583 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime; 00584 ULONG Index, Hand = 0; 00585 00586 /* Make sure the Wait Count is valid */ 00587 if (!WaitBlockArray) 00588 { 00589 /* Check in regards to the Thread Object Limit */ 00590 if (Count > THREAD_WAIT_OBJECTS) 00591 { 00592 /* Bugcheck */ 00593 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); 00594 } 00595 00596 /* Use the Thread's Wait Block */ 00597 WaitBlockArray = &Thread->WaitBlock[0]; 00598 } 00599 else 00600 { 00601 /* Using our own Block Array, so check with the System Object Limit */ 00602 if (Count > MAXIMUM_WAIT_OBJECTS) 00603 { 00604 /* Bugcheck */ 00605 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); 00606 } 00607 } 00608 00609 /* Sanity check */ 00610 ASSERT(Count != 0); 00611 00612 /* Check if the lock is already held */ 00613 if (!Thread->WaitNext) goto WaitStart; 00614 00615 /* Otherwise, we already have the lock, so initialize the wait */ 00616 Thread->WaitNext = FALSE; 00617 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */ 00618 /* uses (and modifies some of) the following local */ 00619 /* variables: */ 00620 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */ 00621 /* If it looks like this code doesn't actually wait for any objects */ 00622 /* at all, it's because the setup is done by that macro. */ 00623 KxMultiThreadWait(); 00624 00625 /* Start wait loop */ 00626 for (;;) 00627 { 00628 /* Disable pre-emption */ 00629 Thread->Preempted = FALSE; 00630 00631 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 00632 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 00633 (Thread->WaitIrql < APC_LEVEL)) 00634 { 00635 /* Unlock the dispatcher */ 00636 KiReleaseDispatcherLock(Thread->WaitIrql); 00637 } 00638 else 00639 { 00640 /* Check what kind of wait this is */ 00641 Index = 0; 00642 if (WaitType == WaitAny) 00643 { 00644 /* Loop blocks */ 00645 do 00646 { 00647 /* Get the Current Object */ 00648 CurrentObject = (PKMUTANT)Object[Index]; 00649 ASSERT(CurrentObject->Header.Type != QueueObject); 00650 00651 /* Check if the Object is a mutant */ 00652 if (CurrentObject->Header.Type == MutantObject) 00653 { 00654 /* Check if it's signaled */ 00655 if ((CurrentObject->Header.SignalState > 0) || 00656 (Thread == CurrentObject->OwnerThread)) 00657 { 00658 /* This is a Wait Any, so unwait this and exit */ 00659 if (CurrentObject->Header.SignalState != 00660 (LONG)MINLONG) 00661 { 00662 /* Normal signal state, unwait it and return */ 00663 KiSatisfyMutantWait(CurrentObject, Thread); 00664 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index; 00665 goto DontWait; 00666 } 00667 else 00668 { 00669 /* Raise an exception (see wasm.ru) */ 00670 KiReleaseDispatcherLock(Thread->WaitIrql); 00671 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 00672 } 00673 } 00674 } 00675 else if (CurrentObject->Header.SignalState > 0) 00676 { 00677 /* Another signaled object, unwait and return */ 00678 KiSatisfyNonMutantWait(CurrentObject); 00679 WaitStatus = Index; 00680 goto DontWait; 00681 } 00682 00683 /* Go to the next block */ 00684 Index++; 00685 } while (Index < Count); 00686 } 00687 else 00688 { 00689 /* Loop blocks */ 00690 do 00691 { 00692 /* Get the Current Object */ 00693 CurrentObject = (PKMUTANT)Object[Index]; 00694 ASSERT(CurrentObject->Header.Type != QueueObject); 00695 00696 /* Check if we're dealing with a mutant again */ 00697 if (CurrentObject->Header.Type == MutantObject) 00698 { 00699 /* Check if it has an invalid count */ 00700 if ((Thread == CurrentObject->OwnerThread) && 00701 (CurrentObject->Header.SignalState == (LONG)MINLONG)) 00702 { 00703 /* Raise an exception */ 00704 KiReleaseDispatcherLock(Thread->WaitIrql); 00705 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 00706 } 00707 else if ((CurrentObject->Header.SignalState <= 0) && 00708 (Thread != CurrentObject->OwnerThread)) 00709 { 00710 /* We don't own it, can't satisfy the wait */ 00711 break; 00712 } 00713 } 00714 else if (CurrentObject->Header.SignalState <= 0) 00715 { 00716 /* Not signaled, can't satisfy */ 00717 break; 00718 } 00719 00720 /* Go to the next block */ 00721 Index++; 00722 } while (Index < Count); 00723 00724 /* Check if we've went through all the objects */ 00725 if (Index == Count) 00726 { 00727 /* Loop wait blocks */ 00728 WaitBlock = WaitBlockArray; 00729 do 00730 { 00731 /* Get the object and satisfy it */ 00732 CurrentObject = (PKMUTANT)WaitBlock->Object; 00733 KiSatisfyObjectWait(CurrentObject, Thread); 00734 00735 /* Go to the next block */ 00736 WaitBlock = WaitBlock->NextWaitBlock; 00737 } while(WaitBlock != WaitBlockArray); 00738 00739 /* Set the wait status and get out */ 00740 WaitStatus = (NTSTATUS)Thread->WaitStatus; 00741 goto DontWait; 00742 } 00743 } 00744 00745 /* Make sure we can satisfy the Alertable request */ 00746 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 00747 if (WaitStatus != STATUS_WAIT_0) break; 00748 00749 /* Enable the Timeout Timer if there was any specified */ 00750 if (Timeout) 00751 { 00752 /* Check if the timer expired */ 00753 InterruptTime.QuadPart = KeQueryInterruptTime(); 00754 if ((ULONGLONG)InterruptTime.QuadPart >= 00755 Timer->DueTime.QuadPart) 00756 { 00757 /* It did, so we don't need to wait */ 00758 WaitStatus = STATUS_TIMEOUT; 00759 goto DontWait; 00760 } 00761 00762 /* It didn't, so activate it */ 00763 Timer->Header.Inserted = TRUE; 00764 00765 /* Link the wait blocks */ 00766 WaitBlock->NextWaitBlock = TimerBlock; 00767 } 00768 00769 /* Insert into Object's Wait List*/ 00770 WaitBlock = WaitBlockArray; 00771 do 00772 { 00773 /* Get the Current Object */ 00774 CurrentObject = WaitBlock->Object; 00775 00776 /* Link the Object to this Wait Block */ 00777 InsertTailList(&CurrentObject->Header.WaitListHead, 00778 &WaitBlock->WaitListEntry); 00779 00780 /* Move to the next Wait Block */ 00781 WaitBlock = WaitBlock->NextWaitBlock; 00782 } while (WaitBlock != WaitBlockArray); 00783 00784 /* Handle Kernel Queues */ 00785 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 00786 00787 /* Setup the wait information */ 00788 Thread->State = Waiting; 00789 00790 /* Add the thread to the wait list */ 00791 KiAddThreadToWaitList(Thread, Swappable); 00792 00793 /* Activate thread swap */ 00794 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 00795 KiSetThreadSwapBusy(Thread); 00796 00797 /* Check if we have a timer */ 00798 if (Timeout) 00799 { 00800 /* Insert it */ 00801 KxInsertTimer(Timer, Hand); 00802 } 00803 else 00804 { 00805 /* Otherwise, unlock the dispatcher */ 00806 KiReleaseDispatcherLockFromDpcLevel(); 00807 } 00808 00809 /* Swap the thread */ 00810 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 00811 00812 /* Check if we were executing an APC */ 00813 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; 00814 00815 /* Check if we had a timeout */ 00816 if (Timeout) 00817 { 00818 /* Recalculate due times */ 00819 Timeout = KiRecalculateDueTime(OriginalDueTime, 00820 &DueTime, 00821 &NewDueTime); 00822 } 00823 } 00824 00825 WaitStart: 00826 /* Setup a new wait */ 00827 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 00828 KxMultiThreadWait(); 00829 KiAcquireDispatcherLockAtDpcLevel(); 00830 } 00831 00832 /* We are done */ 00833 KiReleaseDispatcherLock(Thread->WaitIrql); 00834 return WaitStatus; 00835 00836 DontWait: 00837 /* Release dispatcher lock but maintain high IRQL */ 00838 KiReleaseDispatcherLockFromDpcLevel(); 00839 00840 /* Adjust the Quantum and return the wait status */ 00841 KiAdjustQuantumThread(Thread); 00842 return WaitStatus; 00843 } 00844 00845 NTSTATUS 00846 NTAPI 00847 NtDelayExecution(IN BOOLEAN Alertable, 00848 IN PLARGE_INTEGER DelayInterval) 00849 { 00850 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 00851 LARGE_INTEGER SafeInterval; 00852 NTSTATUS Status; 00853 00854 /* Check the previous mode */ 00855 if (PreviousMode != KernelMode) 00856 { 00857 /* Enter SEH for probing */ 00858 _SEH2_TRY 00859 { 00860 /* Probe and capture the time out */ 00861 SafeInterval = ProbeForReadLargeInteger(DelayInterval); 00862 DelayInterval = &SafeInterval; 00863 } 00864 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 00865 { 00866 /* Return the exception code */ 00867 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 00868 } 00869 _SEH2_END; 00870 } 00871 00872 /* Call the Kernel Function */ 00873 Status = KeDelayExecutionThread(PreviousMode, 00874 Alertable, 00875 DelayInterval); 00876 00877 /* Return Status */ 00878 return Status; 00879 } 00880 00881 /* EOF */ Generated on Sun May 27 2012 04:28:08 for ReactOS by
1.7.6.1
|