/*
- * 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;
-static BOOLEAN WaitSet = FALSE;
-static KIRQL oldlvl = PASSIVE_LEVEL;
-static PKTHREAD Owner = NULL;
+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)
+ /* 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) KxRemoveTreeTimer(Timer);
+
+ /* Increment the Queue's active threads */
+ if (Thread->Queue) Thread->Queue->CurrentCount++;
+}
+
+/* Must be called with the dispatcher lock held */
+VOID
+FASTCALL
+KiUnwaitThread(IN PKTHREAD Thread,
+ IN NTSTATUS WaitStatus,
+ IN KPRIORITY Increment)
{
- Header->Type = Type;
- Header->Absolute = 0;
- Header->Inserted = 0;
- Header->Size = Size;
- Header->SignalState = SignalState;
- InitializeListHead(&(Header->WaitListHead));
+ /* Unlink the thread */
+ KiUnlinkThread(Thread, WaitStatus);
+
+ /* 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 KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
-/*
- * PURPOSE: Acquires the dispatcher database lock for the caller
- */
+VOID
+FASTCALL
+KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
{
- DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
- if (WaitSet && Owner == KeGetCurrentThread())
- {
- return;
- }
- KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
- WaitSet = Wait;
- Owner = KeGetCurrentThread();
+ /* Increase contention count */
+ FastMutex->Contention++;
+
+ /* Wait for the event */
+ KeWaitForSingleObject(&FastMutex->Gate,
+ WrMutex,
+ KernelMode,
+ FALSE,
+ NULL);
}
-VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
+//
+// This routine exits the dispatcher after a compatible operation and
+// swaps the context to the next scheduled thread on the current CPU if
+// one is available.
+//
+// It does NOT attempt to scan for a new thread to schedule.
+//
+VOID
+FASTCALL
+KiExitDispatcher(IN KIRQL OldIrql)
{
- DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x)\n", Wait);
- assert(Wait == WaitSet);
- if (!Wait)
+ 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)
{
- Owner = NULL;
- KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
+ /* 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;
}
-}
-VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
-{
- DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
- assert(Wait==WaitSet);
- if (!Wait)
- {
- Owner = NULL;
- KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
- }
+ /* 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);
}
-static BOOLEAN
-KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
- PKTHREAD Thread)
+/* PUBLIC FUNCTIONS **********************************************************/
+
/*
- * 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)
-{
- 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);
+ /* 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);
- }
+ 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)
+ {
+ /* Another satisfied object */
+ KiSatisfyNonMutantWait(CurrentObject);
+ WaitStatus = STATUS_WAIT_0;
+ goto DontWait;
+ }
+
+ /* Make sure we can satisfy the Alertable request */
+ WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
+ if (WaitStatus != STATUS_WAIT_0) break;
- //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)
+ /* Enable the Timeout Timer if there was any specified */
+ if (Timeout)
{
- if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
- {
- AllSignaled = FALSE;
- break;
- }
+ /* Check if the timer expired */
+ InterruptTime.QuadPart = KeQueryInterruptTime();
+ if ((ULONGLONG)InterruptTime.QuadPart >=
+ Timer->DueTime.QuadPart)
+ {
+ /* It did, so we don't need to wait */
+ WaitStatus = STATUS_TIMEOUT;
+ goto DontWait;
+ }
+
+ /* It didn't, so activate it */
+ Timer->Header.Inserted = TRUE;
}
- }
- if (AllSignaled)
- {
- for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
+ /* Link the Object to this Wait Block */
+ InsertTailList(&CurrentObject->Header.WaitListHead,
+ &WaitBlock->WaitListEntry);
+
+ /* Handle Kernel Queues */
+ if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
+
+ /* 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)
+ {
+ /* Insert it */
+ KxInsertTimer(Timer, Hand);
+ }
+ else
{
- RemoveEntryList(&Waiter->WaitListEntry);
+ /* Otherwise, unlock the dispatcher */
+ KiReleaseDispatcherLockFromDpcLevel();
+ }
+
+ /* Do the actual swap */
+ WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
- if (Waiter->WaitType == WaitAll)
- {
- Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
- ? TRUE : Abandoned;
- }
+ /* Check if we were executing an APC */
+ if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
- //no WaitAny objects can possibly be signaled since we are here
- assert(!(Waiter->WaitType == WaitAny
- && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
+ /* 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();
+ }
- 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);
- }
- }
+ /* Wait complete */
+ KiReleaseDispatcherLock(Thread->WaitIrql);
+ return WaitStatus;
- return WakedAny;
-}
+DontWait:
+ /* Release dispatcher lock but maintain high IRQL */
+ KiReleaseDispatcherLockFromDpcLevel();
+ /* Adjust the Quantum and return the wait status */
+ KiAdjustQuantumThread(Thread);
+ 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
+ * @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)
{
- 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);
-}
+ 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);
-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 the lock is already held */
+ if (!Thread->WaitNext) goto WaitStart;
+ /* Otherwise, we already have the lock, so initialize the wait */
+ Thread->WaitNext = FALSE;
+ KxMultiThreadWait();
-inline
-PVOID
-KiGetWaitableObjectFromObject(PVOID Object)
-{
- //special case when waiting on file objects
- if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
- {
- return &((PFILE_OBJECT)Object)->Event;
- }
+ /* 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;
+ }
+ }
- return Object;
-}
+ /* Make sure we can satisfy the Alertable request */
+ WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
+ if (WaitStatus != STATUS_WAIT_0) break;
+ /* Enable the Timeout Timer if there was any specified */
+ if (Timeout)
+ {
+ /* Check if the timer expired */
+ InterruptTime.QuadPart = KeQueryInterruptTime();
+ if ((ULONGLONG)InterruptTime.QuadPart >=
+ Timer->DueTime.QuadPart)
+ {
+ /* It did, so we don't need to wait */
+ WaitStatus = STATUS_TIMEOUT;
+ goto DontWait;
+ }
+
+ /* It didn't, so activate it */
+ Timer->Header.Inserted = TRUE;
+
+ /* Link the wait blocks */
+ WaitBlock->NextWaitBlock = TimerBlock;
+ }
-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);
- }
- }
+ /* Insert into Object's Wait List*/
+ WaitBlock = WaitBlockArray;
+ do
+ {
+ /* Get the Current Object */
+ CurrentObject = WaitBlock->Object;
- /*
- * Set up the timeout if required
- */
- if (Timeout != NULL && Timeout->QuadPart != 0)
- {
- KeInitializeTimer(&CurrentThread->Timer);
- KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
- }
+ /* Link the Object to this Wait Block */
+ InsertTailList(&CurrentObject->Header.WaitListHead,
+ &WaitBlock->WaitListEntry);
- 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 *) KiGetWaitableObjectFromObject(Object[i]);
-
- if (KiIsObjectSignalled(hdr, CurrentThread))
- {
- CountSignaled++;
+ /* Move to the next Wait Block */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ } while (WaitBlock != WaitBlockArray);
- if (WaitType == WaitAny)
- {
- Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
+ /* Handle Kernel Queues */
+ if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
- if (Timeout != NULL && Timeout->QuadPart != 0)
- {
- KeCancelTimer(&CurrentThread->Timer);
- }
+ /* Setup the wait information */
+ Thread->State = Waiting;
- KeReleaseDispatcherDatabaseLock(FALSE);
+ /* Add the thread to the wait list */
+ KiAddThreadToWaitList(Thread, Swappable);
- DPRINT("One object is (already) signaled!\n");
- if (Abandoned == TRUE)
- {
- return (STATUS_ABANDONED_WAIT_0 + i);
- }
+ /* Activate thread swap */
+ ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
+ KiSetThreadSwapBusy(Thread);
- 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(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 *) 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)
+ /* 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->RunningThreads--;
- if (WaitReason != WrQueue && CurrentThread->Queue->RunningThreads < CurrentThread->Queue->MaximumThreads &&
- !IsListEmpty(&CurrentThread->Queue->EntryListHead))
- {
- KeDispatcherObjectWake(&CurrentThread->Queue->Header);
- }
- }
-
- PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
-
- //io completion
- if (CurrentThread->Queue)
- {
- CurrentThread->Queue->RunningThreads++;
- }
+ /* Swap the thread */
+ WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
- }
- while (Status == STATUS_KERNEL_APC);
+ /* Check if we were executing an APC */
+ if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
- if (Timeout != NULL)
- {
- KeCancelTimer(&CurrentThread->Timer);
- }
+ /* Check if we had a timeout */
+ if (Timeout)
+ {
+ /* Recalculate due times */
+ Timeout = KiRecalculateDueTime(OriginalDueTime,
+ &DueTime,
+ &NewDueTime);
+ }
+ }
- DPRINT("Returning from KeWaitForMultipleObjects()\n");
- return (Status);
-}
+WaitStart:
+ /* Setup a new wait */
+ Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
+ KxMultiThreadWait();
+ KiAcquireDispatcherLockAtDpcLevel();
+ }
-VOID KeInitializeDispatcher(VOID)
-{
- KeInitializeSpinLock(&DispatcherDatabaseLock);
-}
+ /* We are done */
+ KiReleaseDispatcherLock(Thread->WaitIrql);
+ 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);
-}
+DontWait:
+ /* Release dispatcher lock but maintain high IRQL */
+ KiReleaseDispatcherLockFromDpcLevel();
+ /* Adjust the Quantum and return the wait status */
+ KiAdjustQuantumThread(Thread);
+ return WaitStatus;
+}
-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 */