- Rewrite kernel timer implementation to use Windows 2003's hash-based table timer...
[reactos.git] / reactos / ntoskrnl / ke / wait.c
index a6fa042..dabe787 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>
+#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;
+
+    /* 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;
+
+        /* Check the current Wait Mode */
+        if (WaitBlock->WaitType == WaitAny)
+        {
+            /* Easy case, satisfy only this wait */
+            WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
+            KiSatisfyObjectWait(FirstObject, WaitThread);
+        }
+
+        /* Now do the rest of the unwait */
+        KiUnwaitThread(WaitThread, WaitStatus, Increment);
+        WaitEntry = WaitList->Flink;
+    }
+}
 
-#define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
-#define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
+VOID
+FASTCALL
+KiUnlinkThread(IN PKTHREAD Thread,
+               IN NTSTATUS WaitStatus)
+{
+    PKWAIT_BLOCK WaitBlock;
+    PKTIMER Timer;
 
-/* FUNCTIONS *****************************************************************/
+    /* Update wait status */
+    Thread->WaitStatus |= WaitStatus;
 
-VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
-                                 ULONG Type,
-                                 ULONG Size,
-                                 ULONG SignalState)
-{
-   Header->Type = Type;
-   Header->Absolute = 0;
-   Header->Inserted = 0;
-   Header->Size = Size;
-   Header->SignalState = SignalState;
-   InitializeListHead(&(Header->WaitListHead));
-}
+    /* 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);
 
-KIRQL
-KeAcquireDispatcherDatabaseLock(VOID)
-/*
- * PURPOSE: Acquires the dispatcher database lock for the caller
- */
-{
-   KIRQL OldIrql;
+    /* Remove the thread from the wait list! */
+    if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
 
-   DPRINT("KeAcquireDispatcherDatabaseLock()\n");
+    /* Check if there's a Thread Timer */
+    Timer = &Thread->Timer;
+    if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
 
-   KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
-   return OldIrql;
+    /* Increment the Queue's active threads */
+    if (Thread->Queue) Thread->Queue->CurrentCount++;
 }
 
-
+/* Must be called with the dispatcher lock held */
 VOID
-KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
-/*
- * PURPOSE: Acquires the dispatcher database lock for the caller
- */
+FASTCALL
+KiUnwaitThread(IN PKTHREAD Thread,
+               IN NTSTATUS WaitStatus,
+               IN KPRIORITY Increment)
 {
-   DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
+    /* Unlink the thread */
+    KiUnlinkThread(Thread, WaitStatus);
 
-   KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
-}
+    /* Tell the scheduler do to the increment when it readies the thread */
+    ASSERT(Increment >= 0);
+    Thread->AdjustIncrement = (SCHAR)Increment;
+    Thread->AdjustReason = AdjustUnwait;
 
+    /* Reschedule the Thread */
+    KiReadyThread(Thread);
+}
 
 VOID
-KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
+FASTCALL
+KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
 {
-  DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql);
-
-  KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
+    /* Increase contention count */
+    FastMutex->Contention++;
+
+    /* Wait for the event */
+    KeWaitForSingleObject(&FastMutex->Gate,
+                          WrMutex,
+                          KernelMode,
+                          FALSE,
+                          NULL);
 }
 
-
+//
+// 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
-KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
+FASTCALL
+KiExitDispatcher(IN KIRQL OldIrql)
 {
-  DPRINT("KeReleaseDispatcherDatabaseLock()\n");
-
-  KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
+    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);
+        }
+
+        /* Lower IRQL and exit */
+        goto Quickie;
+    }
+
+    /* Make sure there's a new thread scheduled */
+    if (!Prcb->NextThread) goto Quickie;
+
+    /* Lock the PRCB */
+    KiAcquirePrcbLock(Prcb);
+
+    /* Get the next and current threads now */
+    NextThread = Prcb->NextThread;
+    Thread = Prcb->CurrentThread;
+
+    /* Set current thread's swap busy to true */
+    KiSetThreadSwapBusy(Thread);
+
+    /* Switch threads in PRCB */
+    Prcb->NextThread = NULL;
+    Prcb->CurrentThread = NextThread;
+
+    /* Set thread to running */
+    NextThread->State = Running;
+
+    /* Queue it on the ready lists */
+    KxQueueReadyThread(Thread, Prcb);
+
+    /* Set wait IRQL */
+    Thread->WaitIrql = OldIrql;
+
+    /* Swap threads and check if APCs were pending */
+    PendingApc = KiSwapContext(Thread, NextThread);
+    if (PendingApc)
+    {
+        /* Lower only to APC */
+        KeLowerIrql(APC_LEVEL);
+
+        /* Deliver APCs */
+        KiDeliverApc(KernelMode, NULL, NULL);
+        ASSERT(OldIrql == PASSIVE_LEVEL);
+    }
+
+    /* Lower IRQl back */
+Quickie:
+    KeLowerIrql(OldIrql);
 }
 
+/* PUBLIC FUNCTIONS **********************************************************/
 
-static BOOLEAN
-KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
-                        PKTHREAD Thread)
 /*
- * FUNCTION: Perform side effects on object before a wait for a thread is
- *           satisfied
+ * @implemented
  */
+NTSTATUS
+NTAPI
+KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
+                       IN BOOLEAN Alertable,
+                       IN PLARGE_INTEGER Interval OPTIONAL)
 {
-   BOOLEAN Abandoned = FALSE;
-
-   switch (hdr->Type)
-   {
-      case InternalSynchronizationEvent:
-         hdr->SignalState = 0;
-         break;
-      
-      case InternalQueueType:
-      case InternalSemaphoreType:
-         hdr->SignalState--;
-         break;
-
-      case InternalProcessType:
-         break;
-
-      case InternalThreadType:
-         break;
-
-      case InternalNotificationEvent:
-         break;
-
-      case InternalSynchronizationTimer:
-         hdr->SignalState = FALSE;
-         break;
-
-      case InternalNotificationTimer:
-         break;
-
-      case InternalMutexType:
-      {
-         PKMUTEX Mutex;
-
-         Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
-         hdr->SignalState--;
-         assert(hdr->SignalState <= 1);
-         if (hdr->SignalState == 0)
-         {
-            if (Thread == NULL)
+    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();
+        }
+    }
+
+    /* 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 (!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)
             {
-               DPRINT("Thread == NULL!\n");
-               KEBUGCHECK(0);
+                /* It did, so we don't need to wait */
+                goto NoWait;
             }
-            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;
-}
+            /* It didn't, so activate it */
+            Timer->Header.Inserted = TRUE;
 
-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);
-      }
-   }
+            /* Handle Kernel Queues */
+            if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
 
-   if (hdr->SignalState <= 0)
-   {
-      return (FALSE);
-   }
-   else
-   {
-      return (TRUE);
-   }
-}
+            /* Setup the wait information */
+            Thread->State = Waiting;
 
-VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus, BOOL Unblock)
-{
-   PKWAIT_BLOCK WaitBlock, PrevWaitBlock;
-   BOOLEAN WasWaiting = FALSE;
-   KIRQL OldIrql;
-
-   OldIrql = KeAcquireDispatcherDatabaseLock ();
-
-   WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
-   if (WaitBlock != NULL)
-     {
-       WasWaiting = TRUE;
-     }
-   while (WaitBlock != NULL)
-     {
-       if (WaitBlock->WaitListEntry.Flink != NULL && WaitBlock->WaitListEntry.Blink != NULL)
-         { 
-           RemoveEntryList (&WaitBlock->WaitListEntry);
-            WaitBlock->WaitListEntry.Flink = WaitBlock->WaitListEntry.Blink = NULL;
-         }
-       PrevWaitBlock = WaitBlock;
-       WaitBlock = WaitBlock->NextWaitBlock;
-       PrevWaitBlock->NextWaitBlock = NULL;
-     }
-   Thread->Tcb.WaitBlockList = NULL;
-
-   if (WasWaiting && Unblock)
-     {
-       PsUnblockThread(Thread, &WaitStatus);
-     }
-
-   KeReleaseDispatcherDatabaseLock (OldIrql);
+            /* Add the thread to the wait list */
+            KiAddThreadToWaitList(Thread, Swappable);
+
+            /* Insert the timer and swap the thread */
+            ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+            KiSetThreadSwapBusy(Thread);
+            KxInsertTimer(Timer, Hand);
+            WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
+
+            /* Check if were swapped ok */
+            if (WaitStatus != STATUS_KERNEL_APC)
+            {
+                /* This is a good thing */
+                if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
+
+                /* Return Status */
+                return WaitStatus;
+            }
+
+            /* Recalculate due times */
+            Interval = KiRecalculateDueTime(OriginalDueTime,
+                                            &DueTime,
+                                            &NewDueTime);
+        }
+
+WaitStart:
+        /* Setup a new wait */
+        Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
+        KxDelayThreadWait();
+        KiAcquireDispatcherLockAtDpcLevel();
+    }
+
+    /* We're done! */
+    KiReleaseDispatcherLock(Thread->WaitIrql);
+    return WaitStatus;
+
+NoWait:
+    /* There was nothing to wait for. Did we have a wait interval? */
+    if (!Interval->QuadPart)
+    {
+        /* Unlock the dispatcher and do a yield */
+        KiReleaseDispatcherLock(Thread->WaitIrql);
+        return NtYieldExecution();
+    }
+
+    /* Unlock the dispatcher and adjust the quantum for a no-wait */
+    KiReleaseDispatcherLockFromDpcLevel();
+    KiAdjustQuantumThread(Thread);
+    return STATUS_SUCCESS;
 }
 
-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 != NULL);
-      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)
-         {
-            if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
-             {
-               RemoveEntryList(&Waiter->WaitListEntry);
-               Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
-             }
-         }
-
-         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)
+    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;
+
+    /* 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);
+                   }
+                }
+            }
+            else if (CurrentObject->Header.SignalState > 0)
             {
-               if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
-               {
-                  AllSignaled = FALSE;
-                  break;
-               }
+                /* Another satisfied object */
+                KiSatisfyNonMutantWait(CurrentObject);
+                WaitStatus = STATUS_WAIT_0;
+                goto DontWait;
             }
-         }
 
-         if (AllSignaled)
-         {
-            for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
+            /* 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)
             {
-               if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
-              {
-                 RemoveEntryList(&Waiter->WaitListEntry);
-                 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
-              }
-         
-               if (Waiter->WaitType == WaitAll)
-               {
-                  Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
-                     ? TRUE : Abandoned;
-               }
-
-               //no WaitAny objects can possibly be signaled since we are here
-               assert(!(Waiter->WaitType == WaitAny
-                                         && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
+                /* 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;
+                }
+
+                /* It didn't, so activate it */
+                Timer->Header.Inserted = TRUE;
             }
 
-            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);
-      }
-   }
+            /* Link the Object to this Wait Block */
+            InsertTailList(&CurrentObject->Header.WaitListHead,
+                           &WaitBlock->WaitListEntry);
 
-   return WakedAny;
-}
+            /* Handle Kernel Queues */
+            if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
 
+            /* Setup the wait information */
+            Thread->State = Waiting;
 
-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 InternalQueueType:
-      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);
-}
+            /* Add the thread to the wait list */
+            KiAddThreadToWaitList(Thread, Swappable);
 
+            /* Activate thread swap */
+            ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+            KiSetThreadSwapBusy(Thread);
 
-/*
- * @implemented
- */
-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
- */
-{
-   return KeWaitForMultipleObjects(1,
-                                   &Object,
-                                   WaitAny,
-                                   WaitReason,
-                                   WaitMode,
-                                   Alertable,
-                                   Timeout,
-                                   NULL);
-}
+            /* Check if we have a timer */
+            if (Timeout)
+            {
+                /* Insert it */
+                KxInsertTimer(Timer, Hand);
+            }
+            else
+            {
+                /* Otherwise, unlock the dispatcher */
+                KiReleaseDispatcherLockFromDpcLevel();
+            }
 
+            /* Do the actual swap */
+            WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
 
-inline
-PVOID
-KiGetWaitableObjectFromObject(PVOID Object)
-{
-   //special case when waiting on file objects
-   if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
-   {
-      return &((PFILE_OBJECT)Object)->Event;
-   }
+            /* Check if we were executing an APC */
+            if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
 
-   return Object;
+            /* 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();
+    }
+
+    /* Wait complete */
+    KiReleaseDispatcherLock(Thread->WaitIrql);
+    return WaitStatus;
+
+DontWait:
+    /* Release dispatcher lock but maintain high IRQL */
+    KiReleaseDispatcherLockFromDpcLevel();
+
+    /* 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)
 {
-   DISPATCHER_HEADER *hdr;
-   PKWAIT_BLOCK blk;
-   PKTHREAD CurrentThread;
-   ULONG CountSignaled;
-   ULONG i;
-   NTSTATUS Status;
-   KIRQL WaitIrql;
-   KIRQL OldIrql;
-   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);
-      }
-   }
+    PKMUTANT CurrentObject;
+    PKWAIT_BLOCK WaitBlock;
+    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)
+        {
+            /* Bugcheck */
+            KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
+        }
+
+        /* Use the Thread's Wait Block */
+        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);
+        }
+    }
+
+    /* Sanity check */
+    ASSERT(Count != 0);
+
+    /* 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;
+    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;
+                    }
+
+                    /* 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 == 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;
+                    }
+
+                    /* 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;
+                }
+            }
 
-   /*
-    * Set up the timeout if required
-    */
-   if (Timeout != NULL && Timeout->QuadPart != 0)
-   {
-      KeInitializeTimer(&CurrentThread->Timer);
-      KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
-   }
+            /* Make sure we can satisfy the Alertable request */
+            WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
+            if (WaitStatus != STATUS_WAIT_0) break;
 
-   do
-   {
-      if (CurrentThread->WaitNext)
-      {
-         OldIrql = CurrentThread->WaitIrql;
-         CurrentThread->WaitNext = 0;
-         CurrentThread->WaitIrql = PASSIVE_LEVEL;
-      }
-      else
-      {
-         OldIrql = KeAcquireDispatcherDatabaseLock ();
-      }
-
-      /*
-       * If we are going to wait alertably and a user apc is pending
-       * then return
-       */
-      if (Alertable && KiTestAlert())
-      {
-         KeReleaseDispatcherDatabaseLock(OldIrql);
-         return (STATUS_USER_APC);
-      }
-
-      /*
-       * Check if the wait is (already) satisfied
-       */
-      CountSignaled = 0;
-      Abandoned = FALSE;
-      for (i = 0; i < Count; i++)
-      {
-         hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
-
-         if (KiIsObjectSignalled(hdr, CurrentThread))
-         {
-            CountSignaled++;
+            /* 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;
+                }
+
+                /* It didn't, so activate it */
+                Timer->Header.Inserted = TRUE;
+
+                /* Link the wait blocks */
+                WaitBlock->NextWaitBlock = TimerBlock;
+            }
 
-            if (WaitType == WaitAny)
+            /* Insert into Object's Wait List*/
+            WaitBlock = WaitBlockArray;
+            do
             {
-               Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
+                /* Get the Current Object */
+                CurrentObject = WaitBlock->Object;
 
-               if (Timeout != NULL && Timeout->QuadPart != 0)
-               {
-                  KeCancelTimer(&CurrentThread->Timer);
-               }
+                /* Link the Object to this Wait Block */
+                InsertTailList(&CurrentObject->Header.WaitListHead,
+                               &WaitBlock->WaitListEntry);
 
-               KeReleaseDispatcherDatabaseLock(OldIrql);
+                /* Move to the next Wait Block */
+                WaitBlock = WaitBlock->NextWaitBlock;
+            } while (WaitBlock != WaitBlockArray);
 
-               DPRINT("One object is (already) signaled!\n");
-               if (Abandoned == TRUE)
-               {
-                  return (STATUS_ABANDONED_WAIT_0 + i);
-               }
+            /* Handle Kernel Queues */
+            if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
 
-               return (STATUS_WAIT_0 + i);
-            }
-         }
-      }
-
-      Abandoned = FALSE;
-      if ((WaitType == WaitAll) && (CountSignaled == Count))
-      {
-         for (i = 0; i < Count; i++)
-         {
-            hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
-            Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
-         }
-
-         if (Timeout != NULL && Timeout->QuadPart != 0)
-         {
-            KeCancelTimer(&CurrentThread->Timer);
-         }
-
-         KeReleaseDispatcherDatabaseLock(OldIrql);
-         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(OldIrql);
-         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(OldIrql);
-         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 *) KiGetWaitableObjectFromObject(Object[i]);
-
-         blk->Object = KiGetWaitableObjectFromObject(Object[i]);
-         blk->Thread = CurrentThread;
-         blk->WaitKey = STATUS_WAIT_0 + i;
-         blk->WaitType = WaitType;
-
-         if (i == (Count - 1))
-         {
-            if (Timeout != NULL)
+            /* Setup the wait information */
+            Thread->State = Waiting;
+
+            /* Add the thread to the wait list */
+            KiAddThreadToWaitList(Thread, Swappable);
+
+            /* Activate thread swap */
+            ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+            KiSetThreadSwapBusy(Thread);
+
+            /* Check if we have a timer */
+            if (Timeout)
             {
-               blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
+                /* Insert it */
+                KxInsertTimer(Timer, Hand);
             }
             else
             {
-               blk->NextWaitBlock = NULL;
+                /* Otherwise, unlock the dispatcher */
+                KiReleaseDispatcherLockFromDpcLevel();
             }
-         }
-         else
-         {
-            blk->NextWaitBlock = blk + 1;
-         }
-
-         /*
-          * add wait block to disp. obj. wait list
-          * Use FIFO for all waits except for queues which use LIFO
-          */
-         if (WaitReason == WrQueue)
-         {
-            InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
-         }
-         else
-         {
-            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);
-      }
-
-      //io completion
-      if (CurrentThread->Queue)
-      {
-         CurrentThread->Queue->CurrentCount--;   
-         if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
-             !IsListEmpty(&CurrentThread->Queue->EntryListHead))
-         {
-            KeDispatcherObjectWake(&CurrentThread->Queue->Header);
-         }
-      }
-
-      PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
-
-      //io completion
-      if (CurrentThread->Queue)
-      {
-         CurrentThread->Queue->CurrentCount++;
-      }
-
 
-   }
-   while (Status == STATUS_KERNEL_APC);
+            /* Swap the thread */
+            WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
 
-   if (Timeout != NULL)
-   {
-      KeCancelTimer(&CurrentThread->Timer);
-   }
-
-   DPRINT("Returning from KeWaitForMultipleObjects()\n");
-   return (Status);
-}
-
-VOID KeInitializeDispatcher(VOID)
-{
-   KeInitializeSpinLock(&DispatcherDatabaseLock);
-}
+            /* Check if we were executing an APC */
+            if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
 
-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);
+            /* Check if we had a timeout */
+            if (Timeout)
+            {
+                /* Recalculate due times */
+                Timeout = KiRecalculateDueTime(OriginalDueTime,
+                                               &DueTime,
+                                               &NewDueTime);
+            }
+        }
+
+WaitStart:
+        /* Setup a new wait */
+        Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
+        KxMultiThreadWait();
+        KiAcquireDispatcherLockAtDpcLevel();
+    }
+
+    /* We are done */
+    KiReleaseDispatcherLock(Thread->WaitIrql);
+    return WaitStatus;
+
+DontWait:
+    /* Release dispatcher lock but maintain high IRQL */
+    KiReleaseDispatcherLockFromDpcLevel();
+
+    /* Adjust the Quantum and return the wait status */
+    KiAdjustQuantumThread(Thread);
+    return WaitStatus;
 }
 
-
-/*
- * @implemented
- */
-NTSTATUS STDCALL
-NtWaitForSingleObject(IN HANDLE Object,
-                     IN BOOLEAN Alertable,
-                     IN PLARGE_INTEGER Time)
+NTSTATUS
+NTAPI
+NtDelayExecution(IN BOOLEAN Alertable,
+                 IN PLARGE_INTEGER DelayInterval)
 {
-   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);
-}
-
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    LARGE_INTEGER SafeInterval;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* Check the previous mode */
+    if(PreviousMode != KernelMode)
+    {
+        /* Enter SEH for probing */
+        _SEH_TRY
+        {
+            /* Probe and capture the time out */
+            SafeInterval = ProbeForReadLargeInteger(DelayInterval);
+            DelayInterval = &SafeInterval;
+        }
+        _SEH_HANDLE
+        {
+            /* Get SEH exception */
+            Status = _SEH_GetExceptionCode();
+        }
+        _SEH_END;
+        if (!NT_SUCCESS(Status)) return Status;
+   }
 
-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);
+   /* Call the Kernel Function */
+   Status = KeDelayExecutionThread(PreviousMode,
+                                   Alertable,
+                                   DelayInterval);
 
+   /* Return Status */
    return Status;
 }
+
+/* EOF */