- Implement KiRecalculateDueTime to handle cases where a timeout wait has been interu...
[reactos.git] / reactos / ntoskrnl / ke / wait.c
index 859c0d9..92749c2 100644 (file)
 /*
- * COPYRIGHT:            See COPYING in the top level directory
- * PROJECT:              ReactOS project
- * FILE:                 ntoskrnl/ke/wait.c
- * PURPOSE:              Manages non-busy waiting
- * PROGRAMMER:           David Welch (welch@mcmail.com)
- * REVISION HISTORY:
- *           21/07/98: Created
- *          12/1/99:  Phillip Susi: Fixed wake code in KeDispatcherObjectWake
- *                so that things like KeWaitForXXX() return the correct value
- */
-
-/* NOTES ********************************************************************
- *
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            ntoskrnl/ke/wait.c
+ * PURPOSE:         Manages waiting for Dispatcher Objects
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ *                  Gunnar Dalsnes
  */
 
 /* INCLUDES ******************************************************************/
 
-#include <ddk/ntddk.h>
-#include <internal/ke.h>
-#include <internal/ps.h>
-#include <internal/ob.h>
-#include <internal/id.h>
-#include <ntos/ntdef.h>
-
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 /* GLOBALS ******************************************************************/
 
-static KSPIN_LOCK DispatcherDatabaseLock;
-static BOOLEAN WaitSet = FALSE;
-static KIRQL oldlvl = PASSIVE_LEVEL;
-static PKTHREAD Owner = NULL;
-
-#define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
-#define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
+KSPIN_LOCK DispatcherDatabaseLock;
 
-/* FUNCTIONS *****************************************************************/
+/* PRIVATE FUNCTIONS *********************************************************/
 
-VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
-                                 ULONG Type,
-                                 ULONG Size,
-                                 ULONG SignalState)
+VOID
+FASTCALL
+KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock)
 {
-   Header->Type = Type;
-   Header->Absolute = 0;
-   Header->Inserted = 0;
-   Header->Size = Size;
-   Header->SignalState = SignalState;
-   InitializeListHead(&(Header->WaitListHead));
+    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 KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
-/*
- * PURPOSE: Acquires the dispatcher database lock for the caller
- */
+VOID
+FASTCALL
+KiWaitTest(IN PVOID ObjectPointer,
+           IN KPRIORITY Increment)
 {
-   DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
-   if (WaitSet && Owner == KeGetCurrentThread())
-     {
-       return;
-     }
-   KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
-   WaitSet = Wait;
-   Owner = KeGetCurrentThread();
+    PLIST_ENTRY WaitEntry;
+    PLIST_ENTRY WaitList;
+    PKWAIT_BLOCK CurrentWaitBlock;
+    PKWAIT_BLOCK NextWaitBlock;
+    PKTHREAD WaitThread;
+    PKMUTANT FirstObject = ObjectPointer, Object;
+
+    /* Loop the Wait Entries */
+    WaitList = &FirstObject->Header.WaitListHead;
+    WaitEntry = WaitList->Flink;
+    while ((FirstObject->Header.SignalState > 0) &&
+           (WaitEntry != WaitList))
+    {
+        /* 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 */
+            WaitEntry = WaitEntry->Blink;
+            KiSatisfyObjectWait(FirstObject, WaitThread);
+        }
+        else
+        {
+            /* Everything must be satisfied */
+            NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
+
+            /* Loop first to make sure they are valid */
+            while (NextWaitBlock != CurrentWaitBlock)
+            {
+                /* Make sure this isn't a timeout block */
+                if (NextWaitBlock->WaitKey != STATUS_TIMEOUT)
+                {
+                    /* Get the object */
+                    Object = NextWaitBlock->Object;
+
+                    /* Check if this is a mutant */
+                    if ((Object->Header.Type == MutantObject) &&
+                        (Object->Header.SignalState <= 0) &&
+                        (WaitThread == Object->OwnerThread))
+                    {
+                        /* It's a signaled mutant */
+                    }
+                    else if (Object->Header.SignalState <= 0)
+                    {
+                        /* Skip the unwaiting */
+                        goto SkipUnwait;
+                    }
+                }
+
+                /* Go to the next Wait block */
+                NextWaitBlock = NextWaitBlock->NextWaitBlock;
+            }
+
+            /* All the objects are signaled, we can satisfy */
+            WaitEntry = WaitEntry->Blink;
+            KiWaitSatisfyAll(CurrentWaitBlock);
+        }
+
+        /* All waits satisfied, unwait the thread */
+        KiAbortWaitThread(WaitThread, CurrentWaitBlock->WaitKey, Increment);
+
+SkipUnwait:
+        /* Next entry */
+        WaitEntry = WaitEntry->Flink;
+    }
 }
 
-VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
+/* Must be called with the dispatcher lock held */
+VOID
+FASTCALL
+KiAbortWaitThread(IN PKTHREAD Thread,
+                  IN NTSTATUS WaitStatus,
+                  IN KPRIORITY Increment)
 {
-  DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x)\n", Wait);
-  assert(Wait == WaitSet);
-  if (!Wait)
+    PKWAIT_BLOCK WaitBlock;
+    PKTIMER Timer;
+    LONG NewPriority;
+
+    /* Update wait status */
+    Thread->WaitStatus |= WaitStatus;
+
+    /* Remove the Wait Blocks from the list */
+    WaitBlock = Thread->WaitBlockList;
+    do
+    {
+        /* Remove it */
+        RemoveEntryList(&WaitBlock->WaitListEntry);
+
+        /* Go to the next one */
+        WaitBlock = WaitBlock->NextWaitBlock;
+    } while (WaitBlock != Thread->WaitBlockList);
+
+    /* Remove the thread from the wait list! */
+    if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
+
+    /* 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);
+    }
+
+    /* Increment the Queue's active threads */
+    if (Thread->Queue) Thread->Queue->CurrentCount++;
+
+    /* Check if this is a non-RT thread */
+    if (Thread->Priority < LOW_REALTIME_PRIORITY)
+    {
+        /* Check if boosting is enabled and we can boost */
+        if (!(Thread->DisableBoost) && !(Thread->PriorityDecrement))
+        {
+            /* We can boost, so calculate the new priority */
+            NewPriority = Thread->BasePriority + Increment;
+            if (NewPriority > Thread->Priority)
+            {
+                /* Make sure the new priority wouldn't push the thread to RT */
+                if (NewPriority >= LOW_REALTIME_PRIORITY)
+                {
+                    /* Set it just before the RT zone */
+                    Thread->Priority = LOW_REALTIME_PRIORITY - 1;
+                }
+                else
+                {
+                    /* Otherwise, set our calculated priority */
+                    Thread->Priority = NewPriority;
+                }
+            }
+        }
+
+        /* Check if this is a high-priority thread */
+        if (Thread->BasePriority >= 14)
+        {
+            /* It is, simply reset the quantum */
+            Thread->Quantum = Thread->QuantumReset;
+        }
+        else
+        {
+            /* Otherwise, decrease quantum */
+            Thread->Quantum--;
+            if (Thread->Quantum <= 0)
+            {
+                /* We've went below 0, reset it */
+                Thread->Quantum = Thread->QuantumReset;
+
+                /* Apply per-quantum priority decrement */
+                Thread->Priority -= (Thread->PriorityDecrement + 1);
+                if (Thread->Priority < Thread->BasePriority)
+                {
+                    /* We've went too low, reset it */
+                    Thread->Priority = Thread->BasePriority;
+                }
+
+                /* Delete per-quantum decrement */
+                Thread->PriorityDecrement = 0;
+            }
+        }
+    }
+    else
     {
-      Owner = NULL;
-      KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
+        /* For real time threads, just reset the quantum */
+        Thread->Quantum = Thread->QuantumReset;
     }
+
+    /* Reschedule the Thread */
+    KiReadyThread(Thread);
 }
 
-VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
+VOID
+FASTCALL
+KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
 {
-   DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
-   assert(Wait==WaitSet);
-   if (!Wait)
-     {
-       Owner = NULL;
-       KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
-     }
+    /* Increase contention count */
+    FastMutex->Contention++;
+
+    /* Wait for the event */
+    KeWaitForSingleObject(&FastMutex->Gate,
+                          WrMutex,
+                          KernelMode,
+                          FALSE,
+                          NULL);
 }
 
-static BOOLEAN
-KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
-                        PKTHREAD Thread)
-/*
- * FUNCTION: Perform side effects on object before a wait for a thread is
- *           satisfied
- */
+VOID
+FASTCALL
+KiExitDispatcher(IN KIRQL OldIrql)
 {
-   BOOLEAN Abandoned = FALSE;
+    /* Check if it's the idle thread */
+    if (!(KeIsExecutingDpc()) &&
+        (OldIrql < DISPATCH_LEVEL) &&
+        (KeGetCurrentThread()) &&
+        (KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread))
+    {
+        /* Dispatch a new thread */
+        KiDispatchThreadNoLock(Ready);
+    }
+    else
+    {
+        /* Otherwise just release the lock */
+        KeReleaseDispatcherDatabaseLockFromDpcLevel();
+    }
 
-   switch (hdr->Type)
-   {
-      case InternalSynchronizationEvent:
-         hdr->SignalState = 0;
-         break;
+    /* Lower irql back */
+    KeLowerIrql(OldIrql);
+}
 
-      case InternalSemaphoreType:
-         hdr->SignalState--;
-         break;
+/* PUBLIC FUNCTIONS **********************************************************/
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
+                       IN BOOLEAN Alertable,
+                       IN PLARGE_INTEGER Interval OPTIONAL)
+{
+    PKWAIT_BLOCK TimerWaitBlock;
+    PKTIMER ThreadTimer;
+    PKTHREAD CurrentThread = KeGetCurrentThread();
+    NTSTATUS WaitStatus = STATUS_SUCCESS;
+    BOOLEAN Swappable;
+    PLARGE_INTEGER OriginalDueTime = Interval;
+    LARGE_INTEGER DueTime, NewDueTime;
+
+    /* Check if the lock is already held */
+    if (CurrentThread->WaitNext)
+    {
+        /* Lock is held, disable Wait Next */
+        CurrentThread->WaitNext = FALSE;
+    }
+    else
+    {
+        /* Lock not held, acquire it */
+        CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+    }
 
-      case InternalProcessType:
-         break;
+    /* Use built-in Wait block */
+    TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
 
-      case InternalThreadType:
-         break;
+    /* Start Wait Loop */
+    do
+    {
+        /* Check if a kernel APC is pending and we're below APC_LEVEL */
+        if ((CurrentThread->ApcState.KernelApcPending) &&
+            !(CurrentThread->SpecialApcDisable) &&
+            (CurrentThread->WaitIrql < APC_LEVEL))
+        {
+            /* Unlock the dispatcher */
+            KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+        }
+        else
+        {
+            /* Check if we can do an alertable wait, if requested */
+            KiCheckAlertability();
+
+            /* Check if we can swap the thread's stack */
+            CurrentThread->WaitListEntry.Flink = NULL;
+            KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
+
+            /* Set status */
+            CurrentThread->WaitStatus = STATUS_WAIT_0;
+
+            /* Set Timer */
+            ThreadTimer = &CurrentThread->Timer;
+
+            /* Setup the Wait Block */
+            CurrentThread->WaitBlockList = TimerWaitBlock;
+            TimerWaitBlock->NextWaitBlock = TimerWaitBlock;
+
+            /* Link the timer to this Wait Block */
+            ThreadTimer->Header.WaitListHead.Flink =
+                &TimerWaitBlock->WaitListEntry;
+            ThreadTimer->Header.WaitListHead.Blink =
+                &TimerWaitBlock->WaitListEntry;
+
+            /* Insert the Timer into the Timer Lists and enable it */
+            if (!KiInsertTimer(ThreadTimer, *Interval))
+            {
+                /* FIXME: We should find a new ready thread */
+                WaitStatus = STATUS_SUCCESS;
+                break;
+            }
 
-      case InternalNotificationEvent:
-         break;
+            /* Save due time */
+            DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
 
-      case InternalSynchronizationTimer:
-         hdr->SignalState = FALSE;
-         break;
+            /* Handle Kernel Queues */
+            if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
 
-      case InternalNotificationTimer:
-         break;
+            /* Setup the wait information */
+            CurrentThread->Alertable = Alertable;
+            CurrentThread->WaitMode = WaitMode;
+            CurrentThread->WaitReason = DelayExecution;
+            CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
+            CurrentThread->State = Waiting;
 
-      case InternalMutexType:
-      {
-         PKMUTEX Mutex;
+            /* Find a new thread to run */
+            KiAddThreadToWaitList(CurrentThread, Swappable);
+            WaitStatus = KiSwapThread();
+            ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
 
-         Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
-         hdr->SignalState--;
-         assert(hdr->SignalState <= 1);
-         if (hdr->SignalState == 0)
-         {
-            if (Thread == NULL)
+            /* Check if we were executing an APC or if we timed out */
+            if (WaitStatus != STATUS_KERNEL_APC)
             {
-               DPRINT("Thread == NULL!\n");
-               KeBugCheck(0);
+                /* This is a good thing */
+                if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
+
+                /* Return Status */
+                return WaitStatus;
             }
-            Abandoned = Mutex->Abandoned;
-            if (Thread != NULL)
-               InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
-            Mutex->OwnerThread = Thread;
-            Mutex->Abandoned = FALSE;
-         }
-      }
-         break;
-
-      default:
-         DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
-         KeBugCheck(0);
-   }
-
-   return Abandoned;
-}
 
-static BOOLEAN
-KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
-                    PKTHREAD Thread)
-{
-   if (hdr->Type == InternalMutexType)
-   {
-      PKMUTEX Mutex;
-
-      Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
-
-      assert(hdr->SignalState <= 1);
-
-      if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
-      {
-         return (TRUE);
-      }
-      else
-      {
-         return (FALSE);
-      }
-   }
-
-   if (hdr->SignalState <= 0)
-   {
-      return (FALSE);
-   }
-   else
-   {
-      return (TRUE);
-   }
-}
+            /* Recalculate due times */
+            Interval = KiRecalculateDueTime(OriginalDueTime,
+                                            &DueTime,
+                                            &NewDueTime);
+        }
 
-VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
-{
-   PKWAIT_BLOCK WaitBlock;
-   BOOLEAN WasWaiting = FALSE;
-
-   KeAcquireDispatcherDatabaseLock(FALSE);
-
-   WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
-   if (WaitBlock != NULL)
-     {
-       WasWaiting = TRUE;
-     }
-   while (WaitBlock != NULL)
-     {
-       RemoveEntryList(&WaitBlock->WaitListEntry);
-       WaitBlock = WaitBlock->NextWaitBlock;
-     }
-   Thread->Tcb.WaitBlockList = NULL;
-
-   if (WasWaiting)
-     {
-       PsUnblockThread(Thread, &WaitStatus);
-     }
-
-   KeReleaseDispatcherDatabaseLock(FALSE);
+        /* Acquire again the lock */
+        CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+    } while (TRUE);
+
+    /* Release the Lock, we are done */
+    KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+    return WaitStatus;
 }
 
-static BOOLEAN
-KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
-                               BOOLEAN WakeAll)
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+KeWaitForSingleObject(IN PVOID Object,
+                      IN KWAIT_REASON WaitReason,
+                      IN KPROCESSOR_MODE WaitMode,
+                      IN BOOLEAN Alertable,
+                      IN PLARGE_INTEGER Timeout OPTIONAL)
 {
-   PKWAIT_BLOCK Waiter;
-   PKWAIT_BLOCK WaiterHead;
-   PLIST_ENTRY EnumEntry;
-   NTSTATUS Status;
-   BOOLEAN Abandoned;
-   BOOLEAN AllSignaled;
-   BOOLEAN WakedAny = FALSE;
-
-   DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
-   DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
-           hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
-
-   if (IsListEmpty(&hdr->WaitListHead))
-   {
-      return (FALSE);
-   }
-
-   //enum waiters for this dispatcher object
-   EnumEntry = hdr->WaitListHead.Flink;
-   while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
-   {
-      WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
-      DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
-      EnumEntry = EnumEntry->Flink;
-      assert(WaiterHead->Thread->WaitBlockList != NULL);
-
-      Abandoned = FALSE;
-
-      if (WaiterHead->WaitType == WaitAny)
-      {
-         DPRINT("WaitAny: Remove all wait blocks.\n");
-         for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
-         {
-            RemoveEntryList(&Waiter->WaitListEntry);
-         }
-
-         WaiterHead->Thread->WaitBlockList = NULL;
-
-         /*
-          * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
-          * but thats ok since WakeAll objects has no sideeffects.
-          */
-         Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
-      }
-      else
-      {
-         DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
-
-         AllSignaled = TRUE;
-
-         //all WaitAll obj. for thread need to be signaled to satisfy a wake
-         for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
-         {
-            //no need to check hdr since it has to be signaled
-            if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
+    PKMUTANT CurrentObject;
+    PKWAIT_BLOCK WaitBlock;
+    PKWAIT_BLOCK TimerWaitBlock;
+    PKTIMER ThreadTimer;
+    PKTHREAD CurrentThread = KeGetCurrentThread();
+    NTSTATUS WaitStatus = STATUS_SUCCESS;
+    BOOLEAN Swappable;
+    LARGE_INTEGER DueTime, NewDueTime;
+    PLARGE_INTEGER OriginalDueTime = Timeout;
+
+    /* Check if the lock is already held */
+    if (CurrentThread->WaitNext)
+    {
+        /* Lock is held, disable Wait Next */
+        CurrentThread->WaitNext = FALSE;
+    }
+    else
+    {
+        /* Lock not held, acquire it */
+        CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+    }
+
+    /* Start the actual Loop */
+    WaitBlock = &CurrentThread->WaitBlock[0];
+    do
+    {
+        /* Check if a kernel APC is pending and we're below APC_LEVEL */
+        if ((CurrentThread->ApcState.KernelApcPending) &&
+            !(CurrentThread->SpecialApcDisable) &&
+            (CurrentThread->WaitIrql < APC_LEVEL))
+        {
+            /* Unlock the dispatcher */
+            KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+        }
+        else
+        {
+            /* Set default status */
+            CurrentThread->WaitStatus = STATUS_WAIT_0;
+
+            /* Get the Current Object */
+            CurrentObject = (PKMUTANT)Object;
+
+            /* 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) ||
+                    (CurrentThread == 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, CurrentThread);
+                        WaitStatus = CurrentThread->WaitStatus;
+                        goto DontWait;
+                    }
+                    else
+                    {
+                        /* Raise an exception (see wasm.ru) */
+                        KeReleaseDispatcherDatabaseLock(CurrentThread->
+                                                        WaitIrql);
+                        ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
+                   }
+                }
+            }
+            else if (CurrentObject->Header.SignalState > 0)
             {
-               if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
-               {
-                  AllSignaled = FALSE;
-                  break;
-               }
+                /* Another satisfied object */
+                KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
+                WaitStatus = STATUS_WAIT_0;
+                goto DontWait;
             }
-         }
 
-         if (AllSignaled)
-         {
-            for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
+            /* Append wait block to the KTHREAD wait block list */
+            CurrentThread->WaitBlockList = WaitBlock;
+
+            /* Set up the Wait Block */
+            WaitBlock->Object = CurrentObject;
+            WaitBlock->WaitKey = (USHORT)(STATUS_SUCCESS);
+            WaitBlock->WaitType = WaitAny;
+
+            /* Make sure we can satisfy the Alertable request */
+            KiCheckAlertability();
+
+            /* Check if we can swap the thread's stack */
+            CurrentThread->WaitListEntry.Flink = NULL;
+            KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
+
+            /* 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;
+                }
+
+                /* 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->NextWaitBlock = WaitBlock;
+
+                /* Link the timer to this Wait Block */
+                ThreadTimer->Header.WaitListHead.Flink =
+                    &TimerWaitBlock->WaitListEntry;
+                ThreadTimer->Header.WaitListHead.Blink =
+                    &TimerWaitBlock->WaitListEntry;
+
+                /* Insert the Timer into the Timer Lists and enable it */
+                if (!KiInsertTimer(ThreadTimer, *Timeout))
+                {
+                    /* Return a timeout if we couldn't insert the timer */
+                    WaitStatus = STATUS_TIMEOUT;
+                    goto DontWait;
+                }
+
+                /* Set the current due time */
+                DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
+            }
+            else
             {
-               RemoveEntryList(&Waiter->WaitListEntry);
+                /* No timer block, so just set our wait block as next */
+                WaitBlock->NextWaitBlock = WaitBlock;
+            }
+
+            /* Link the Object to this Wait Block */
+            InsertTailList(&CurrentObject->Header.WaitListHead,
+                           &WaitBlock->WaitListEntry);
+
+            /* Handle Kernel Queues */
+            if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
 
-               if (Waiter->WaitType == WaitAll)
-               {
-                  Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
-                     ? TRUE : Abandoned;
-               }
+            /* Setup the wait information */
+            CurrentThread->Alertable = Alertable;
+            CurrentThread->WaitMode = WaitMode;
+            CurrentThread->WaitReason = WaitReason;
+            CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
+            CurrentThread->State = Waiting;
 
-               //no WaitAny objects can possibly be signaled since we are here
-               assert(!(Waiter->WaitType == WaitAny
-                                         && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
+            /* Find a new thread to run */
+            KiAddThreadToWaitList(CurrentThread, Swappable);
+            WaitStatus = KiSwapThread();
+            ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
+
+            /* 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);
             }
+        }
 
-            WaiterHead->Thread->WaitBlockList = NULL;
-         }
-      }
-
-      if (WaiterHead->Thread->WaitBlockList == NULL)
-      {
-         Status = WaiterHead->WaitKey;
-         if (Abandoned)
-         {
-            DPRINT("Abandoned mutex among objects");
-            Status += STATUS_ABANDONED_WAIT_0;
-         }
-
-         WakedAny = TRUE;
-         DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
-         PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
-      }
-   }
-
-   return WakedAny;
-}
+        /* Acquire again the lock */
+        CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+    } while (TRUE);
 
+    /* Release the Lock, we are done */
+    KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+    return WaitStatus;
 
-BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
-/*
- * FUNCTION: Wake threads waiting on a dispatcher object
- * NOTE: The exact semantics of waking are dependant on the type of object
- */
-{
-   BOOL Ret;
-
-   DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
-//   DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
-//       &hdr->WaitListHead,hdr->WaitListHead.Flink);
-   DPRINT("hdr->Type %x\n",hdr->Type);
-   switch (hdr->Type)
-     {
-      case InternalNotificationEvent:
-       return(KeDispatcherObjectWakeAll(hdr));
-
-      case InternalNotificationTimer:
-       return(KeDispatcherObjectWakeAll(hdr));
-
-      case InternalSynchronizationEvent:
-       return(KeDispatcherObjectWakeOne(hdr));
-
-      case InternalSynchronizationTimer:
-       return(KeDispatcherObjectWakeOne(hdr));
-
-      case InternalSemaphoreType:
-       DPRINT("hdr->SignalState %d\n", hdr->SignalState);
-       if(hdr->SignalState>0)
-         {
-           do
-             {
-               DPRINT("Waking one semaphore waiter\n");
-               Ret = KeDispatcherObjectWakeOne(hdr);
-             } while(hdr->SignalState > 0 &&  Ret) ;
-           return(Ret);
-         }
-       else return FALSE;
-
-     case InternalProcessType:
-       return(KeDispatcherObjectWakeAll(hdr));
-
-     case InternalThreadType:
-       return(KeDispatcherObjectWakeAll(hdr));
-
-     case InternalMutexType:
-       return(KeDispatcherObjectWakeOne(hdr));
-     }
-   DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
-   KeBugCheck(0);
-   return(FALSE);
-}
+DontWait:
+    /* Adjust the Quantum */
+    KiAdjustQuantumThread(CurrentThread);
 
+    /* Release & Return */
+    KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+    return WaitStatus;
+}
 
-NTSTATUS STDCALL
-KeWaitForSingleObject(PVOID Object,
-                      KWAIT_REASON WaitReason,
-                      KPROCESSOR_MODE WaitMode,
-                      BOOLEAN Alertable,
-                      PLARGE_INTEGER Timeout)
 /*
- * 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
+ * @implemented
  */
+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)
 {
-   return KeWaitForMultipleObjects(1,
-                                   &Object,
-                                   WaitAny,
-                                   WaitReason,
-                                   WaitMode,
-                                   Alertable,
-                                   Timeout,
-                                   NULL);
-}
+    PKMUTANT CurrentObject;
+    PKWAIT_BLOCK WaitBlock;
+    PKWAIT_BLOCK TimerWaitBlock;
+    PKTIMER ThreadTimer;
+    PKTHREAD CurrentThread = KeGetCurrentThread();
+    ULONG AllObjectsSignaled;
+    ULONG WaitIndex;
+    NTSTATUS WaitStatus = STATUS_SUCCESS;
+    BOOLEAN Swappable;
+    PLARGE_INTEGER OriginalDueTime = Timeout;
+    LARGE_INTEGER DueTime, NewDueTime;
+
+    /* Set the Current Thread */
+    CurrentThread = KeGetCurrentThread();
+
+    /* Check if the lock is already held */
+    if (CurrentThread->WaitNext)
+    {
+        /* Lock is held, disable Wait Next */
+        CurrentThread->WaitNext = FALSE;
+    }
+    else
+    {
+        /* Lock not held, acquire it */
+        CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+    }
+
+    /* Make sure the Wait Count is valid */
+    if (!WaitBlockArray)
+    {
+        /* Check in regards to the Thread Object Limit */
+        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, so check with the System Object Limit */
+        if (Count > MAXIMUM_WAIT_OBJECTS)
+        {
+            /* Bugcheck */
+            KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
+        }
+    }
 
+    /* Start the actual Loop */
+    do
+    {
+        /* Check if a kernel APC is pending and we're below APC_LEVEL */
+        if ((CurrentThread->ApcState.KernelApcPending) &&
+            !(CurrentThread->SpecialApcDisable) &&
+            (CurrentThread->WaitIrql < APC_LEVEL))
+        {
+            /* Unlock the dispatcher */
+            KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+        }
+        else
+        {
+            /* Append wait block to the KTHREAD wait block list */
+            CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
+
+            /* Set default wait status */
+            CurrentThread->WaitStatus = STATUS_WAIT_0;
+
+            /* 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];
+
+                /* Check the type of wait */
+                if (WaitType == WaitAny)
+                {
+                    /* Check if the Object is a mutant */
+                    if (CurrentObject->Header.Type == MutantObject)
+                    {
+                        /* Check if it's signaled */
+                        if ((CurrentObject->Header.SignalState > 0) ||
+                            (CurrentThread == 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,
+                                                    CurrentThread);
+                                WaitStatus = CurrentThread->WaitStatus |
+                                             WaitIndex;
+                                goto DontWait;
+                            }
+                            else
+                            {
+                                /* Raise an exception (see wasm.ru) */
+                                KeReleaseDispatcherDatabaseLock(CurrentThread->
+                                                                WaitIrql);
+                                ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
+                            }
+                        }
+                    }
+                    else if (CurrentObject->Header.SignalState > 0)
+                    {
+                        /* Another signaled object, unwait and return */
+                        KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
+                        WaitStatus = WaitIndex;
+                        goto DontWait;
+                    }
+                }
+                else
+                {
+                    /* Check if we're dealing with a mutant again */
+                    if (CurrentObject->Header.Type == MutantObject)
+                    {
+                        /* Check if it has an invalid count */
+                        if ((CurrentThread == CurrentObject->OwnerThread) &&
+                            (CurrentObject->Header.SignalState == MINLONG))
+                        {
+                            /* Raise an exception */
+                            KeReleaseDispatcherDatabaseLock(CurrentThread->
+                                                            WaitIrql);
+                            ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
+                        }
+                        else if ((CurrentObject->Header.SignalState <= 0) &&
+                                 (CurrentThread != 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;
+                    }
+                }
+
+                /* Set up a Wait Block for this Object */
+                WaitBlock->Object = CurrentObject;
+                WaitBlock->Thread = CurrentThread;
+                WaitBlock->WaitKey = (USHORT)WaitIndex;
+                WaitBlock->WaitType = (USHORT)WaitType;
+                WaitBlock->NextWaitBlock = WaitBlock + 1;
+
+                /* Move to the next Wait Block */
+                WaitBlock = WaitBlock->NextWaitBlock;
+            }
 
-NTSTATUS STDCALL
-KeWaitForMultipleObjects(ULONG Count,
-                         PVOID Object[],
-                         WAIT_TYPE WaitType,
-                         KWAIT_REASON WaitReason,
-                         KPROCESSOR_MODE WaitMode,
-                         BOOLEAN Alertable,
-                         PLARGE_INTEGER Timeout,
-                         PKWAIT_BLOCK WaitBlockArray)
-{
-   DISPATCHER_HEADER *hdr;
-   PKWAIT_BLOCK blk;
-   PKTHREAD CurrentThread;
-   ULONG CountSignaled;
-   ULONG i;
-   NTSTATUS Status;
-   KIRQL WaitIrql;
-   BOOLEAN Abandoned;
-
-   DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
-          "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
-
-   CurrentThread = KeGetCurrentThread();
-   WaitIrql = KeGetCurrentIrql();
-
-   /*
-    * Work out where we are going to put the wait blocks
-    */
-   if (WaitBlockArray == NULL)
-   {
-      if (Count > THREAD_WAIT_OBJECTS)
-      {
-         DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
-         return (STATUS_UNSUCCESSFUL);
-      }
-      WaitBlockArray = &CurrentThread->WaitBlock[0];
-   }
-   else
-   {
-      if (Count > EX_MAXIMUM_WAIT_OBJECTS)
-      {
-         DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
-         return (STATUS_UNSUCCESSFUL);
-      }
-   }
-
-   /*
-    * Set up the timeout if required
-    */
-   if (Timeout != NULL && Timeout->QuadPart != 0)
-   {
-      KeInitializeTimer(&CurrentThread->Timer);
-      KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
-   }
-
-   do
-   {
-      KeAcquireDispatcherDatabaseLock(FALSE);
-
-         /*
-       * If we are going to wait alertably and a user apc is pending
-       * then return
-       */
-      if (Alertable && KiTestAlert())
-      {
-         KeReleaseDispatcherDatabaseLock(FALSE);
-         return (STATUS_USER_APC);
-      }
-
-      /*
-       * Check if the wait is (already) satisfied
-       */
-      CountSignaled = 0;
-      Abandoned = FALSE;
-      for (i = 0; i < Count; i++)
-      {
-         hdr = (DISPATCHER_HEADER *) Object[i];
-
-         if (KiIsObjectSignalled(hdr, CurrentThread))
-         {
-            CountSignaled++;
-
-            if (WaitType == WaitAny)
+            /* 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))
             {
-               Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
+                /* Return to the Root Wait Block */
+                WaitBlock = CurrentThread->WaitBlockList;
 
-               if (Timeout != NULL && Timeout->QuadPart != 0)
-               {
-                  KeCancelTimer(&CurrentThread->Timer);
-               }
+                /* Satisfy their Waits and return to the caller */
+                KiWaitSatisfyAll(WaitBlock);
+                WaitStatus = CurrentThread->WaitStatus;
+                goto DontWait;
+            }
 
-               KeReleaseDispatcherDatabaseLock(FALSE);
+            /* Make sure we can satisfy the Alertable request */
+            KiCheckAlertability();
 
-               DPRINT("One object is (already) signaled!\n");
-               if (Abandoned == TRUE)
-               {
-                  return (STATUS_ABANDONED_WAIT_0 + i);
-               }
+            /* Check if we can swap the thread's stack */
+            CurrentThread->WaitListEntry.Flink = NULL;
+            KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
 
-               return (STATUS_WAIT_0 + i);
-            }
-         }
-      }
-
-      Abandoned = FALSE;
-      if ((WaitType == WaitAll) && (CountSignaled == Count))
-      {
-         for (i = 0; i < Count; i++)
-         {
-            hdr = (DISPATCHER_HEADER *) Object[i];
-            Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
-         }
-
-         if (Timeout != NULL && Timeout->QuadPart != 0)
-         {
-            KeCancelTimer(&CurrentThread->Timer);
-         }
-
-         KeReleaseDispatcherDatabaseLock(FALSE);
-         DPRINT("All objects are (already) signaled!\n");
-
-         if (Abandoned == TRUE)
-         {
-            return (STATUS_ABANDONED_WAIT_0);
-         }
-
-         return (STATUS_WAIT_0);
-      }
-
-      //zero timeout is used for testing if the object(s) can be immediately acquired
-      if (Timeout != NULL && Timeout->QuadPart == 0)
-      {
-         KeReleaseDispatcherDatabaseLock(FALSE);
-         return STATUS_TIMEOUT;
-      }
-
-      /*
-       * Check if we have already timed out
-       */
-      if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
-      {
-         KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
-         KeCancelTimer(&CurrentThread->Timer);
-         KeReleaseDispatcherDatabaseLock(FALSE);
-         return (STATUS_TIMEOUT);
-      }
-
-      /* Append wait block to the KTHREAD wait block list */
-      CurrentThread->WaitBlockList = blk = WaitBlockArray;
-
-      /*
-       * Set up the wait
-       */
-      CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
-
-      for (i = 0; i < Count; i++)
-      {
-         hdr = (DISPATCHER_HEADER *) Object[i];
-
-         blk->Object = Object[i];
-         blk->Thread = CurrentThread;
-         blk->WaitKey = STATUS_WAIT_0 + i;
-         blk->WaitType = WaitType;
-
-         if (i == (Count - 1))
-         {
-            if (Timeout != NULL)
+            /* Enable the Timeout Timer if there was any specified */
+            if (Timeout)
             {
-               blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
+                /* Make sure the timeout interval isn't actually 0 */
+                if (!Timeout->QuadPart)
+                {
+                    /* Return a timeout */
+                    WaitStatus = STATUS_TIMEOUT;
+                    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->NextWaitBlock = WaitBlockArray;
+
+                /* Initialize the list head */
+                InitializeListHead(&ThreadTimer->Header.WaitListHead);
+
+                /* Insert the Timer into the Timer Lists and enable it */
+                if (!KiInsertTimer(ThreadTimer, *Timeout))
+                {
+                    /* Return a timeout if we couldn't insert the timer */
+                    WaitStatus = STATUS_TIMEOUT;
+                    goto DontWait;
+                }
+
+                /* Set the current due time */
+                DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
             }
-            else
+
+            /* Insert into Object's Wait List*/
+            WaitBlock = CurrentThread->WaitBlockList;
+            do
             {
-               blk->NextWaitBlock = NULL;
-            }
-         }
-         else
-         {
-            blk->NextWaitBlock = blk + 1;
-         }
-
-         //add wait block to disp. obj. wait list
-         InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
-
-         blk = blk->NextWaitBlock;
-      }
-
-      if (Timeout != NULL)
-      {
-         CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
-         CurrentThread->WaitBlock[3].Thread = CurrentThread;
-         CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
-         CurrentThread->WaitBlock[3].WaitType = WaitAny;
-         CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
-
-         InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
-                        &CurrentThread->WaitBlock[3].WaitListEntry);
-      }
-
-      PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
-   }
-   while (Status == STATUS_KERNEL_APC);
-
-   if (Timeout != NULL)
-   {
-      KeCancelTimer(&CurrentThread->Timer);
-   }
-
-   DPRINT("Returning from KeWaitForMultipleObjects()\n");
-   return (Status);
-}
+                /* Get the Current Object */
+                CurrentObject = WaitBlock->Object;
 
-VOID KeInitializeDispatcher(VOID)
-{
-   KeInitializeSpinLock(&DispatcherDatabaseLock);
-}
+                /* Link the Object to this Wait Block */
+                InsertTailList(&CurrentObject->Header.WaitListHead,
+                               &WaitBlock->WaitListEntry);
 
-NTSTATUS STDCALL
-NtWaitForMultipleObjects(IN ULONG Count,
-                        IN HANDLE Object [],
-                        IN WAIT_TYPE WaitType,
-                        IN BOOLEAN Alertable,
-                        IN PLARGE_INTEGER Time)
-{
-   KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
-   PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
-   NTSTATUS Status;
-   ULONG i, j;
-   KPROCESSOR_MODE WaitMode;
-
-   DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
-         "Time %x)\n", Count,Object,Alertable,Time);
-
-   if (Count > EX_MAXIMUM_WAIT_OBJECTS)
-     return STATUS_UNSUCCESSFUL;
-
-   WaitMode = ExGetPreviousMode();
-
-   /* reference all objects */
-   for (i = 0; i < Count; i++)
-     {
-        Status = ObReferenceObjectByHandle(Object[i],
-                                           SYNCHRONIZE,
-                                           NULL,
-                                           WaitMode,
-                                           &ObjectPtrArray[i],
-                                           NULL);
-        if (Status != STATUS_SUCCESS)
-          {
-             /* dereference all referenced objects */
-             for (j = 0; j < i; j++)
-               {
-                  ObDereferenceObject(ObjectPtrArray[j]);
-               }
-
-             return(Status);
-          }
-     }
-
-   Status = KeWaitForMultipleObjects(Count,
-                                     ObjectPtrArray,
-                                     WaitType,
-                                     UserRequest,
-                                     WaitMode,
-                                     Alertable,
-                                     Time,
-                                     WaitBlockArray);
-
-   /* dereference all objects */
-   for (i = 0; i < Count; i++)
-     {
-        ObDereferenceObject(ObjectPtrArray[i]);
-     }
-
-   return(Status);
-}
+                /* Move to the next Wait Block */
+                WaitBlock = WaitBlock->NextWaitBlock;
+            } while (WaitBlock != WaitBlockArray);
 
+            /* Handle Kernel Queues */
+            if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
 
-NTSTATUS STDCALL
-NtWaitForSingleObject(IN HANDLE Object,
-                     IN BOOLEAN Alertable,
-                     IN PLARGE_INTEGER Time)
-{
-   PVOID ObjectPtr;
-   NTSTATUS Status;
-   KPROCESSOR_MODE WaitMode;
-
-   DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
-         Object,Alertable,Time);
-
-   WaitMode = ExGetPreviousMode();
-
-   Status = ObReferenceObjectByHandle(Object,
-                                     SYNCHRONIZE,
-                                     NULL,
-                                     WaitMode,
-                                     &ObjectPtr,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-
-   Status = KeWaitForSingleObject(ObjectPtr,
-                                 UserRequest,
-                                 WaitMode,
-                                 Alertable,
-                                 Time);
-
-   ObDereferenceObject(ObjectPtr);
-
-   return(Status);
-}
+            /* Setup the wait information */
+            CurrentThread->Alertable = Alertable;
+            CurrentThread->WaitMode = WaitMode;
+            CurrentThread->WaitReason = WaitReason;
+            CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
+            CurrentThread->State = Waiting;
 
+            /* Find a new thread to run */
+            KiAddThreadToWaitList(CurrentThread, Swappable);
+            WaitStatus = KiSwapThread();
+            ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
 
-NTSTATUS STDCALL
-NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
-                              IN HANDLE WaitObject,
-                              IN BOOLEAN Alertable,
-                              IN PLARGE_INTEGER Time)
-{
-   KPROCESSOR_MODE WaitMode;
-   DISPATCHER_HEADER* hdr;
-   PVOID SignalObj;
-   PVOID WaitObj;
-   NTSTATUS Status;
-
-   WaitMode = ExGetPreviousMode();
-   Status = ObReferenceObjectByHandle(SignalObject,
-                                     0,
-                                     NULL,
-                                     WaitMode,
-                                     &SignalObj,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       return Status;
-     }
-
-   Status = ObReferenceObjectByHandle(WaitObject,
-                                     SYNCHRONIZE,
-                                     NULL,
-                                     WaitMode,
-                                     &WaitObj,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       ObDereferenceObject(SignalObj);
-       return Status;
-     }
-
-   hdr = (DISPATCHER_HEADER *)SignalObj;
-   switch (hdr->Type)
-     {
-      case InternalNotificationEvent:
-      case InternalSynchronizationEvent:
-       KeSetEvent(SignalObj,
-                  EVENT_INCREMENT,
-                  TRUE);
-       break;
-
-      case InternalMutexType:
-       KeReleaseMutex(SignalObj,
-                      TRUE);
-       break;
-
-      case InternalSemaphoreType:
-       KeReleaseSemaphore(SignalObj,
-                          SEMAPHORE_INCREMENT,
-                          1,
-                          TRUE);
-       break;
-
-      default:
-       ObDereferenceObject(SignalObj);
-       ObDereferenceObject(WaitObj);
-       return STATUS_OBJECT_TYPE_MISMATCH;
-     }
-
-   Status = KeWaitForSingleObject(WaitObj,
-                                 UserRequest,
-                                 WaitMode,
-                                 Alertable,
-                                 Time);
-
-   ObDereferenceObject(SignalObj);
-   ObDereferenceObject(WaitObj);
-
-   return Status;
+            /* 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);
+            }
+
+            /* Acquire again the lock */
+            CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+        }
+    } while (TRUE);
+
+    /* Release the Lock, we are done */
+    KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+    return WaitStatus;
+
+DontWait:
+    /* Adjust the Quantum */
+    KiAdjustQuantumThread(CurrentThread);
+
+    /* Release & Return */
+    KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+    return WaitStatus;
 }
+
+/* EOF */