/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS project
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/wait.c
- * PURPOSE: Manages non-busy waiting
- *
- * PROGRAMMERS: Alex Ionescu - Fixes and optimization.
- * Gunnar Dalsnes - Implementation
+ * PURPOSE: Manages waiting for Dispatcher Objects
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ * Gunnar Dalsnes
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
-
#define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
-/* GLOBALS ******************************************************************/
+/* PRIVATE FUNCTIONS *********************************************************/
-static KSPIN_LOCK DispatcherDatabaseLock;
+VOID
+FASTCALL
+KiWaitTest(IN PVOID ObjectPointer,
+ IN KPRIORITY Increment)
+{
+ PLIST_ENTRY WaitEntry, WaitList;
+ PKWAIT_BLOCK WaitBlock;
+ PKTHREAD WaitThread;
+ PKMUTANT FirstObject = ObjectPointer;
+ NTSTATUS WaitStatus;
-/* Tells us if the Timer or Event is a Syncronization or Notification Object */
-#define TIMER_OR_EVENT_TYPE 0x7L
+ /* Loop the Wait Entries */
+ WaitList = &FirstObject->Header.WaitListHead;
+ WaitEntry = WaitList->Flink;
+ while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList))
+ {
+ /* Get the current wait block */
+ WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
+ WaitThread = WaitBlock->Thread;
+ WaitStatus = STATUS_KERNEL_APC;
-/* One of the Reserved Wait Blocks, this one is for the Thread's Timer */
-#define TIMER_WAIT_BLOCK 0x3L
+ /* Check the current Wait Mode */
+ if (WaitBlock->WaitType == WaitAny)
+ {
+ /* Easy case, satisfy only this wait */
+ WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
+ KiSatisfyObjectWait(FirstObject, WaitThread);
+ }
-/* FUNCTIONS *****************************************************************/
+ /* Now do the rest of the unwait */
+ KiUnwaitThread(WaitThread, WaitStatus, Increment);
+ WaitEntry = WaitList->Flink;
+ }
+}
-BOOLEAN
-inline
+VOID
FASTCALL
-KiCheckAlertability(BOOLEAN Alertable,
- PKTHREAD CurrentThread,
- KPROCESSOR_MODE WaitMode,
- PNTSTATUS Status)
+KiUnlinkThread(IN PKTHREAD Thread,
+ IN NTSTATUS WaitStatus)
{
- /* At this point, we have to do a wait, so make sure we can make the thread Alertable if requested */
- if (Alertable) {
+ PKWAIT_BLOCK WaitBlock;
+ PKTIMER Timer;
- /* If the Thread is Alerted, set the Wait Status accordingly */
- if (CurrentThread->Alerted[(int)WaitMode]) {
+ /* Update wait status */
+ Thread->WaitStatus |= WaitStatus;
- CurrentThread->Alerted[(int)WaitMode] = FALSE;
- DPRINT("Thread was Alerted\n");
- *Status = STATUS_ALERTED;
- return TRUE;
+ /* Remove the Wait Blocks from the list */
+ WaitBlock = Thread->WaitBlockList;
+ do
+ {
+ /* Remove it */
+ RemoveEntryList(&WaitBlock->WaitListEntry);
- /* If there are User APCs Pending, then we can't really be alertable */
- } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) &&
- (WaitMode != KernelMode)) {
+ /* Go to the next one */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ } while (WaitBlock != Thread->WaitBlockList);
- DPRINT("APCs are Pending\n");
- CurrentThread->ApcState.UserApcPending = TRUE;
- *Status = STATUS_USER_APC;
- return TRUE;
- }
+ /* Remove the thread from the wait list! */
+ if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
- /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */
- } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode != KernelMode)) {
- DPRINT("APCs are Pending\n");
- *Status = STATUS_USER_APC;
- return TRUE;
- }
+ /* Check if there's a Thread Timer */
+ Timer = &Thread->Timer;
+ if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
- return FALSE;
+ /* Increment the Queue's active threads */
+ if (Thread->Queue) Thread->Queue->CurrentCount++;
}
-/*
- * @implemented
- *
- * FUNCTION: Puts the current thread into an alertable or nonalertable
- * wait state for a given internal
- * ARGUMENTS:
- * WaitMode = Processor mode in which the caller is waiting
- * Altertable = Specifies if the wait is alertable
- * Interval = Specifies the interval to wait
- * RETURNS: Status
- */
-NTSTATUS
-STDCALL
-KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
- BOOLEAN Alertable,
- PLARGE_INTEGER Interval)
+/* Must be called with the dispatcher lock held */
+VOID
+FASTCALL
+KiUnwaitThread(IN PKTHREAD Thread,
+ IN NTSTATUS WaitStatus,
+ IN KPRIORITY Increment)
{
- PKWAIT_BLOCK TimerWaitBlock;
- PKTIMER ThreadTimer;
- PKTHREAD CurrentThread = KeGetCurrentThread();
- NTSTATUS Status;
+ /* Unlink the thread */
+ KiUnlinkThread(Thread, WaitStatus);
- DPRINT("Entering KeDelayExecutionThread\n");
-
- /* Check if the lock is already held */
- if (CurrentThread->WaitNext) {
+ /* Tell the scheduler do to the increment when it readies the thread */
+ ASSERT(Increment >= 0);
+ Thread->AdjustIncrement = (SCHAR)Increment;
+ Thread->AdjustReason = AdjustUnwait;
- /* Lock is held, disable Wait Next */
- DPRINT("Lock is held\n");
- CurrentThread->WaitNext = FALSE;
+ /* Reschedule the Thread */
+ KiReadyThread(Thread);
+}
- } else {
+VOID
+FASTCALL
+KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
+{
+ /* Increase contention count */
+ FastMutex->Contention++;
+
+ /* Wait for the event */
+ KeWaitForSingleObject(&FastMutex->Gate,
+ WrMutex,
+ KernelMode,
+ FALSE,
+ NULL);
+}
- /* Lock not held, acquire it */
- DPRINT("Lock is not held, acquiring\n");
- CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+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;
}
+}
- /* Use built-in Wait block */
- TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
-
- /* Start Wait Loop */
- do {
-
- /* We are going to wait no matter what (that's the point), so test Alertability */
- if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status))
- break;
-
- /* Set Timer */
- ThreadTimer = &CurrentThread->Timer;
-
- /* Setup the Wait Block */
- CurrentThread->WaitBlockList = TimerWaitBlock;
- TimerWaitBlock->Object = (PVOID)ThreadTimer;
- TimerWaitBlock->Thread = CurrentThread;
- TimerWaitBlock->WaitKey = (USHORT)STATUS_TIMEOUT;
- TimerWaitBlock->WaitType = WaitAny;
- TimerWaitBlock->NextWaitBlock = TimerWaitBlock;
+//
+// This routine exits the dispatcher after a compatible operation and
+// swaps the context to the next scheduled thread on the current CPU if
+// one is available.
+//
+// It does NOT attempt to scan for a new thread to schedule.
+//
+VOID
+FASTCALL
+KiExitDispatcher(IN KIRQL OldIrql)
+{
+ PKPRCB Prcb = KeGetCurrentPrcb();
+ PKTHREAD Thread, NextThread;
+ BOOLEAN PendingApc;
+
+ /* Make sure we're at synchronization level */
+ ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
+
+ /* Check if we have deferred threads */
+ KiCheckDeferredReadyList(Prcb);
+
+ /* Check if we were called at dispatcher level or higher */
+ if (OldIrql >= DISPATCH_LEVEL)
+ {
+ /* Check if we have a thread to schedule, and that no DPC is active */
+ if ((Prcb->NextThread) && !(Prcb->DpcRoutineActive))
+ {
+ /* Request DPC interrupt */
+ HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
+ }
- /* Link the timer to this Wait Block */
- InitializeListHead(&ThreadTimer->Header.WaitListHead);
- InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
+ /* Lower IRQL and exit */
+ goto Quickie;
+ }
- /* Insert the Timer into the Timer Lists and enable it */
- if (!KiInsertTimer(ThreadTimer, *Interval)) {
+ /* Make sure there's a new thread scheduled */
+ if (!Prcb->NextThread) goto Quickie;
- /* FIXME: The timer already expired, we should find a new ready thread */
- Status = STATUS_SUCCESS;
- break;
- }
+ /* Lock the PRCB */
+ KiAcquirePrcbLock(Prcb);
- /* Handle Kernel Queues */
- if (CurrentThread->Queue) {
+ /* Get the next and current threads now */
+ NextThread = Prcb->NextThread;
+ Thread = Prcb->CurrentThread;
- DPRINT("Waking Queue\n");
- KiWakeQueue(CurrentThread->Queue);
- }
+ /* Set current thread's swap busy to true */
+ KiSetThreadSwapBusy(Thread);
- /* Block the Thread */
- DPRINT("Blocking the Thread: %d, %d, %x\n", Alertable, WaitMode, KeGetCurrentThread());
- KiBlockThread(&Status,
- Alertable,
- WaitMode,
- DelayExecution);
+ /* Switch threads in PRCB */
+ Prcb->NextThread = NULL;
+ Prcb->CurrentThread = NextThread;
- /* Check if we were executing an APC or if we timed out */
- if (Status != STATUS_KERNEL_APC) {
+ /* Set thread to running */
+ NextThread->State = Running;
- /* This is a good thing */
- if (Status == STATUS_TIMEOUT) Status = STATUS_SUCCESS;
+ /* Queue it on the ready lists */
+ KxQueueReadyThread(Thread, Prcb);
- /* Return Status */
- return Status;
- }
+ /* Set wait IRQL */
+ Thread->WaitIrql = OldIrql;
- DPRINT("Looping Again\n");
- CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+ /* Swap threads and check if APCs were pending */
+ PendingApc = KiSwapContext(Thread, NextThread);
+ if (PendingApc)
+ {
+ /* Lower only to APC */
+ KeLowerIrql(APC_LEVEL);
- } while (TRUE);
+ /* Deliver APCs */
+ KiDeliverApc(KernelMode, NULL, NULL);
+ ASSERT(OldIrql == PASSIVE_LEVEL);
+ }
- /* Release the Lock, we are done */
- DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n", KeGetCurrentThread(), Status);
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
- return Status;
+ /* Lower IRQl back */
+Quickie:
+ KeLowerIrql(OldIrql);
}
+/* PUBLIC FUNCTIONS **********************************************************/
+
/*
* @implemented
- *
- * FUNCTION: Puts the current thread into a wait state until the
- * given dispatcher object is set to signalled
- * ARGUMENTS:
- * Object = Object to wait on
- * WaitReason = Reason for the wait (debugging aid)
- * WaitMode = Can be KernelMode or UserMode, if UserMode then
- * user-mode APCs can be delivered and the thread's
- * stack can be paged out
- * Altertable = Specifies if the wait is a alertable
- * Timeout = Optional timeout value
- * RETURNS: Status
*/
NTSTATUS
-STDCALL
-KeWaitForSingleObject(PVOID Object,
- KWAIT_REASON WaitReason,
- KPROCESSOR_MODE WaitMode,
- BOOLEAN Alertable,
- PLARGE_INTEGER Timeout)
+NTAPI
+KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Interval OPTIONAL)
{
- PDISPATCHER_HEADER CurrentObject;
- PKWAIT_BLOCK WaitBlock;
- PKWAIT_BLOCK TimerWaitBlock;
- PKTIMER ThreadTimer;
- PKTHREAD CurrentThread = KeGetCurrentThread();
- NTSTATUS Status;
+ PKTIMER Timer;
+ PKWAIT_BLOCK TimerBlock;
+ PKTHREAD Thread = KeGetCurrentThread();
NTSTATUS WaitStatus;
+ BOOLEAN Swappable;
+ PLARGE_INTEGER OriginalDueTime;
+ LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
+ ULONG Hand = 0;
+
+ /* If this is a user-mode wait of 0 seconds, yield execution */
+ if (!(Interval->QuadPart) && (WaitMode != KernelMode))
+ {
+ /* Make sure the wait isn't alertable or interrupting an APC */
+ if (!(Alertable) && !(Thread->ApcState.UserApcPending))
+ {
+ /* Yield execution */
+ NtYieldExecution();
+ }
+ }
- DPRINT("Entering KeWaitForSingleObject\n");
+ /* Setup the original time and timer/wait blocks */
+ OriginalDueTime = Interval;
+ Timer = &Thread->Timer;
+ TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
/* Check if the lock is already held */
- if (CurrentThread->WaitNext) {
+ if (!Thread->WaitNext) goto WaitStart;
+
+ /* Otherwise, we already have the lock, so initialize the wait */
+ Thread->WaitNext = FALSE;
+ KxDelayThreadWait();
+
+ /* 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))
+ {
+ /* 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;
+
+ /* 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;
+ }
- /* Lock is held, disable Wait Next */
- DPRINT("Lock is held\n");
- CurrentThread->WaitNext = FALSE;
+ /* It didn't, so activate it */
+ Timer->Header.Inserted = TRUE;
- } else {
+ /* Handle Kernel Queues */
+ if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
- /* Lock not held, acquire it */
- DPRINT("Lock is not held, acquiring\n");
- CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
- }
+ /* Setup the wait information */
+ Thread->State = Waiting;
- /* Start the actual Loop */
- do {
+ /* Add the thread to the wait list */
+ KiAddThreadToWaitList(Thread, Swappable);
- /* Get the current Wait Status */
- WaitStatus = CurrentThread->WaitStatus;
+ /* Insert the timer and swap the thread */
+ ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+ KiSetThreadSwapBusy(Thread);
+ KxInsertTimer(Timer, Hand);
+ WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
- /* Append wait block to the KTHREAD wait block list */
- CurrentThread->WaitBlockList = WaitBlock = &CurrentThread->WaitBlock[0];
+ /* Check if were swapped ok */
+ if (WaitStatus != STATUS_KERNEL_APC)
+ {
+ /* This is a good thing */
+ if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
- /* Get the Current Object */
- CurrentObject = (PDISPATCHER_HEADER)Object;
+ /* Return Status */
+ return WaitStatus;
+ }
- /* Check if the Object is Signaled */
- if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
+ /* Recalculate due times */
+ Interval = KiRecalculateDueTime(OriginalDueTime,
+ &DueTime,
+ &NewDueTime);
+ }
- /* Just unwait this guy and exit */
- if (CurrentObject->SignalState != (LONG)MINLONG) {
+WaitStart:
+ /* Setup a new wait */
+ Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
+ KxDelayThreadWait();
+ KiAcquireDispatcherLockAtDpcLevel();
+ }
- /* It has a normal signal state, so unwait it and return */
- KiSatisfyObjectWait(CurrentObject, CurrentThread);
- Status = STATUS_WAIT_0;
- goto DontWait;
+ /* 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();
+ }
- } else {
+ /* Unlock the dispatcher and adjust the quantum for a no-wait */
+ KiReleaseDispatcherLockFromDpcLevel();
+ KiAdjustQuantumThread(Thread);
+ return STATUS_SUCCESS;
+}
- /* Is this a Mutant? */
- if (CurrentObject->Type == MutantObject) {
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+KeWaitForSingleObject(IN PVOID Object,
+ IN KWAIT_REASON WaitReason,
+ IN KPROCESSOR_MODE WaitMode,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout OPTIONAL)
+{
+ 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, InterruptTime;
+ PLARGE_INTEGER OriginalDueTime = Timeout;
+ ULONG Hand = 0;
- /* According to wasm.ru, we must raise this exception (tested and true) */
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
- ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
+ /* Check if the lock is already held */
+ if (!Thread->WaitNext) goto WaitStart;
+
+ /* Otherwise, we already have the lock, so initialize the wait */
+ Thread->WaitNext = FALSE;
+ KxSingleThreadWait();
+
+ /* 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))
+ {
+ /* 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);
+ }
}
}
- }
-
- /* Set up the Wait Block */
- WaitBlock->Object = CurrentObject;
- WaitBlock->Thread = CurrentThread;
- WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0);
- WaitBlock->WaitType = WaitAny;
- WaitBlock->NextWaitBlock = WaitBlock;
-
- /* Make sure we can satisfy the Alertable request */
- if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status))
- break;
-
- /* Set the Wait Status */
- CurrentThread->WaitStatus = Status;
-
- /* Enable the Timeout Timer if there was any specified */
- if (Timeout != NULL) {
-
- /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
- if (!Timeout->QuadPart) {
-
- /* Return a timeout */
- Status = STATUS_TIMEOUT;
+ else if (CurrentObject->Header.SignalState > 0)
+ {
+ /* Another satisfied object */
+ KiSatisfyNonMutantWait(CurrentObject);
+ WaitStatus = STATUS_WAIT_0;
goto DontWait;
}
- /* Point to Timer Wait Block and Thread Timer */
- TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
- ThreadTimer = &CurrentThread->Timer;
-
- /* Connect the 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;
+
+ /* 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;
+ }
- /* Set up the Timer Wait Block */
- TimerWaitBlock->Object = (PVOID)ThreadTimer;
- TimerWaitBlock->Thread = CurrentThread;
- TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
- TimerWaitBlock->WaitType = WaitAny;
- TimerWaitBlock->NextWaitBlock = WaitBlock;
+ /* It didn't, so activate it */
+ Timer->Header.Inserted = TRUE;
+ }
- /* Link the timer to this Wait Block */
- InitializeListHead(&ThreadTimer->Header.WaitListHead);
- InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
+ /* Link the Object to this Wait Block */
+ InsertTailList(&CurrentObject->Header.WaitListHead,
+ &WaitBlock->WaitListEntry);
- /* Insert the Timer into the Timer Lists and enable it */
- if (!KiInsertTimer(ThreadTimer, *Timeout)) {
+ /* Handle Kernel Queues */
+ if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
- /* Return a timeout if we couldn't insert the timer for some reason */
- Status = STATUS_TIMEOUT;
- goto DontWait;
- }
- }
+ /* Setup the wait information */
+ Thread->State = Waiting;
- /* Link the Object to this Wait Block */
- InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
+ /* Add the thread to the wait list */
+ KiAddThreadToWaitList(Thread, Swappable);
- /* Handle Kernel Queues */
- if (CurrentThread->Queue) {
+ /* Activate thread swap */
+ ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+ KiSetThreadSwapBusy(Thread);
- DPRINT("Waking Queue\n");
- KiWakeQueue(CurrentThread->Queue);
- }
+ /* Check if we have a timer */
+ if (Timeout)
+ {
+ /* Insert it */
+ KxInsertTimer(Timer, Hand);
+ }
+ else
+ {
+ /* Otherwise, unlock the dispatcher */
+ KiReleaseDispatcherLockFromDpcLevel();
+ }
- /* Block the Thread */
- DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
- KiBlockThread(&Status,
- Alertable,
- WaitMode,
- (UCHAR)WaitReason);
+ /* Do the actual swap */
+ WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
- /* Check if we were executing an APC */
- if (Status != STATUS_KERNEL_APC) {
+ /* Check if we were executing an APC */
+ if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
- /* Return Status */
- return Status;
+ /* Check if we had a timeout */
+ if (Timeout)
+ {
+ /* Recalculate due times */
+ Timeout = KiRecalculateDueTime(OriginalDueTime,
+ &DueTime,
+ &NewDueTime);
+ }
}
+WaitStart:
+ /* Setup a new wait */
+ Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
+ KxSingleThreadWait();
+ KiAcquireDispatcherLockAtDpcLevel();
+ }
- DPRINT("Looping Again\n");
- CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
-
- } while (TRUE);
-
- /* Release the Lock, we are done */
- DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
- return Status;
+ /* Wait complete */
+ KiReleaseDispatcherLock(Thread->WaitIrql);
+ return WaitStatus;
DontWait:
- /* Adjust the Quantum */
- KiAdjustQuantumThread(CurrentThread);
+ /* Release dispatcher lock but maintain high IRQL */
+ KiReleaseDispatcherLockFromDpcLevel();
- /* Release & Return */
- DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n. We did not wait.", KeGetCurrentThread(), Status);
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
- return Status;
+ /* Adjust the Quantum and return the wait status */
+ KiAdjustQuantumThread(Thread);
+ return WaitStatus;
}
/*
* @implemented
*/
-NTSTATUS STDCALL
-KeWaitForMultipleObjects(ULONG Count,
- PVOID Object[],
- WAIT_TYPE WaitType,
- KWAIT_REASON WaitReason,
- KPROCESSOR_MODE WaitMode,
- BOOLEAN Alertable,
- PLARGE_INTEGER Timeout,
- PKWAIT_BLOCK WaitBlockArray)
+NTSTATUS
+NTAPI
+KeWaitForMultipleObjects(IN ULONG Count,
+ IN PVOID Object[],
+ IN WAIT_TYPE WaitType,
+ IN KWAIT_REASON WaitReason,
+ IN KPROCESSOR_MODE WaitMode,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout OPTIONAL,
+ OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
{
- PDISPATCHER_HEADER CurrentObject;
+ PKMUTANT CurrentObject;
PKWAIT_BLOCK WaitBlock;
- PKWAIT_BLOCK TimerWaitBlock;
- PKTIMER ThreadTimer;
- PKTHREAD CurrentThread = KeGetCurrentThread();
- ULONG AllObjectsSignaled;
- ULONG WaitIndex;
- NTSTATUS Status;
- NTSTATUS WaitStatus;
-
- DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
- "PsGetCurrentThread() %x, Timeout %x\n", Count, Object, PsGetCurrentThread(), Timeout);
-
- /* Set the Current Thread */
- CurrentThread = KeGetCurrentThread();
-
- /* Check if the lock is already held */
- if (CurrentThread->WaitNext) {
-
- /* Lock is held, disable Wait Next */
- DPRINT("Lock is held\n");
- CurrentThread->WaitNext = FALSE;
-
- } else {
-
- /* Lock not held, acquire it */
- DPRINT("Lock is not held, acquiring\n");
- CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
- }
-
- /* Make sure the Wait Count is valid for the Thread and Maximum Wait Objects */
- if (!WaitBlockArray) {
-
+ PKTHREAD Thread = KeGetCurrentThread();
+ 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, InterruptTime;
+ ULONG Index, Hand = 0;
+
+ /* Make sure the Wait Count is valid */
+ if (!WaitBlockArray)
+ {
/* Check in regards to the Thread Object Limit */
- if (Count > THREAD_WAIT_OBJECTS) {
-
- KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
+ if (Count > THREAD_WAIT_OBJECTS)
+ {
+ /* Bugcheck */
+ KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
}
/* Use the Thread's Wait Block */
- WaitBlockArray = &CurrentThread->WaitBlock[0];
-
- } else {
-
- /* Using our own Block Array. Check in regards to System Object Limit */
- if (Count > MAXIMUM_WAIT_OBJECTS) {
-
- KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
+ WaitBlockArray = &Thread->WaitBlock[0];
+ }
+ else
+ {
+ /* Using our own Block Array, so check with the System Object Limit */
+ if (Count > MAXIMUM_WAIT_OBJECTS)
+ {
+ /* Bugcheck */
+ KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
}
}
- /* Start the actual Loop */
- do {
-
- /* Get the current Wait Status */
- WaitStatus = CurrentThread->WaitStatus;
-
- /* Append wait block to the KTHREAD wait block list */
- CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
-
- /* Check if the wait is (already) satisfied */
- AllObjectsSignaled = TRUE;
+ /* Sanity check */
+ ASSERT(Count != 0);
- /* First, we'll try to satisfy the wait directly */
- for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) {
-
- /* Get the Current Object */
- CurrentObject = (PDISPATCHER_HEADER)Object[WaitIndex];
-
- /* Check if the Object is Signaled */
- if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
-
- /* Check what kind of wait this is */
- if (WaitType == WaitAny) {
-
- /* This is a Wait Any, so just unwait this guy and exit */
- if (CurrentObject->SignalState != (LONG)MINLONG) {
-
- /* It has a normal signal state, so unwait it and return */
- KiSatisfyObjectWait(CurrentObject, CurrentThread);
- Status = STATUS_WAIT_0 | WaitIndex;
+ /* Check if the lock is already held */
+ 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))
+ {
+ /* Unlock the dispatcher */
+ KiReleaseDispatcherLock(Thread->WaitIrql);
+ }
+ else
+ {
+ /* Check what kind of wait this is */
+ Index = 0;
+ if (WaitType == WaitAny)
+ {
+ /* Loop blocks */
+ do
+ {
+ /* 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)
+ {
+ /* 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 if (CurrentObject->Header.SignalState > 0)
+ {
+ /* Another signaled object, unwait and return */
+ KiSatisfyNonMutantWait(CurrentObject);
+ WaitStatus = Index;
goto DontWait;
+ }
- } else {
-
- /* Is this a Mutant? */
- if (CurrentObject->Type == MutantObject) {
-
- /* According to wasm.ru, we must raise this exception (tested and true) */
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+ /* Go to the next block */
+ Index++;
+ } while (Index < Count);
+ }
+ else
+ {
+ /* Loop blocks */
+ do
+ {
+ /* Get the Current Object */
+ CurrentObject = (PKMUTANT)Object[Index];
+ ASSERT(CurrentObject->Header.Type != QueueObject);
+
+ /* 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;
}
- }
-
- } else {
-
- /* One of the objects isn't signaled... if this is a WaitAll, we will fail later */
- AllObjectsSignaled = FALSE;
- }
-
- /* Set up a Wait Block for this Object */
- WaitBlock->Object = CurrentObject;
- WaitBlock->Thread = CurrentThread;
- WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0 + WaitIndex);
- WaitBlock->WaitType = (USHORT)WaitType;
- WaitBlock->NextWaitBlock = WaitBlock + 1;
-
- /* Move to the next Wait Block */
- WaitBlock = WaitBlock->NextWaitBlock;
- }
-
- /* Return to the Root Wait Block */
- WaitBlock--;
- WaitBlock->NextWaitBlock = WaitBlockArray;
-
- /* Check if this is a Wait All and all the objects are signaled */
- if ((WaitType == WaitAll) && (AllObjectsSignaled)) {
-
- /* Return to the Root Wait Block */
- WaitBlock = CurrentThread->WaitBlockList;
-
- /* Satisfy their Waits and return to the caller */
- KiSatisifyMultipleObjectWaits(WaitBlock);
- Status = STATUS_WAIT_0;
- goto DontWait;
- }
-
- /* Make sure we can satisfy the Alertable request */
- if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status))
- break;
-
- /* Set the Wait Status */
- CurrentThread->WaitStatus = Status;
-
- /* Enable the Timeout Timer if there was any specified */
- if (Timeout != NULL) {
-
- /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
- if (!Timeout->QuadPart) {
- /* Return a timeout */
- Status = STATUS_TIMEOUT;
- goto DontWait;
+ /* Go to the next block */
+ Index++;
+ } while (Index < Count);
+
+ /* 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);
+
+ /* Go to the next block */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ } while(WaitBlock != WaitBlockArray);
+
+ /* Set the wait status and get out */
+ WaitStatus = Thread->WaitStatus;
+ goto DontWait;
+ }
}
- /* Point to Timer Wait Block and Thread Timer */
- TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
- ThreadTimer = &CurrentThread->Timer;
-
- /* Connect the Timer Wait Block */
- WaitBlock->NextWaitBlock = TimerWaitBlock;
-
- /* Set up the Timer Wait Block */
- TimerWaitBlock->Object = (PVOID)ThreadTimer;
- TimerWaitBlock->Thread = CurrentThread;
- TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
- TimerWaitBlock->WaitType = WaitAny;
- TimerWaitBlock->NextWaitBlock = WaitBlockArray;
-
- /* Link the timer to this Wait Block */
- InitializeListHead(&ThreadTimer->Header.WaitListHead);
+ /* 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)
+ {
+ /* 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 */
- if (!KiInsertTimer(ThreadTimer, *Timeout)) {
+ /* It didn't, so activate it */
+ Timer->Header.Inserted = TRUE;
- /* Return a timeout if we couldn't insert the timer for some reason */
- Status = STATUS_TIMEOUT;
- goto DontWait;
+ /* Link the wait blocks */
+ WaitBlock->NextWaitBlock = TimerBlock;
}
- }
-
- /* Insert into Object's Wait List*/
- WaitBlock = CurrentThread->WaitBlockList;
- do {
-
- /* Get the Current Object */
- CurrentObject = WaitBlock->Object;
-
- /* Link the Object to this Wait Block */
- InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
-
- /* Move to the next Wait Block */
- WaitBlock = WaitBlock->NextWaitBlock;
- } while (WaitBlock != WaitBlockArray);
- /* Handle Kernel Queues */
- if (CurrentThread->Queue) {
+ /* Insert into Object's Wait List*/
+ WaitBlock = WaitBlockArray;
+ do
+ {
+ /* Get the Current Object */
+ CurrentObject = WaitBlock->Object;
- DPRINT("Waking Queue\n");
- KiWakeQueue(CurrentThread->Queue);
- }
+ /* Link the Object to this Wait Block */
+ InsertTailList(&CurrentObject->Header.WaitListHead,
+ &WaitBlock->WaitListEntry);
- /* Block the Thread */
- DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode,
- WaitReason, KeGetCurrentThread());
- KiBlockThread(&Status,
- Alertable,
- WaitMode,
- (UCHAR)WaitReason);
+ /* Move to the next Wait Block */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ } while (WaitBlock != WaitBlockArray);
- /* Check if we were executing an APC */
- DPRINT("Thread is back\n");
- if (Status != STATUS_KERNEL_APC) {
-
- /* Return Status */
- return Status;
- }
+ /* Handle Kernel Queues */
+ if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
- DPRINT("Looping Again\n");
- CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+ /* Setup the wait information */
+ Thread->State = Waiting;
- } while (TRUE);
-
- /* Release the Lock, we are done */
- DPRINT("Returning, %x. Status: %d\n", KeGetCurrentThread(), Status);
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
- return Status;
-
-DontWait:
- /* Adjust the Quantum */
- KiAdjustQuantumThread(CurrentThread);
-
- /* Release & Return */
- DPRINT("Returning, %x. Status: %d\n. We did not wait.",
- KeGetCurrentThread(), Status);
- KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
- return Status;
-}
-
-VOID
-FASTCALL
-KiSatisfyObjectWait(PDISPATCHER_HEADER Object,
- PKTHREAD Thread)
-
-{
- /* Special case for Mutants */
- if (Object->Type == MutantObject) {
+ /* Add the thread to the wait list */
+ KiAddThreadToWaitList(Thread, Swappable);
- /* Decrease the Signal State */
- Object->SignalState--;
+ /* Activate thread swap */
+ ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+ KiSetThreadSwapBusy(Thread);
- /* Check if it's now non-signaled */
- if (Object->SignalState == 0) {
-
- /* Set the Owner Thread */
- ((PKMUTANT)Object)->OwnerThread = Thread;
-
- /* Disable APCs if needed */
- Thread->KernelApcDisable -= ((PKMUTANT)Object)->ApcDisable;
-
- /* Check if it's abandoned */
- if (((PKMUTANT)Object)->Abandoned) {
-
- /* Unabandon it */
- ((PKMUTANT)Object)->Abandoned = FALSE;
-
- /* Return Status */
- Thread->WaitStatus = STATUS_ABANDONED;
+ /* Check if we have a timer */
+ if (Timeout)
+ {
+ /* Insert it */
+ KxInsertTimer(Timer, Hand);
+ }
+ else
+ {
+ /* Otherwise, unlock the dispatcher */
+ KiReleaseDispatcherLockFromDpcLevel();
}
- /* Insert it into the Mutant List */
- InsertHeadList(&Thread->MutantListHead, &((PKMUTANT)Object)->MutantListEntry);
- }
-
- } else if ((Object->Type & TIMER_OR_EVENT_TYPE) == EventSynchronizationObject) {
-
- /* These guys (Syncronization Timers and Events) just get un-signaled */
- Object->SignalState = 0;
-
- } else if (Object->Type == SemaphoreObject) {
-
- /* These ones can have multiple signalings, so we only decrease it */
- Object->SignalState--;
- }
-}
-
-VOID
-FASTCALL
-KiWaitTest(PDISPATCHER_HEADER Object,
- KPRIORITY Increment)
-{
- PLIST_ENTRY WaitEntry;
- PLIST_ENTRY WaitList;
- PKWAIT_BLOCK CurrentWaitBlock;
- PKWAIT_BLOCK NextWaitBlock;
- PKTHREAD WaitThread;
-
- /* Loop the Wait Entries */
- DPRINT("KiWaitTest for Object: %x\n", Object);
- WaitList = &Object->WaitListHead;
- WaitEntry = WaitList->Flink;
- while ((WaitEntry != WaitList) && (Object->SignalState > 0)) {
-
- /* Get the current wait block */
- CurrentWaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
- WaitThread = CurrentWaitBlock->Thread;
-
- /* Check the current Wait Mode */
- if (CurrentWaitBlock->WaitType == WaitAny) {
-
- /* Easy case, satisfy only this wait */
- DPRINT("Satisfiying a Wait any\n");
- WaitEntry = WaitEntry->Blink;
- KiSatisfyObjectWait(Object, WaitThread);
-
- } else {
-
- /* Everything must be satisfied */
- DPRINT("Checking for a Wait All\n");
- NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
-
- /* Loop first to make sure they are valid */
- while (NextWaitBlock != CurrentWaitBlock) {
-
- /* Check if the object is signaled */
- DPRINT("Checking: %x %d\n", NextWaitBlock->Object, Object->SignalState);
- if (!KiIsObjectSignaled(NextWaitBlock->Object, WaitThread)) {
+ /* Swap the thread */
+ WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
- /* It's not, move to the next one */
- DPRINT("One of the object is non-signaled, sorry.\n");
- goto SkipUnwait;
- }
+ /* Check if we were executing an APC */
+ if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
- /* Go to the next Wait block */
- NextWaitBlock = NextWaitBlock->NextWaitBlock;
+ /* Check if we had a timeout */
+ if (Timeout)
+ {
+ /* Recalculate due times */
+ Timeout = KiRecalculateDueTime(OriginalDueTime,
+ &DueTime,
+ &NewDueTime);
}
-
- /* All the objects are signaled, we can satisfy */
- DPRINT("Satisfiying a Wait All\n");
- WaitEntry = WaitEntry->Blink;
- KiSatisifyMultipleObjectWaits(CurrentWaitBlock);
}
- /* All waits satisfied, unwait the thread */
- DPRINT("Unwaiting the Thread\n");
- KiAbortWaitThread(WaitThread, CurrentWaitBlock->WaitKey, Increment);
-
-SkipUnwait:
- /* Next entry */
- WaitEntry = WaitEntry->Flink;
+WaitStart:
+ /* Setup a new wait */
+ Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
+ KxMultiThreadWait();
+ KiAcquireDispatcherLockAtDpcLevel();
}
- DPRINT("Done\n");
-}
+ /* We are done */
+ KiReleaseDispatcherLock(Thread->WaitIrql);
+ return WaitStatus;
-/* Must be called with the dispatcher lock held */
-VOID
-FASTCALL
-KiAbortWaitThread(PKTHREAD Thread,
- NTSTATUS WaitStatus,
- KPRIORITY Increment)
-{
- PKWAIT_BLOCK WaitBlock;
-
- /* If we are blocked, we must be waiting on something also */
- DPRINT("KiAbortWaitThread: %x, Status: %x, %x \n", Thread, WaitStatus, Thread->WaitBlockList);
- ASSERT((Thread->State == Waiting) == (Thread->WaitBlockList != NULL));
-
- /* Remove the Wait Blocks from the list */
- DPRINT("Removing waits\n");
- WaitBlock = Thread->WaitBlockList;
- do {
-
- /* Remove it */
- DPRINT("Removing Waitblock: %x, %x\n", WaitBlock, WaitBlock->NextWaitBlock);
- RemoveEntryList(&WaitBlock->WaitListEntry);
-
- /* Go to the next one */
- WaitBlock = WaitBlock->NextWaitBlock;
- } while (WaitBlock != Thread->WaitBlockList);
-
- /* Check if there's a Thread Timer */
- if (Thread->Timer.Header.Inserted) {
-
- /* Cancel the Thread Timer with the no-lock fastpath */
- DPRINT("Removing the Thread's Timer\n");
- Thread->Timer.Header.Inserted = FALSE;
- RemoveEntryList(&Thread->Timer.TimerListEntry);
- }
-
- /* Increment the Queue's active threads */
- if (Thread->Queue) {
-
- DPRINT("Incrementing Queue's active threads\n");
- Thread->Queue->CurrentCount++;
- }
+DontWait:
+ /* Release dispatcher lock but maintain high IRQL */
+ KiReleaseDispatcherLockFromDpcLevel();
- /* Reschedule the Thread */
- DPRINT("Unblocking the Thread\n");
- KiUnblockThread(Thread, &WaitStatus, 0);
+ /* Adjust the Quantum and return the wait status */
+ KiAdjustQuantumThread(Thread);
+ return WaitStatus;
}
-BOOLEAN
-inline
-FASTCALL
-KiIsObjectSignaled(PDISPATCHER_HEADER Object,
- PKTHREAD Thread)
+NTSTATUS
+NTAPI
+NtDelayExecution(IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER DelayInterval)
{
- /* Mutants are...well...mutants! */
- if (Object->Type == MutantObject) {
-
- /*
- * Because Cutler hates mutants, they are actually signaled if the Signal State is <= 0
- * Well, only if they are recursivly acquired (i.e if we own it right now).
- * Of course, they are also signaled if their signal state is 1.
- */
- if ((Object->SignalState <= 0 && ((PKMUTANT)Object)->OwnerThread == Thread) ||
- (Object->SignalState == 1)) {
-
- /* Signaled Mutant */
- return (TRUE);
-
- } else {
+ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ LARGE_INTEGER SafeInterval;
+ NTSTATUS Status;
- /* Unsignaled Mutant */
- return (FALSE);
+ /* Check the previous mode */
+ if (PreviousMode != KernelMode)
+ {
+ /* Enter SEH for probing */
+ _SEH2_TRY
+ {
+ /* Probe and capture the time out */
+ SafeInterval = ProbeForReadLargeInteger(DelayInterval);
+ DelayInterval = &SafeInterval;
}
- }
-
- /* Any other object is not a mutated freak, so let's use logic */
- return (!Object->SignalState <= 0);
-}
-
-VOID
-inline
-FASTCALL
-KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock)
-{
- PKWAIT_BLOCK FirstBlock = WaitBlock;
- PKTHREAD WaitThread = WaitBlock->Thread;
-
- /* Loop through all the Wait Blocks, and wake each Object */
- do {
-
- /* Wake the Object */
- KiSatisfyObjectWait(WaitBlock->Object, WaitThread);
- WaitBlock = WaitBlock->NextWaitBlock;
- } while (WaitBlock != FirstBlock);
-}
-
-VOID
-inline
-FASTCALL
-KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
- ULONG Type,
- ULONG Size,
- ULONG SignalState)
-{
- Header->Type = (UCHAR)Type;
- Header->Absolute = 0;
- Header->Inserted = 0;
- Header->Size = (UCHAR)Size;
- Header->SignalState = SignalState;
- InitializeListHead(&(Header->WaitListHead));
-}
-
-KIRQL
-inline
-FASTCALL
-KeAcquireDispatcherDatabaseLock(VOID)
-{
- KIRQL OldIrql;
-
- KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
- return OldIrql;
-}
-
-VOID
-inline
-FASTCALL
-KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
-{
- KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
-}
-
-VOID
-inline
-FASTCALL
-KeInitializeDispatcher(VOID)
-{
- /* Initialize the Dispatcher Lock */
- KeInitializeSpinLock(&DispatcherDatabaseLock);
-}
-
-VOID
-inline
-FASTCALL
-KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
-{
- /* If it's the idle thread, dispatch */
- if (!KeIsExecutingDpc() && OldIrql < DISPATCH_LEVEL && KeGetCurrentThread() != NULL &&
- KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread) {
-
- KiDispatchThreadNoLock(Ready);
- KeLowerIrql(OldIrql);
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Return the exception code */
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
- } else {
+ /* Call the Kernel Function */
+ Status = KeDelayExecutionThread(PreviousMode,
+ Alertable,
+ DelayInterval);
- /* Just release the spin lock */
- KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
- }
-}
-
-VOID
-inline
-FASTCALL
-KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
-{
- KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
+ /* Return Status */
+ return Status;
}
/* EOF */