X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=reactos%2Fntoskrnl%2Fke%2Fwait.c;h=a4737b0b7faf173b06412b938f8624e14d870db1;hp=8970ca72808877ca07b58bd915382a930da7930c;hb=cbdf28bb6ace5e3faec494c5743371d7288f191c;hpb=a04cac242c52fd9ac39cf4318d605c61a7e9defb diff --git a/reactos/ntoskrnl/ke/wait.c b/reactos/ntoskrnl/ke/wait.c index 8970ca72808..a4737b0b7fa 100644 --- a/reactos/ntoskrnl/ke/wait.c +++ b/reactos/ntoskrnl/ke/wait.c @@ -11,32 +11,10 @@ #include #define NDEBUG -#include +#include /* PRIVATE FUNCTIONS *********************************************************/ -VOID -FASTCALL -KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock) -{ - PKWAIT_BLOCK WaitBlock = FirstBlock; - PKTHREAD WaitThread = WaitBlock->Thread; - - /* Loop through all the Wait Blocks, and wake each Object */ - do - { - /* Make sure it hasn't timed out */ - if (WaitBlock->WaitKey != STATUS_TIMEOUT) - { - /* Wake the Object */ - KiSatisfyObjectWait((PKMUTANT)WaitBlock->Object, WaitThread); - } - - /* Move to the next block */ - WaitBlock = WaitBlock->NextWaitBlock; - } while (WaitBlock != FirstBlock); -} - VOID FASTCALL KiWaitTest(IN PVOID ObjectPointer, @@ -99,13 +77,7 @@ KiUnlinkThread(IN PKTHREAD Thread, /* Check if there's a Thread Timer */ Timer = &Thread->Timer; - if (Timer->Header.Inserted) - { - /* Remove the timer */ - Timer->Header.Inserted = FALSE; - RemoveEntryList(&Timer->TimerListEntry); - //KiRemoveTimer(Timer); - } + if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer); /* Increment the Queue's active threads */ if (Thread->Queue) Thread->Queue->CurrentCount++; @@ -115,7 +87,7 @@ KiUnlinkThread(IN PKTHREAD Thread, VOID FASTCALL KiUnwaitThread(IN PKTHREAD Thread, - IN NTSTATUS WaitStatus, + IN LONG_PTR WaitStatus, IN KPRIORITY Increment) { /* Unlink the thread */ @@ -138,13 +110,83 @@ KiAcquireFastMutex(IN PFAST_MUTEX FastMutex) FastMutex->Contention++; /* Wait for the event */ - KeWaitForSingleObject(&FastMutex->Gate, + KeWaitForSingleObject(&FastMutex->Event, WrMutex, KernelMode, FALSE, NULL); } +VOID +FASTCALL +KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex) +{ + ULONG BitsToRemove, BitsToAdd; + LONG OldValue, NewValue; + + /* We depend on these bits being just right */ + C_ASSERT((GM_LOCK_WAITER_WOKEN * 2) == GM_LOCK_WAITER_INC); + + /* Increase the contention count */ + GuardedMutex->Contention++; + + /* Start by unlocking the Guarded Mutex */ + BitsToRemove = GM_LOCK_BIT; + BitsToAdd = GM_LOCK_WAITER_INC; + + /* Start change loop */ + for (;;) + { + /* Loop sanity checks */ + ASSERT((BitsToRemove == GM_LOCK_BIT) || + (BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN))); + ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) || + (BitsToAdd == GM_LOCK_WAITER_WOKEN)); + + /* Get the Count Bits */ + OldValue = GuardedMutex->Count; + + /* Start internal bit change loop */ + for (;;) + { + /* Check if the Guarded Mutex is locked */ + if (OldValue & GM_LOCK_BIT) + { + /* Sanity check */ + ASSERT((BitsToRemove == GM_LOCK_BIT) || + ((OldValue & GM_LOCK_WAITER_WOKEN) != 0)); + + /* Unlock it by removing the Lock Bit */ + NewValue = OldValue ^ BitsToRemove; + NewValue = InterlockedCompareExchange(&GuardedMutex->Count, + NewValue, + OldValue); + if (NewValue == OldValue) return; + } + else + { + /* The Guarded Mutex isn't locked, so simply set the bits */ + NewValue = OldValue + BitsToAdd; + NewValue = InterlockedCompareExchange(&GuardedMutex->Count, + NewValue, + OldValue); + if (NewValue == OldValue) break; + } + + /* Old value changed, loop again */ + OldValue = NewValue; + } + + /* Now we have to wait for it */ + KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode); + ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0); + + /* Ok, the wait is done, so set the new bits */ + BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN; + BitsToAdd = GM_LOCK_WAITER_WOKEN; + } +} + // // This routine exits the dispatcher after a compatible operation and // swaps the context to the next scheduled thread on the current CPU if @@ -161,7 +203,7 @@ KiExitDispatcher(IN KIRQL OldIrql) BOOLEAN PendingApc; /* Make sure we're at synchronization level */ - ASSERT_IRQL_EQUAL(SYNCH_LEVEL); + ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL); /* Check if we have deferred threads */ KiCheckDeferredReadyList(Prcb); @@ -234,89 +276,123 @@ KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode, IN BOOLEAN Alertable, IN PLARGE_INTEGER Interval OPTIONAL) { - PKTIMER ThreadTimer; + PKTIMER Timer; + PKWAIT_BLOCK TimerBlock; PKTHREAD Thread = KeGetCurrentThread(); NTSTATUS WaitStatus; BOOLEAN Swappable; - PLARGE_INTEGER OriginalDueTime = Interval; - LARGE_INTEGER DueTime, NewDueTime; + PLARGE_INTEGER OriginalDueTime; + LARGE_INTEGER DueTime, NewDueTime, InterruptTime; + ULONG Hand = 0; - /* Check if the lock is already held */ - if (Thread->WaitNext) - { - /* Lock is held, disable Wait Next */ - Thread->WaitNext = FALSE; - Swappable = KxDelayThreadWait(Thread, Alertable, WaitMode); - } - else + /* If this is a user-mode wait of 0 seconds, yield execution */ + if (!(Interval->QuadPart) && (WaitMode != KernelMode)) { - /* Lock not held, acquire it */ -WaitStart: - Thread->WaitIrql = KiAcquireDispatcherLock(); - Swappable = KxDelayThreadWait(Thread, Alertable, WaitMode); + /* Make sure the wait isn't alertable or interrupting an APC */ + if (!(Alertable) && !(Thread->ApcState.UserApcPending)) + { + /* Yield execution */ + NtYieldExecution(); + } } - /* Check if a kernel APC is pending and we're below APC_LEVEL */ - if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && - (Thread->WaitIrql < APC_LEVEL)) - { - /* Unlock the dispatcher */ - KiReleaseDispatcherLock(Thread->WaitIrql); - goto WaitStart; - } + /* Setup the original time and timer/wait blocks */ + OriginalDueTime = Interval; + Timer = &Thread->Timer; + TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; - /* Check if we have to bail out due to an alerted state */ - WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); - if (WaitStatus != STATUS_WAIT_0) - { - /* Unlock the dispatcher and return */ - KiReleaseDispatcherLock(Thread->WaitIrql); - return WaitStatus; - } + /* Check if the lock is already held */ + if (!Thread->WaitNext) goto WaitStart; - /* Set Timer */ - ThreadTimer = &Thread->Timer; + /* Otherwise, we already have the lock, so initialize the wait */ + Thread->WaitNext = FALSE; + KxDelayThreadWait(); - /* Insert the Timer into the Timer Lists and enable it */ - if (!KiInsertTimer(ThreadTimer, *Interval)) + /* Start wait loop */ + for (;;) { - /* FIXME: We should find a new ready thread */ - KiReleaseDispatcherLock(Thread->WaitIrql); - return STATUS_WAIT_0; - } + /* Disable pre-emption */ + Thread->Preempted = FALSE; + + /* Check if a kernel APC is pending and we're below APC_LEVEL */ + if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && + (Thread->WaitIrql < APC_LEVEL)) + { + /* Unlock the dispatcher */ + KiReleaseDispatcherLock(Thread->WaitIrql); + } + else + { + /* Check if we have to bail out due to an alerted state */ + WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); + if (WaitStatus != STATUS_WAIT_0) break; - /* Save due time */ - DueTime.QuadPart = ThreadTimer->DueTime.QuadPart; + /* Check if the timer expired */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart) + { + /* It did, so we don't need to wait */ + goto NoWait; + } - /* Handle Kernel Queues */ - if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); + /* It didn't, so activate it */ + Timer->Header.Inserted = TRUE; - /* Setup the wait information */ - Thread->State = Waiting; + /* Handle Kernel Queues */ + if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); - /* Add the thread to the wait list */ - KiAddThreadToWaitList(Thread, Swappable); + /* Setup the wait information */ + Thread->State = Waiting; - /* Swap the thread */ - ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); - KiSetThreadSwapBusy(Thread); - WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb()); + /* Add the thread to the wait list */ + KiAddThreadToWaitList(Thread, Swappable); - /* Check if we were executing an APC or if we timed out */ - if (WaitStatus == STATUS_KERNEL_APC) - { - /* Recalculate due times */ - Interval = KiRecalculateDueTime(OriginalDueTime, - &DueTime, - &NewDueTime); - goto WaitStart; - } + /* Insert the timer and swap the thread */ + ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); + KiSetThreadSwapBusy(Thread); + KxInsertTimer(Timer, Hand); + WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb()); + + /* Check if were swapped ok */ + if (WaitStatus != STATUS_KERNEL_APC) + { + /* This is a good thing */ + if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS; + + /* Return Status */ + return WaitStatus; + } - /* This is a good thing */ - if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS; + /* Recalculate due times */ + Interval = KiRecalculateDueTime(OriginalDueTime, + &DueTime, + &NewDueTime); + } + +WaitStart: + /* Setup a new wait */ + Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); + KxDelayThreadWait(); + KiAcquireDispatcherLockAtDpcLevel(); + } - /* Return Status */ + /* We're done! */ + KiReleaseDispatcherLock(Thread->WaitIrql); return WaitStatus; + +NoWait: + /* There was nothing to wait for. Did we have a wait interval? */ + if (!Interval->QuadPart) + { + /* Unlock the dispatcher and do a yield */ + KiReleaseDispatcherLock(Thread->WaitIrql); + return NtYieldExecution(); + } + + /* Unlock the dispatcher and adjust the quantum for a no-wait */ + KiReleaseDispatcherLockFromDpcLevel(); + KiAdjustQuantumThread(Thread); + return STATUS_SUCCESS; } /* @@ -330,157 +406,147 @@ KeWaitForSingleObject(IN PVOID Object, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL) { - PKMUTANT CurrentObject; - PKWAIT_BLOCK WaitBlock; - PKTIMER ThreadTimer; PKTHREAD Thread = KeGetCurrentThread(); + PKMUTANT CurrentObject = (PKMUTANT)Object; + PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0]; + PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; + PKTIMER Timer = &Thread->Timer; NTSTATUS WaitStatus; BOOLEAN Swappable; - LARGE_INTEGER DueTime, NewDueTime; + LARGE_INTEGER DueTime, NewDueTime, InterruptTime; PLARGE_INTEGER OriginalDueTime = Timeout; - - /* Get wait block */ - WaitBlock = &Thread->WaitBlock[0]; + ULONG Hand = 0; /* Check if the lock is already held */ - if (Thread->WaitNext) - { - /* Lock is held, disable Wait Next */ - Thread->WaitNext = FALSE; - Swappable = KxSingleThreadWait(Thread, - WaitBlock, - Object, - Timeout, - Alertable, - WaitReason, - WaitMode); - } - else - { -StartWait: - /* Lock not held, acquire it */ - Thread->WaitIrql = KiAcquireDispatcherLock(); - Swappable = KxSingleThreadWait(Thread, - WaitBlock, - Object, - Timeout, - WaitReason, - WaitMode, - Alertable); - } + if (!Thread->WaitNext) goto WaitStart; - /* Check if a kernel APC is pending and we're below APC_LEVEL */ - if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && - (Thread->WaitIrql < APC_LEVEL)) - { - /* Unlock the dispatcher and wait again */ - KiReleaseDispatcherLock(Thread->WaitIrql); - goto StartWait; - } - - /* Get the Current Object */ - CurrentObject = (PKMUTANT)Object; - ASSERT(CurrentObject->Header.Type != QueueObject); + /* Otherwise, we already have the lock, so initialize the wait */ + Thread->WaitNext = FALSE; + KxSingleThreadWait(); - /* Check if it's a mutant */ - if (CurrentObject->Header.Type == MutantObject) + /* Start wait loop */ + for (;;) { - /* Check its signal state or if we own it */ - if ((CurrentObject->Header.SignalState > 0) || - (Thread == CurrentObject->OwnerThread)) + /* Disable pre-emption */ + Thread->Preempted = FALSE; + + /* Check if a kernel APC is pending and we're below APC_LEVEL */ + if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && + (Thread->WaitIrql < APC_LEVEL)) { - /* Just unwait this guy and exit */ - if (CurrentObject->Header.SignalState != (LONG)MINLONG) + /* Unlock the dispatcher */ + KiReleaseDispatcherLock(Thread->WaitIrql); + } + else + { + /* Sanity check */ + ASSERT(CurrentObject->Header.Type != QueueObject); + + /* Check if it's a mutant */ + if (CurrentObject->Header.Type == MutantObject) + { + /* Check its signal state or if we own it */ + if ((CurrentObject->Header.SignalState > 0) || + (Thread == CurrentObject->OwnerThread)) + { + /* Just unwait this guy and exit */ + if (CurrentObject->Header.SignalState != (LONG)MINLONG) + { + /* It has a normal signal state. Unwait and return */ + KiSatisfyMutantWait(CurrentObject, Thread); + WaitStatus = Thread->WaitStatus; + goto DontWait; + } + else + { + /* Raise an exception */ + KiReleaseDispatcherLock(Thread->WaitIrql); + ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + } + } + } + else if (CurrentObject->Header.SignalState > 0) { - /* It has a normal signal state. Unwait and return */ - KiSatisfyMutantWait(CurrentObject, Thread); - WaitStatus = Thread->WaitStatus; + /* Another satisfied object */ + KiSatisfyNonMutantWait(CurrentObject); + WaitStatus = STATUS_WAIT_0; goto DontWait; } - else + + /* Make sure we can satisfy the Alertable request */ + WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); + if (WaitStatus != STATUS_WAIT_0) break; + + /* Enable the Timeout Timer if there was any specified */ + if (Timeout) { - /* Raise an exception */ - KiReleaseDispatcherLock(Thread->WaitIrql); - ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); - } - } - } - else if (CurrentObject->Header.SignalState > 0) - { - /* Another satisfied object */ - KiSatisfyNonMutantWait(CurrentObject, Thread); - WaitStatus = STATUS_WAIT_0; - goto DontWait; - } + /* Check if the timer expired */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + if ((ULONGLONG)InterruptTime.QuadPart >= + Timer->DueTime.QuadPart) + { + /* It did, so we don't need to wait */ + WaitStatus = STATUS_TIMEOUT; + goto DontWait; + } - /* Make sure we can satisfy the Alertable request */ - WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); - if (WaitStatus != STATUS_WAIT_0) - { - /* Unlock the dispatcher and return */ - KiReleaseDispatcherLock(Thread->WaitIrql); - return WaitStatus; - } + /* It didn't, so activate it */ + Timer->Header.Inserted = TRUE; + } - /* Enable the Timeout Timer if there was any specified */ - if (Timeout) - { - /* Fail if the timeout interval is actually 0 */ - if (!Timeout->QuadPart) - { - /* Return a timeout */ - WaitStatus = STATUS_TIMEOUT; - goto DontWait; - } + /* Link the Object to this Wait Block */ + InsertTailList(&CurrentObject->Header.WaitListHead, + &WaitBlock->WaitListEntry); - /* Insert the Timer into the Timer Lists and enable it */ - ThreadTimer = &Thread->Timer; - if (!KiInsertTimer(ThreadTimer, *Timeout)) - { - /* Return a timeout if we couldn't insert the timer */ - WaitStatus = STATUS_TIMEOUT; - goto DontWait; - } + /* Handle Kernel Queues */ + if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); - /* Set the current due time */ - DueTime.QuadPart = ThreadTimer->DueTime.QuadPart; - } + /* Setup the wait information */ + Thread->State = Waiting; - /* Link the Object to this Wait Block */ - InsertTailList(&CurrentObject->Header.WaitListHead, - &WaitBlock->WaitListEntry); + /* Add the thread to the wait list */ + KiAddThreadToWaitList(Thread, Swappable); - /* Handle Kernel Queues */ - if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); + /* Activate thread swap */ + ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); + KiSetThreadSwapBusy(Thread); - /* Setup the wait information */ - Thread->State = Waiting; + /* Check if we have a timer */ + if (Timeout) + { + /* Insert it */ + KxInsertTimer(Timer, Hand); + } + else + { + /* Otherwise, unlock the dispatcher */ + KiReleaseDispatcherLockFromDpcLevel(); + } - /* Add the thread to the wait list */ - KiAddThreadToWaitList(Thread, Swappable); + /* Do the actual swap */ + WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb()); - /* Swap the thread */ - ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); - KiSetThreadSwapBusy(Thread); - WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb()); + /* Check if we were executing an APC */ + if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; - /* Check if we were executing an APC */ - if (WaitStatus == STATUS_KERNEL_APC) - { - /* Check if we had a timeout */ - if (Timeout) - { - /* Recalculate due times */ - Timeout = KiRecalculateDueTime(OriginalDueTime, - &DueTime, - &NewDueTime); + /* Check if we had a timeout */ + if (Timeout) + { + /* Recalculate due times */ + Timeout = KiRecalculateDueTime(OriginalDueTime, + &DueTime, + &NewDueTime); + } } - - /* Wait again */ - goto StartWait; +WaitStart: + /* Setup a new wait */ + Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); + KxSingleThreadWait(); + KiAcquireDispatcherLockAtDpcLevel(); } /* Wait complete */ + KiReleaseDispatcherLock(Thread->WaitIrql); return WaitStatus; DontWait: @@ -508,15 +574,14 @@ KeWaitForMultipleObjects(IN ULONG Count, { PKMUTANT CurrentObject; PKWAIT_BLOCK WaitBlock; - PKWAIT_BLOCK TimerWaitBlock; - PKTIMER ThreadTimer; PKTHREAD Thread = KeGetCurrentThread(); - ULONG AllObjectsSignaled; - ULONG WaitIndex; + PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; + PKTIMER Timer = &Thread->Timer; NTSTATUS WaitStatus = STATUS_SUCCESS; BOOLEAN Swappable; PLARGE_INTEGER OriginalDueTime = Timeout; - LARGE_INTEGER DueTime, NewDueTime; + LARGE_INTEGER DueTime, NewDueTime, InterruptTime; + ULONG Index, Hand = 0; /* Make sure the Wait Count is valid */ if (!WaitBlockArray) @@ -525,7 +590,7 @@ KeWaitForMultipleObjects(IN ULONG Count, if (Count > THREAD_WAIT_OBJECTS) { /* Bugcheck */ - KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); + KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); } /* Use the Thread's Wait Block */ @@ -537,7 +602,7 @@ KeWaitForMultipleObjects(IN ULONG Count, if (Count > MAXIMUM_WAIT_OBJECTS) { /* Bugcheck */ - KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); + KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); } } @@ -545,219 +610,227 @@ KeWaitForMultipleObjects(IN ULONG Count, ASSERT(Count != 0); /* Check if the lock is already held */ - if (Thread->WaitNext) - { - /* Lock is held, disable Wait Next */ - Thread->WaitNext = FALSE; - } - else - { - /* Lock not held, acquire it */ -StartWait: - Thread->WaitIrql = KiAcquireDispatcherLock(); - } - - /* Prepare for the wait */ - Swappable = KxMultiThreadWait(Thread, - WaitBlockArray, - Alertable, - WaitReason, - WaitMode); - - /* Check if a kernel APC is pending and we're below APC_LEVEL */ - if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && - (Thread->WaitIrql < APC_LEVEL)) - { - /* Unlock the dispatcher */ - KiReleaseDispatcherLock(Thread->WaitIrql); - goto StartWait; - } - - /* Append wait block to the KTHREAD wait block list */ - WaitBlock = WaitBlockArray; - - /* Check if the wait is (already) satisfied */ - AllObjectsSignaled = TRUE; - - /* First, we'll try to satisfy the wait directly */ - for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) - { - /* Get the Current Object */ - CurrentObject = (PKMUTANT)Object[WaitIndex]; - ASSERT(CurrentObject->Header.Type != QueueObject); - - /* Check the type of wait */ - if (WaitType == WaitAny) + if (!Thread->WaitNext) goto WaitStart; + + /* Otherwise, we already have the lock, so initialize the wait */ + Thread->WaitNext = FALSE; + /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */ + /* uses (and modifies some of) the following local */ + /* variables: */ + /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */ + /* If it looks like this code doesn't actually wait for any objects */ + /* at all, it's because the setup is done by that macro. */ + KxMultiThreadWait(); + + /* Start wait loop */ + for (;;) + { + /* Disable pre-emption */ + Thread->Preempted = FALSE; + + /* Check if a kernel APC is pending and we're below APC_LEVEL */ + if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && + (Thread->WaitIrql < APC_LEVEL)) { - /* Check if the Object is a mutant */ - if (CurrentObject->Header.Type == MutantObject) + /* Unlock the dispatcher */ + KiReleaseDispatcherLock(Thread->WaitIrql); + } + else + { + /* Check what kind of wait this is */ + Index = 0; + if (WaitType == WaitAny) { - /* Check if it's signaled */ - if ((CurrentObject->Header.SignalState > 0) || - (Thread == CurrentObject->OwnerThread)) + /* Loop blocks */ + do { - /* This is a Wait Any, so unwait this and exit */ - if (CurrentObject->Header.SignalState != - (LONG)MINLONG) + /* Get the Current Object */ + CurrentObject = (PKMUTANT)Object[Index]; + ASSERT(CurrentObject->Header.Type != QueueObject); + + /* Check if the Object is a mutant */ + if (CurrentObject->Header.Type == MutantObject) { - /* Normal signal state, unwait it and return */ - KiSatisfyMutantWait(CurrentObject, Thread); - WaitStatus = Thread->WaitStatus | WaitIndex; - goto DontWait; + /* Check if it's signaled */ + if ((CurrentObject->Header.SignalState > 0) || + (Thread == CurrentObject->OwnerThread)) + { + /* This is a Wait Any, so unwait this and exit */ + if (CurrentObject->Header.SignalState != + (LONG)MINLONG) + { + /* Normal signal state, unwait it and return */ + KiSatisfyMutantWait(CurrentObject, Thread); + WaitStatus = Thread->WaitStatus | Index; + goto DontWait; + } + else + { + /* Raise an exception (see wasm.ru) */ + KiReleaseDispatcherLock(Thread->WaitIrql); + ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + } + } } - else + else if (CurrentObject->Header.SignalState > 0) { - /* Raise an exception (see wasm.ru) */ - KiReleaseDispatcherLock(Thread->WaitIrql); - ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + /* Another signaled object, unwait and return */ + KiSatisfyNonMutantWait(CurrentObject); + WaitStatus = Index; + goto DontWait; } - } - } - else if (CurrentObject->Header.SignalState > 0) - { - /* Another signaled object, unwait and return */ - KiSatisfyNonMutantWait(CurrentObject, Thread); - WaitStatus = WaitIndex; - goto DontWait; + + /* Go to the next block */ + Index++; + } while (Index < Count); } - } - else - { - /* Check if we're dealing with a mutant again */ - if (CurrentObject->Header.Type == MutantObject) + else { - /* Check if it has an invalid count */ - if ((Thread == CurrentObject->OwnerThread) && - (CurrentObject->Header.SignalState == MINLONG)) + /* Loop blocks */ + do { - /* Raise an exception */ - KiReleaseDispatcherLock(Thread->WaitIrql); - ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); - } - else if ((CurrentObject->Header.SignalState <= 0) && - (Thread != CurrentObject->OwnerThread)) - { - /* We don't own it, can't satisfy the wait */ - AllObjectsSignaled = FALSE; - } - } - else if (CurrentObject->Header.SignalState <= 0) - { - /* Not signaled, can't satisfy */ - AllObjectsSignaled = FALSE; - } - } + /* Get the Current Object */ + CurrentObject = (PKMUTANT)Object[Index]; + ASSERT(CurrentObject->Header.Type != QueueObject); - /* Set up a Wait Block for this Object */ - WaitBlock = &WaitBlockArray[WaitIndex]; - WaitBlock->Object = CurrentObject; - WaitBlock->Thread = Thread; - WaitBlock->WaitKey = (USHORT)WaitIndex; - WaitBlock->WaitType = (UCHAR)WaitType; - WaitBlock->NextWaitBlock = &WaitBlockArray[WaitIndex + 1]; - } + /* Check if we're dealing with a mutant again */ + if (CurrentObject->Header.Type == MutantObject) + { + /* Check if it has an invalid count */ + if ((Thread == CurrentObject->OwnerThread) && + (CurrentObject->Header.SignalState == (LONG)MINLONG)) + { + /* Raise an exception */ + KiReleaseDispatcherLock(Thread->WaitIrql); + ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + } + else if ((CurrentObject->Header.SignalState <= 0) && + (Thread != CurrentObject->OwnerThread)) + { + /* We don't own it, can't satisfy the wait */ + break; + } + } + else if (CurrentObject->Header.SignalState <= 0) + { + /* Not signaled, can't satisfy */ + break; + } - /* Check if this is a Wait All and all the objects are signaled */ - if ((WaitType == WaitAll) && (AllObjectsSignaled)) - { - /* Return to the Root Wait Block */ - WaitBlock->NextWaitBlock = &WaitBlockArray[0]; + /* Go to the next block */ + Index++; + } while (Index < Count); - /* Satisfy their Waits and return to the caller */ - KiWaitSatisfyAll(WaitBlock); - WaitStatus = Thread->WaitStatus; - goto DontWait; - } + /* Check if we've went through all the objects */ + if (Index == Count) + { + /* Loop wait blocks */ + WaitBlock = WaitBlockArray; + do + { + /* Get the object and satisfy it */ + CurrentObject = (PKMUTANT)WaitBlock->Object; + KiSatisfyObjectWait(CurrentObject, Thread); - /* Make sure we can satisfy the Alertable request */ - WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); - if (WaitStatus != STATUS_WAIT_0) - { - /* Unlock the dispatcher and return */ - KiReleaseDispatcherLock(Thread->WaitIrql); - return WaitStatus; - } + /* Go to the next block */ + WaitBlock = WaitBlock->NextWaitBlock; + } while(WaitBlock != WaitBlockArray); - /* Enable the Timeout Timer if there was any specified */ - if (Timeout) - { - /* Make sure the timeout interval isn't actually 0 */ - if (!Timeout->QuadPart) - { - /* Return a timeout */ - WaitStatus = STATUS_TIMEOUT; - goto DontWait; - } + /* Set the wait status and get out */ + WaitStatus = Thread->WaitStatus; + goto DontWait; + } + } - /* Link timer wait block */ - TimerWaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; - WaitBlock->NextWaitBlock = TimerWaitBlock; + /* Make sure we can satisfy the Alertable request */ + WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); + if (WaitStatus != STATUS_WAIT_0) break; - /* Use this the timer block for linking below */ - WaitBlock = TimerWaitBlock; + /* Enable the Timeout Timer if there was any specified */ + if (Timeout) + { + /* Check if the timer expired */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + if ((ULONGLONG)InterruptTime.QuadPart >= + Timer->DueTime.QuadPart) + { + /* It did, so we don't need to wait */ + WaitStatus = STATUS_TIMEOUT; + goto DontWait; + } - /* Insert the Timer into the Timer Lists and enable it */ - ThreadTimer = &Thread->Timer; - if (!KiInsertTimer(ThreadTimer, *Timeout)) - { - /* Return a timeout if we couldn't insert the timer */ - WaitStatus = STATUS_TIMEOUT; - goto DontWait; - } + /* It didn't, so activate it */ + Timer->Header.Inserted = TRUE; - /* Set the current due time */ - DueTime.QuadPart = ThreadTimer->DueTime.QuadPart; - } + /* Link the wait blocks */ + WaitBlock->NextWaitBlock = TimerBlock; + } - /* Link to the Root Wait Block */ - WaitBlock->NextWaitBlock = &WaitBlockArray[0]; + /* Insert into Object's Wait List*/ + WaitBlock = WaitBlockArray; + do + { + /* Get the Current Object */ + CurrentObject = WaitBlock->Object; - /* Insert into Object's Wait List*/ - WaitBlock = &WaitBlockArray[0]; - do - { - /* Get the Current Object */ - CurrentObject = WaitBlock->Object; + /* Link the Object to this Wait Block */ + InsertTailList(&CurrentObject->Header.WaitListHead, + &WaitBlock->WaitListEntry); - /* Link the Object to this Wait Block */ - InsertTailList(&CurrentObject->Header.WaitListHead, - &WaitBlock->WaitListEntry); + /* Move to the next Wait Block */ + WaitBlock = WaitBlock->NextWaitBlock; + } while (WaitBlock != WaitBlockArray); - /* Move to the next Wait Block */ - WaitBlock = WaitBlock->NextWaitBlock; - } while (WaitBlock != WaitBlockArray); + /* Handle Kernel Queues */ + if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); - /* Handle Kernel Queues */ - if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); + /* Setup the wait information */ + Thread->State = Waiting; - /* Setup the wait information */ - Thread->State = Waiting; + /* Add the thread to the wait list */ + KiAddThreadToWaitList(Thread, Swappable); - /* Add the thread to the wait list */ - KiAddThreadToWaitList(Thread, Swappable); + /* Activate thread swap */ + ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); + KiSetThreadSwapBusy(Thread); - /* Swap the thread */ - ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); - KiSetThreadSwapBusy(Thread); - WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb()); + /* Check if we have a timer */ + if (Timeout) + { + /* Insert it */ + KxInsertTimer(Timer, Hand); + } + else + { + /* Otherwise, unlock the dispatcher */ + KiReleaseDispatcherLockFromDpcLevel(); + } - /* Check if we were executing an APC */ - if (WaitStatus == STATUS_KERNEL_APC) - { - /* Check if we had a timeout */ - if (Timeout) - { - /* Recalculate due times */ - Timeout = KiRecalculateDueTime(OriginalDueTime, - &DueTime, - &NewDueTime); + /* Swap the thread */ + WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb()); + + /* Check if we were executing an APC */ + if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; + + /* Check if we had a timeout */ + if (Timeout) + { + /* Recalculate due times */ + Timeout = KiRecalculateDueTime(OriginalDueTime, + &DueTime, + &NewDueTime); + } } - /* Wait again */ - goto StartWait; +WaitStart: + /* Setup a new wait */ + Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); + KxMultiThreadWait(); + KiAcquireDispatcherLockAtDpcLevel(); } /* We are done */ + KiReleaseDispatcherLock(Thread->WaitIrql); return WaitStatus; DontWait: @@ -776,25 +849,24 @@ NtDelayExecution(IN BOOLEAN Alertable, { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); LARGE_INTEGER SafeInterval; - NTSTATUS Status = STATUS_SUCCESS; + NTSTATUS Status; /* Check the previous mode */ - if(PreviousMode != KernelMode) + if (PreviousMode != KernelMode) { /* Enter SEH for probing */ - _SEH_TRY + _SEH2_TRY { /* Probe and capture the time out */ SafeInterval = ProbeForReadLargeInteger(DelayInterval); DelayInterval = &SafeInterval; } - _SEH_HANDLE + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { - /* Get SEH exception */ - Status = _SEH_GetExceptionCode(); + /* Return the exception code */ + _SEH2_YIELD(return _SEH2_GetExceptionCode()); } - _SEH_END; - if (!NT_SUCCESS(Status)) return Status; + _SEH2_END; } /* Call the Kernel Function */