/* FUNCTIONS *****************************************************************/
-
+LONG STDCALL KiInsertQueue(IN PKQUEUE Queue, IN PLIST_ENTRY Entry, BOOLEAN Head);
+
/*
* @implemented
*/
-VOID STDCALL
+VOID
+STDCALL
KeInitializeQueue(IN PKQUEUE Queue,
- IN ULONG Count OPTIONAL)
+ IN ULONG Count OPTIONAL)
{
- KeInitializeDispatcherHeader(&Queue->Header,
- QueueObject,
- sizeof(KQUEUE)/sizeof(ULONG),
- 0);
- InitializeListHead(&Queue->EntryListHead);
- InitializeListHead(&Queue->ThreadListHead);
- Queue->CurrentCount = 0;
- Queue->MaximumCount = (Count == 0) ? (ULONG) KeNumberProcessors : Count;
+ DPRINT("KeInitializeQueue %x\n", Queue);
+
+ /* Initialize the Header */
+ KeInitializeDispatcherHeader(&Queue->Header,
+ QueueObject,
+ sizeof(KQUEUE)/sizeof(ULONG),
+ 0);
+
+ /* Initialize the Lists */
+ InitializeListHead(&Queue->EntryListHead);
+ InitializeListHead(&Queue->ThreadListHead);
+
+ /* Set the Current and Maximum Count */
+ Queue->CurrentCount = 0;
+ Queue->MaximumCount = (Count == 0) ? (ULONG) KeNumberProcessors : Count;
}
-
/*
* @implemented
- *
- * Returns number of entries in the queue
- */
-LONG STDCALL
-KeReadStateQueue(IN PKQUEUE Queue)
-{
- return(Queue->Header.SignalState);
-}
-
-/*
- * Returns the previous number of entries in the queue
*/
-LONG STDCALL
-KiInsertQueue(
- IN PKQUEUE Queue,
- IN PLIST_ENTRY Entry,
- BOOLEAN Head
- )
+LONG
+STDCALL
+KeInsertHeadQueue(IN PKQUEUE Queue,
+ IN PLIST_ENTRY Entry)
{
- ULONG InitialState;
-
- DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry);
+ LONG PreviousState;
+ KIRQL OldIrql;
+
+ DPRINT("KeInsertHeadQueue %x\n", Queue);
+
+ /* Lock the Dispatcher Database */
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+
+ /* Insert the Queue */
+ PreviousState = KiInsertQueue(Queue, Entry, TRUE);
+
+ /* Release the Dispatcher Lock */
+ KeReleaseDispatcherDatabaseLock(OldIrql);
- InitialState = Queue->Header.SignalState;
-
- if (Head)
- {
- InsertHeadList(&Queue->EntryListHead, Entry);
- }
- else
- {
- InsertTailList(&Queue->EntryListHead, Entry);
- }
-
- //inc. num entries in queue
- Queue->Header.SignalState++;
-
- /* Why the KeGetCurrentThread()->Queue != Queue?
- * KiInsertQueue might be called from an APC for the current thread.
- * -Gunnar
- */
- if (Queue->CurrentCount < Queue->MaximumCount &&
- !IsListEmpty(&Queue->Header.WaitListHead) &&
- KeGetCurrentThread()->Queue != Queue)
- {
- KiDispatcherObjectWake(&Queue->Header, IO_NO_INCREMENT);
- }
-
- return InitialState;
+ /* Return previous State */
+ return PreviousState;
}
-
-
/*
* @implemented
*/
LONG STDCALL
-KeInsertHeadQueue(IN PKQUEUE Queue,
- IN PLIST_ENTRY Entry)
+KeInsertQueue(IN PKQUEUE Queue,
+ IN PLIST_ENTRY Entry)
{
- LONG Result;
- KIRQL OldIrql;
-
- OldIrql = KeAcquireDispatcherDatabaseLock();
- Result = KiInsertQueue(Queue,Entry,TRUE);
- KeReleaseDispatcherDatabaseLock(OldIrql);
+ LONG PreviousState;
+ KIRQL OldIrql;
+
+ DPRINT("KeInsertQueue %x\n", Queue);
+
+ /* Lock the Dispatcher Database */
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+
+ /* Insert the Queue */
+ PreviousState = KiInsertQueue(Queue, Entry, FALSE);
+
+ /* Release the Dispatcher Lock */
+ KeReleaseDispatcherDatabaseLock(OldIrql);
- return Result;
+ /* Return previous State */
+ return PreviousState;
}
-
/*
* @implemented
+ *
+ * Returns number of entries in the queue
*/
-LONG STDCALL
-KeInsertQueue(IN PKQUEUE Queue,
- IN PLIST_ENTRY Entry)
+LONG
+STDCALL
+KeReadStateQueue(IN PKQUEUE Queue)
{
- LONG Result;
- KIRQL OldIrql;
-
- OldIrql = KeAcquireDispatcherDatabaseLock();
- Result = KiInsertQueue(Queue,Entry,FALSE);
- KeReleaseDispatcherDatabaseLock(OldIrql);
-
- return Result;
+ /* Returns the Signal State */
+ return(Queue->Header.SignalState);
}
-
/*
* @implemented
*/
-PLIST_ENTRY STDCALL
+PLIST_ENTRY
+STDCALL
KeRemoveQueue(IN PKQUEUE Queue,
- IN KPROCESSOR_MODE WaitMode,
- IN PLARGE_INTEGER Timeout OPTIONAL)
+ IN KPROCESSOR_MODE WaitMode,
+ IN PLARGE_INTEGER Timeout OPTIONAL)
{
- PLIST_ENTRY ListEntry;
- NTSTATUS Status;
- PKTHREAD Thread = KeGetCurrentThread();
- KIRQL OldIrql;
-
- OldIrql = KeAcquireDispatcherDatabaseLock ();
-
- if (Thread->Queue != Queue)
- {
- /*
- * INVESTIGATE: What is the Thread->QueueListEntry used for? It's linked it into the
- * Queue->ThreadListHead when the thread registers with the queue and unlinked when
- * the thread registers with a new queue. The Thread->Queue already tells us what
- * queue the thread is registered with.
- * -Gunnar
- */
-
- //unregister thread from previous queue (if any)
- if (Thread->Queue)
- {
- RemoveEntryList(&Thread->QueueListEntry);
- Thread->Queue->CurrentCount--;
-
- if (Thread->Queue->CurrentCount < Thread->Queue->MaximumCount &&
- !IsListEmpty(&Thread->Queue->EntryListHead))
- {
- KiDispatcherObjectWake(&Thread->Queue->Header, 0);
- }
+ PLIST_ENTRY ListEntry;
+ NTSTATUS Status;
+ PKTHREAD Thread = KeGetCurrentThread();
+ KIRQL OldIrql;
+ PKQUEUE PreviousQueue;
+ PKWAIT_BLOCK WaitBlock;
+ PKWAIT_BLOCK TimerWaitBlock;
+ PKTIMER Timer;
+
+ DPRINT("KeRemoveQueue %x\n", Queue);
+
+ /* Check if the Lock is already held */
+ if (Thread->WaitNext) {
+
+ DPRINT("Lock is already held\n");
+
+ } else {
+
+ /* Lock the Dispatcher Database */
+ DPRINT("Lock not held, acquiring\n");
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+ Thread->WaitIrql = OldIrql;
+ }
+
+ /* This is needed so that we can set the new queue right here, before additional processing */
+ PreviousQueue = Thread->Queue;
+ Thread->Queue = Queue;
+
+ /* Check if this is a different queue */
+ if (Queue != PreviousQueue) {
+
+ /*
+ * INVESTIGATE: What is the Thread->QueueListEntry used for? It's linked it into the
+ * Queue->ThreadListHead when the thread registers with the queue and unlinked when
+ * the thread registers with a new queue. The Thread->Queue already tells us what
+ * queue the thread is registered with.
+ * -Gunnar
+ */
+ DPRINT("Different Queue\n");
+ if (PreviousQueue) {
+
+ /* Remove from this list */
+ DPRINT("Removing Old Queue\n");
+ RemoveEntryList(&Thread->QueueListEntry);
+
+ /* Wake the queue */
+ DPRINT("Activating new thread\n");
+ KiWakeQueue(PreviousQueue);
}
- // register thread with this queue
- InsertTailList(&Queue->ThreadListHead, &Thread->QueueListEntry);
- Thread->Queue = Queue;
- }
- else /* if (Thread->Queue == Queue) */
- {
- //dec. num running threads
- Queue->CurrentCount--;
- }
-
-
-
+ /* Insert in this new Queue */
+ DPRINT("Inserting new Queue!\n");
+ InsertTailList(&Queue->ThreadListHead, &Thread->QueueListEntry);
- while (TRUE)
- {
- if (Queue->CurrentCount < Queue->MaximumCount && !IsListEmpty(&Queue->EntryListHead))
- {
- ListEntry = RemoveHeadList(&Queue->EntryListHead);
- //dec. num entries in queue
- Queue->Header.SignalState--;
- //inc. num running threads
- Queue->CurrentCount++;
-
- KeReleaseDispatcherDatabaseLock(OldIrql);
- return ListEntry;
- }
- else
- {
- //inform KeWaitXxx that we are holding disp. lock
- Thread->WaitNext = TRUE;
- Thread->WaitIrql = OldIrql;
-
- Status = KeWaitForSingleObject(Queue,
- WrQueue,
- WaitMode,
- TRUE, //bAlertable
- Timeout);
-
- if (Status == STATUS_TIMEOUT || Status == STATUS_USER_APC)
- {
- return (PVOID)Status;
- }
-
- OldIrql = KeAcquireDispatcherDatabaseLock ();
- }
- }
+ } else {
+
+ /* Same queue, decrement waiting threads */
+ DPRINT("Same Queue!\n");
+ Queue->CurrentCount--;
+ }
+
+ /* Loop until the queue is processed */
+ while (TRUE) {
+
+ /* Get the Entry */
+ ListEntry = Queue->EntryListHead.Flink;
+
+ /* Check if the counts are valid and if there is still a queued entry */
+ if ((Queue->CurrentCount < Queue->MaximumCount) &&
+ (ListEntry != &Queue->EntryListHead)) {
+
+ /* Remove the Entry and Save it */
+ DPRINT("Removing Queue Entry. CurrentCount: %d, Maximum Count: %d\n",
+ Queue->CurrentCount, Queue->MaximumCount);
+ ListEntry = RemoveHeadList(&Queue->EntryListHead);
+
+ /* Decrease the number of entries */
+ Queue->Header.SignalState--;
+
+ /* Increase numbef of running threads */
+ Queue->CurrentCount++;
+
+ /* Check if the entry is valid. If not, bugcheck */
+ if (!ListEntry->Flink || !ListEntry->Blink) {
+
+ KEBUGCHECK(INVALID_WORK_QUEUE_ITEM);
+ }
+
+ /* Remove the Entry */
+ RemoveEntryList(ListEntry);
+ ListEntry->Flink = NULL;
+
+ /* Nothing to wait on */
+ break;
+
+ } else {
+
+ /* Do the wait */
+ DPRINT("Waiting on Queue Entry. CurrentCount: %d, Maximum Count: %d\n",
+ Queue->CurrentCount, Queue->MaximumCount);
+
+ /* Use the Thread's Wait Block, it's big enough */
+ Thread->WaitBlockList = &Thread->WaitBlock[0];
+
+ /* Fail if there's an APC Pending */
+ if (WaitMode == UserMode && Thread->ApcState.UserApcPending) {
+
+ /* Return the status and increase the pending threads */
+ ListEntry = (PLIST_ENTRY)STATUS_USER_APC;
+ Queue->CurrentCount++;
+
+ /* Nothing to wait on */
+ break;
+ }
+
+ /* Build the Wait Block */
+ WaitBlock = &Thread->WaitBlock[0];
+ WaitBlock->Object = (PVOID)Queue;
+ WaitBlock->WaitKey = STATUS_SUCCESS;
+ WaitBlock->WaitType = WaitAny;
+ WaitBlock->Thread = Thread;
+ WaitBlock->NextWaitBlock = NULL;
+
+ Thread->WaitStatus = STATUS_SUCCESS;
+
+ /* We need to wait for the object... check if we have a timeout */
+ if (Timeout) {
+
+ /* If it's zero, then don't do any waiting */
+ if (!Timeout->QuadPart) {
+
+ /* Instant Timeout, return the status and increase the pending threads */
+ DPRINT("Queue Wait has timed out\n");
+ ListEntry = (PLIST_ENTRY)STATUS_TIMEOUT;
+ Queue->CurrentCount++;
+
+ /* Nothing to wait on */
+ break;
+ }
+
+ /*
+ * Set up the Timer. We'll use the internal function so that we can
+ * hold on to the dispatcher lock.
+ */
+ Timer = &Thread->Timer;
+ TimerWaitBlock = &Thread->WaitBlock[1];
+
+ /* Set up the Timer Wait Block */
+ TimerWaitBlock->Object = (PVOID)Timer;
+ TimerWaitBlock->Thread = Thread;
+ TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
+ TimerWaitBlock->WaitType = WaitAny;
+ TimerWaitBlock->NextWaitBlock = NULL;
+
+ /* Link the timer to this Wait Block */
+ InitializeListHead(&Timer->Header.WaitListHead);
+ InsertTailList(&Timer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
+
+ /* Create Timer */
+ DPRINT("Creating Timer with timeout %I64d\n", *Timeout);
+ KiInsertTimer(Timer, *Timeout);
+ }
+
+ /* Insert the wait block into the Queues's wait list */
+ WaitBlock = Thread->WaitBlockList;
+ InsertTailList(&Queue->Header.WaitListHead, &WaitBlock->WaitListEntry);
+
+ /* Block the Thread */
+ DPRINT("Blocking the Thread: %x %x!\n", KeGetCurrentThread(), Thread);
+ PsBlockThread(&Status,
+ FALSE,
+ WaitMode,
+ WrQueue);
+
+ /* Reset the wait reason */
+ Thread->WaitReason = 0;
+
+ /* Check if we were executing an APC */
+ if (Status != STATUS_KERNEL_APC) {
+
+ /* Done Waiting */
+ DPRINT("Done waking queue. Thread: %x %x!\n", KeGetCurrentThread(), Thread);
+ return (PLIST_ENTRY)Status;
+ }
+
+ /* Acquire again the lock */
+ DPRINT("Looping again\n");
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+
+ /* Save the new IRQL and decrease number of waiting threads */
+ Thread->WaitIrql = OldIrql;
+ Queue->CurrentCount--;
+ }
+ }
+
+ /* Unlock Database and return */
+ KeReleaseDispatcherDatabaseLock(Thread->WaitIrql);
+ DPRINT("Returning. CurrentCount: %d, Maximum Count: %d\n",
+ Queue->CurrentCount, Queue->MaximumCount);
+ return ListEntry;
}
-
/*
* @implemented
*/
-PLIST_ENTRY STDCALL
+PLIST_ENTRY
+STDCALL
KeRundownQueue(IN PKQUEUE Queue)
{
- PLIST_ENTRY EnumEntry;
- PKTHREAD Thread;
- KIRQL OldIrql;
-
- DPRINT("KeRundownQueue(Queue %x)\n", Queue);
-
- /* I'm just guessing how this should work:-/
- * -Gunnar
- */
-
- OldIrql = KeAcquireDispatcherDatabaseLock ();
-
- //no thread must wait on queue at rundown
- ASSERT(IsListEmpty(&Queue->Header.WaitListHead));
+ PLIST_ENTRY EnumEntry;
+ PLIST_ENTRY FirstEntry;
+ PKTHREAD Thread;
+ KIRQL OldIrql;
+
+ DPRINT("KeRundownQueue(Queue %x)\n", Queue);
+
+ /* Get the Dispatcher Lock */
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+
+ /* Get the First Empty Entry */
+ FirstEntry = Queue->EntryListHead.Flink;
+
+ /* Make sure the list is not empty */
+ if (FirstEntry == &Queue->EntryListHead) {
+
+ /* It is, so don't return anything */
+ EnumEntry = NULL;
- // unlink threads and clear their Thread->Queue
- while (!IsListEmpty(&Queue->ThreadListHead))
- {
- EnumEntry = RemoveHeadList(&Queue->ThreadListHead);
- Thread = CONTAINING_RECORD(EnumEntry, KTHREAD, QueueListEntry);
- Thread->Queue = NULL;
- }
-
- if (IsListEmpty(&Queue->EntryListHead))
- {
- EnumEntry = NULL;
- }
- else
- {
- EnumEntry = Queue->EntryListHead.Flink;
- }
+ } else {
+
+ /* Remove it */
+ RemoveEntryList(&Queue->EntryListHead);
+ }
+
+ /* Unlink threads and clear their Thread->Queue */
+ while (!IsListEmpty(&Queue->ThreadListHead)) {
+
+ /* Get the Entry and Remove it */
+ EnumEntry = RemoveHeadList(&Queue->ThreadListHead);
+
+ /* Get the Entry's Thread */
+ Thread = CONTAINING_RECORD(EnumEntry, KTHREAD, QueueListEntry);
+
+ /* Kill its Queue */
+ Thread->Queue = NULL;
+ }
+
+ /* Release the lock and return */
+ KeReleaseDispatcherDatabaseLock(OldIrql);
+ return FirstEntry;
+}
- KeReleaseDispatcherDatabaseLock (OldIrql);
+/*
+ * Called when a thread which has a queue entry is entering a wait state
+ */
+VOID
+FASTCALL
+KiWakeQueue(IN PKQUEUE Queue)
+{
+ PLIST_ENTRY QueueEntry;
+ PLIST_ENTRY WaitEntry;
+ PKWAIT_BLOCK WaitBlock;
+
+ /* Decrement the number of active threads */
+ DPRINT("KiWakeQueue: %x. Thread: %x\n", Queue, KeGetCurrentThread());
+ Queue->CurrentCount--;
+
+ /* Make sure the counts are OK */
+ if (Queue->CurrentCount < Queue->MaximumCount) {
+
+ /* Get the Queue Entry */
+ QueueEntry = Queue->EntryListHead.Flink;
+
+ /* Get the Wait Entry */
+ WaitEntry = Queue->Header.WaitListHead.Blink;
+ DPRINT("Queue Count is ok, Queue entries: %x, %x\n", QueueEntry, WaitEntry);
+
+ /* Make sure that the Queue List isn't empty and that this entry is valid */
+ if (!IsListEmpty(&Queue->Header.WaitListHead) &&
+ (QueueEntry != &Queue->EntryListHead)) {
+
+ /* Remove this entry */
+ DPRINT("Queue in List, removing it\n");
+ RemoveEntryList(QueueEntry);
+ QueueEntry->Flink = NULL;
+
+ /* Decrease the Signal State */
+ Queue->Header.SignalState--;
+
+ /* Unwait the Thread */
+ DPRINT("Unwaiting Thread\n");
+ WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
+ KiAbortWaitThread(WaitBlock->Thread, (NTSTATUS)QueueEntry);
+ }
+ }
+}
- return EnumEntry;
+/*
+ * Returns the previous number of entries in the queue
+ */
+LONG
+STDCALL
+KiInsertQueue(IN PKQUEUE Queue,
+ IN PLIST_ENTRY Entry,
+ BOOLEAN Head)
+{
+ ULONG InitialState;
+ PKTHREAD Thread = KeGetCurrentThread();
+ PKWAIT_BLOCK WaitBlock;
+ PLIST_ENTRY WaitEntry;
+
+ DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry);
+
+ /* Save the old state */
+ InitialState = Queue->Header.SignalState;
+
+ /* Get the Entry */
+ WaitEntry = Queue->Header.WaitListHead.Blink;
+ DPRINT("Initial State, WaitEntry: %d, %x\n", InitialState, WaitEntry);
+
+ /*
+ * Why the KeGetCurrentThread()->Queue != Queue?
+ * KiInsertQueue might be called from an APC for the current thread.
+ * -Gunnar
+ */
+ if ((Queue->CurrentCount < Queue->MaximumCount) &&
+ (WaitEntry != &Queue->Header.WaitListHead) &&
+ ((Thread->Queue != Queue) || (Thread->WaitReason != WrQueue))) {
+
+ /* Remove the wait entry */
+ DPRINT("Removing Entry\n");
+ RemoveEntryList(WaitEntry);
+
+ /* Get the Wait Block and Thread */
+ WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
+ DPRINT("Got wait block: %x\n", WaitBlock);
+ Thread = WaitBlock->Thread;
+
+ /* Reset the wait reason */
+ Thread->WaitReason = 0;
+
+ /* Increase the waiting threads */
+ Queue->CurrentCount++;
+
+ /* Check if there's a Thread Timer */
+ if (Thread->Timer.Header.Inserted) {
+
+ /* Cancel the Thread Timer with the no-lock fastpath */
+ DPRINT("Removing the Thread's Timer\n");
+ Thread->Timer.Header.Inserted = FALSE;
+ RemoveEntryList(&Thread->Timer.TimerListEntry);
+ }
+
+ /* Reschedule the Thread */
+ DPRINT("Unblocking the Thread\n");
+ PsUnblockThread((PETHREAD)Thread, (PNTSTATUS)&Entry, 0);
+
+ } else {
+
+ /* Increase the Entries */
+ DPRINT("Adding new Queue Entry: %d %d\n", Head, Queue->Header.SignalState);
+ Queue->Header.SignalState++;
+
+ if (Head) {
+
+ InsertHeadList(&Queue->EntryListHead, Entry);
+
+ } else {
+
+ InsertTailList(&Queue->EntryListHead, Entry);
+ }
+ }
+
+ /* Return the previous state */
+ DPRINT("Returning\n");
+ return InitialState;
}
/* EOF */
-/* $Id$
- *
+/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS project
* FILE: ntoskrnl/ke/wait.c
* PURPOSE: Manages non-busy waiting
*
- * PROGRAMMERS: David Welch (welch@mcmail.com)
- * Phillip Susi
- */
-
-/* NOTES ********************************************************************
- *
+ * PROGRAMMERS: Alex Ionescu - Fixes and optimization.
+ * Gunnar Dalsnes - Implementation
*/
/* INCLUDES ******************************************************************/
static KSPIN_LOCK DispatcherDatabaseLock;
-#define KeDispatcherObjectWakeOne(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, FALSE)
-#define KeDispatcherObjectWakeAll(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, TRUE)
+/* Tells us if the Timer or Event is a Syncronization or Notification Object */
+#define TIMER_OR_EVENT_TYPE 0x7L
-extern POBJECT_TYPE EXPORTED ExMutantObjectType;
-extern POBJECT_TYPE EXPORTED ExSemaphoreObjectType;
-extern POBJECT_TYPE EXPORTED ExTimerType;
+/* One of the Reserved Wait Blocks, this one is for the Thread's Timer */
+#define TIMER_WAIT_BLOCK 0x3L
/* FUNCTIONS *****************************************************************/
-VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
- ULONG Type,
- ULONG Size,
- ULONG SignalState)
-{
- Header->Type = (UCHAR)Type;
- Header->Absolute = 0;
- Header->Inserted = 0;
- Header->Size = (UCHAR)Size;
- Header->SignalState = SignalState;
- InitializeListHead(&(Header->WaitListHead));
-}
-
-
-KIRQL
-KeAcquireDispatcherDatabaseLock(VOID)
-/*
- * PURPOSE: Acquires the dispatcher database lock for the caller
- */
-{
- KIRQL OldIrql;
-
- DPRINT("KeAcquireDispatcherDatabaseLock()\n");
-
- KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
- return OldIrql;
-}
-
-
VOID
-KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
-/*
- * PURPOSE: Acquires the dispatcher database lock for the caller
- */
-{
- DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
-
- KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
-}
-
-
-VOID
-KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
-{
- DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql);
- if (!KeIsExecutingDpc() &&
- OldIrql < DISPATCH_LEVEL &&
- KeGetCurrentThread() != NULL &&
- KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread)
- {
- PsDispatchThreadNoLock(THREAD_STATE_READY);
- KeLowerIrql(OldIrql);
- }
- else
- {
- KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
- }
-}
-
-
-VOID
-KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
-{
- DPRINT("KeReleaseDispatcherDatabaseLock()\n");
-
- KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
-}
-
-
-static BOOLEAN
-KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
- PKTHREAD Thread)
-/*
- * FUNCTION: Perform side effects on object before a wait for a thread is
- * satisfied
- */
-{
- BOOLEAN Abandoned = FALSE;
-
- switch (hdr->Type)
- {
- case EventSynchronizationObject:
- hdr->SignalState = 0;
- break;
-
- case QueueObject:
- break;
-
- case SemaphoreObject:
- hdr->SignalState--;
- break;
-
- case ProcessObject:
- break;
-
- case ThreadObject:
- break;
-
- case EventNotificationObject:
- break;
-
- case TimerSynchronizationObject:
- hdr->SignalState = FALSE;
- break;
-
- case TimerNotificationObject:
- break;
-
- case MutantObject:
- {
- PKMUTEX Mutex;
-
- Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
- hdr->SignalState--;
- ASSERT(hdr->SignalState <= 1);
- if (hdr->SignalState == 0)
- {
- if (Thread == NULL)
- {
- DPRINT("Thread == NULL!\n");
- KEBUGCHECK(0);
- }
- 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 == MutantObject)
- {
- 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);
- }
-}
-
-/* Must be called with the dispatcher lock held */
-BOOLEAN KiAbortWaitThread(PKTHREAD Thread, NTSTATUS WaitStatus)
-{
- PKWAIT_BLOCK WaitBlock;
- BOOLEAN WasWaiting;
-
- /* if we are blocked, we must be waiting on something also */
- ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL));
-
- WaitBlock = (PKWAIT_BLOCK)Thread->WaitBlockList;
- WasWaiting = (WaitBlock != NULL);
-
- while (WaitBlock)
- {
- RemoveEntryList(&WaitBlock->WaitListEntry);
- WaitBlock = WaitBlock->NextWaitBlock;
- }
-
- Thread->WaitBlockList = NULL;
-
- if (WasWaiting)
- {
- PsUnblockThread((PETHREAD)Thread, &WaitStatus, 0);
- }
- return WasWaiting;
-}
-
-static BOOLEAN
-KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
- KPRIORITY increment,
- BOOLEAN WakeAll)
-{
- 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)
- {
- 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);
- }
- 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)
- {
- if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
- {
- AllSignaled = FALSE;
- break;
- }
- }
- }
-
- if (AllSignaled)
- {
- for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
- {
- RemoveEntryList(&Waiter->WaitListEntry);
-
- if (Waiter->WaitType == WaitAll)
- {
- Abandoned |= KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread);
- }
-
- //no WaitAny objects can possibly be signaled since we are here
- ASSERT(!(Waiter->WaitType == WaitAny
- && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
- }
-
- 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, increment);
- }
- }
-
- return WakedAny;
-}
-
-
-BOOLEAN KiDispatcherObjectWake(DISPATCHER_HEADER* hdr, KPRIORITY increment)
-/*
- * FUNCTION: Wake threads waiting on a dispatcher object
- * NOTE: The exact semantics of waking are dependant on the type of object
- */
+inline
+FASTCALL
+KiCheckAlertability(BOOLEAN Alertable,
+ PKTHREAD CurrentThread,
+ KPROCESSOR_MODE WaitMode,
+ PNTSTATUS Status)
{
- 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 EventNotificationObject:
- return(KeDispatcherObjectWakeAll(hdr, increment));
-
- case TimerNotificationObject:
- return(KeDispatcherObjectWakeAll(hdr, increment));
-
- case EventSynchronizationObject:
- return(KeDispatcherObjectWakeOne(hdr, increment));
-
- case TimerSynchronizationObject:
- return(KeDispatcherObjectWakeOne(hdr, increment));
-
- case QueueObject:
- return(KeDispatcherObjectWakeOne(hdr, increment));
-
- case SemaphoreObject:
- DPRINT("hdr->SignalState %d\n", hdr->SignalState);
- if(hdr->SignalState>0)
- {
- do
- {
- DPRINT("Waking one semaphore waiter\n");
- Ret = KeDispatcherObjectWakeOne(hdr, increment);
- } while(hdr->SignalState > 0 && Ret) ;
- return(Ret);
- }
- else return FALSE;
-
- case ProcessObject:
- return(KeDispatcherObjectWakeAll(hdr, increment));
-
- case ThreadObject:
- return(KeDispatcherObjectWakeAll(hdr, increment));
-
- case MutantObject:
- return(KeDispatcherObjectWakeOne(hdr, increment));
- }
- DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
- KEBUGCHECK(0);
- return(FALSE);
+ /* At this point, we have to do a wait, so make sure we can make the thread Alertable if requested */
+ if (Alertable) {
+
+ /* If the Thread is Alerted, set the Wait Status accordingly */
+ if (CurrentThread->Alerted[(int)WaitMode]) {
+
+ CurrentThread->Alerted[(int)WaitMode] = FALSE;
+ DPRINT("Thread was Alerted\n");
+ *Status = STATUS_ALERTED;
+
+ /* If there are User APCs Pending, then we can't really be alertable */
+ } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) &&
+ (WaitMode == UserMode)) {
+
+ DPRINT("APCs are Pending\n");
+ CurrentThread->ApcState.UserApcPending = TRUE;
+ *Status = STATUS_USER_APC;
+ }
+
+ /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */
+ } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode == UserMode)) {
+ DPRINT("APCs are Pending\n");
+ *Status = STATUS_USER_APC;
+ }
}
/*
* @implemented
- */
-NTSTATUS STDCALL
-KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
- BOOLEAN Alertable,
- PLARGE_INTEGER Interval)
-/*
+ *
* FUNCTION: Puts the current thread into an alertable or nonalertable
* wait state for a given internal
* ARGUMENTS:
* Interval = Specifies the interval to wait
* RETURNS: Status
*/
+NTSTATUS
+STDCALL
+KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
+ BOOLEAN Alertable,
+ PLARGE_INTEGER Interval)
{
- PKTHREAD Thread = KeGetCurrentThread();
+ PKWAIT_BLOCK TimerWaitBlock;
+ PKTIMER ThreadTimer;
+ PKTHREAD CurrentThread = KeGetCurrentThread();
+ NTSTATUS Status;
- KeSetTimer(&Thread->Timer, *Interval, NULL);
- return (KeWaitForSingleObject(&Thread->Timer,
- (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */
- WaitMode, /* TMN: Was UserMode */
- Alertable,
- NULL));
+ DPRINT("Entering KeDelayExecutionThread\n");
+
+ /* Check if the lock is already held */
+ if (CurrentThread->WaitNext) {
+
+ /* Lock is held, disable Wait Next */
+ DPRINT("Lock is held\n");
+ CurrentThread->WaitNext = FALSE;
+
+ } else {
+
+ /* Lock not held, acquire it */
+ DPRINT("Lock is not held, acquiring\n");
+ CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+ }
+
+ /* Use built-in Wait block */
+ TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
+
+ /* Start Wait Loop */
+ do {
+
+ /* We are going to wait no matter what (that's the point), so test Alertability */
+ KiCheckAlertability(Alertable, CurrentThread, KernelMode, &Status);
+
+ /* Set Timer */
+ ThreadTimer = &CurrentThread->Timer;
+
+ /* Setup the Wait Block */
+ CurrentThread->WaitBlockList = TimerWaitBlock;
+ TimerWaitBlock->Object = (PVOID)ThreadTimer;
+ TimerWaitBlock->Thread = CurrentThread;
+ TimerWaitBlock->WaitKey = (USHORT)STATUS_TIMEOUT;
+ TimerWaitBlock->WaitType = WaitAny;
+ TimerWaitBlock->NextWaitBlock = NULL;
+
+ /* Link the timer to this Wait Block */
+ InitializeListHead(&ThreadTimer->Header.WaitListHead);
+ InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
+
+ /* Insert the Timer into the Timer Lists and enable it */
+ if (!KiInsertTimer(ThreadTimer, *Interval)) {
+
+ /* FIXME: Unhandled case...what should we do? */
+ DPRINT1("Could not create timer for KeDelayExecutionThread\n");
+ }
+
+ /* Handle Kernel Queues */
+ if (CurrentThread->Queue) {
+
+ DPRINT("Waking Queue\n");
+ KiWakeQueue(CurrentThread->Queue);
+ }
+
+ /* Block the Thread */
+ DPRINT("Blocking the Thread: %d, %d, %x\n", Alertable, WaitMode, KeGetCurrentThread());
+ PsBlockThread(&Status,
+ Alertable,
+ WaitMode,
+ DelayExecution);
+
+ /* Check if we were executing an APC or if we timed out */
+ if (Status != STATUS_KERNEL_APC) {
+
+ /* This is a good thing */
+ if (Status == STATUS_TIMEOUT) Status = STATUS_SUCCESS;
+
+ /* Return Status */
+ return Status;
+ }
+
+ DPRINT("Looping Again\n");
+ CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+
+ } while (TRUE);
+
+ /* Release the Lock, we are done */
+ DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n", KeGetCurrentThread(), Status);
+ KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+ return Status;
}
/*
* @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:
* Timeout = Optional timeout value
* RETURNS: Status
*/
+NTSTATUS
+STDCALL
+KeWaitForSingleObject(PVOID Object,
+ KWAIT_REASON WaitReason,
+ KPROCESSOR_MODE WaitMode,
+ BOOLEAN Alertable,
+ PLARGE_INTEGER Timeout)
{
- return KeWaitForMultipleObjects(1,
- &Object,
- WaitAny,
- WaitReason,
- WaitMode,
- Alertable,
- Timeout,
- NULL);
-}
+ PDISPATCHER_HEADER CurrentObject;
+ PKWAIT_BLOCK WaitBlock;
+ PKWAIT_BLOCK TimerWaitBlock;
+ PKTIMER ThreadTimer;
+ PKTHREAD CurrentThread = KeGetCurrentThread();
+ NTSTATUS Status;
+ NTSTATUS WaitStatus;
+
+ DPRINT("Entering KeWaitForSingleObject\n");
+
+ /* Check if the lock is already held */
+ if (CurrentThread->WaitNext) {
+
+ /* Lock is held, disable Wait Next */
+ DPRINT("Lock is held\n");
+ CurrentThread->WaitNext = FALSE;
+
+ } else {
+
+ /* Lock not held, acquire it */
+ DPRINT("Lock is not held, acquiring\n");
+ CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+ }
+ /* Start the actual Loop */
+ do {
+
+ /* Get the current Wait Status */
+ WaitStatus = CurrentThread->WaitStatus;
+
+ /* Append wait block to the KTHREAD wait block list */
+ CurrentThread->WaitBlockList = WaitBlock = &CurrentThread->WaitBlock[0];
+
+ /* Get the Current Object */
+ CurrentObject = (PDISPATCHER_HEADER)Object;
+
+ /* FIXME:
+ * Temporary hack until my Object Manager re-write. Basically some objects, like
+ * the File Object, but also LPCs and others, are actually waitable on their event.
+ * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member,
+ * by using pretty much the same kind of hack as us. Normal objects point to themselves
+ * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by
+ * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however,
+ * I will keep this hack here, since there's no need to make an interim hack until the rewrite
+ * -- Alex Ionescu 24/02/05
+ */
+ if (CurrentObject->Type == IO_TYPE_FILE) {
+
+ DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event);
+ CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event);
+ }
-inline
-PVOID
-KiGetWaitableObjectFromObject(PVOID Object)
-{
- //special case when waiting on file objects
- if ( ((PDISPATCHER_HEADER)Object)->Type == IO_TYPE_FILE)
- {
- return &((PFILE_OBJECT)Object)->Event;
- }
+ /* Check if the Object is Signaled */
+ if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
+
+ /* Just unwait this guy and exit */
+ if (CurrentObject->SignalState != MINLONG) {
+
+ /* It has a normal signal state, so unwait it and return */
+ KiSatisfyObjectWait(CurrentObject, CurrentThread);
+ Status = STATUS_WAIT_0;
+ goto WaitDone;
+
+ } else {
+
+ /* Is this a Mutant? */
+ if (CurrentObject->Type == MutantObject) {
+
+ /* According to wasm.ru, we must raise this exception (tested and true) */
+ KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+ ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
+ }
+ }
+ }
+
+ /* Set up the Wait Block */
+ WaitBlock->Object = CurrentObject;
+ WaitBlock->Thread = CurrentThread;
+ WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0);
+ WaitBlock->WaitType = WaitAny;
+ WaitBlock->NextWaitBlock = NULL;
+
+ /* Make sure we can satisfy the Alertable request */
+ KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status);
+
+ /* Set the Wait Status */
+ CurrentThread->WaitStatus = Status;
+
+ /* Enable the Timeout Timer if there was any specified */
+ if (Timeout != NULL) {
+
+ /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
+ if (!Timeout->QuadPart) {
+
+ /* Return a timeout */
+ Status = STATUS_TIMEOUT;
+ goto WaitDone;
+ }
+
+ /* Point to Timer Wait Block and Thread Timer */
+ TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
+ ThreadTimer = &CurrentThread->Timer;
- return Object;
-}
+ /* Connect the Timer Wait Block */
+ WaitBlock->NextWaitBlock = TimerWaitBlock;
+
+ /* Set up the Timer Wait Block */
+ TimerWaitBlock->Object = (PVOID)ThreadTimer;
+ TimerWaitBlock->Thread = CurrentThread;
+ TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
+ TimerWaitBlock->WaitType = WaitAny;
+ TimerWaitBlock->NextWaitBlock = NULL;
+
+ /* Link the timer to this Wait Block */
+ InitializeListHead(&ThreadTimer->Header.WaitListHead);
+ InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
+ /* Insert the Timer into the Timer Lists and enable it */
+ if (!KiInsertTimer(ThreadTimer, *Timeout)) {
-inline BOOL
-KiIsObjectWaitable(PVOID Object)
-{
- POBJECT_HEADER Header;
- Header = BODY_TO_HEADER(Object);
- if (Header->ObjectType == ExEventObjectType ||
- Header->ObjectType == ExIoCompletionType ||
- Header->ObjectType == ExMutantObjectType ||
- Header->ObjectType == ExSemaphoreObjectType ||
- Header->ObjectType == ExTimerType ||
- Header->ObjectType == PsProcessType ||
- Header->ObjectType == PsThreadType ||
- Header->ObjectType == IoFileObjectType)
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
+ /* Return a timeout if we couldn't insert the timer for some reason */
+ Status = STATUS_TIMEOUT;
+ goto WaitDone;
+ }
+ }
+
+ /* Link the Object to this Wait Block */
+ InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
+
+ /* Handle Kernel Queues */
+ if (CurrentThread->Queue) {
+
+ DPRINT("Waking Queue\n");
+ KiWakeQueue(CurrentThread->Queue);
+ }
+
+ /* Block the Thread */
+ DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
+ PsBlockThread(&Status,
+ Alertable,
+ WaitMode,
+ (UCHAR)WaitReason);
+
+ /* Check if we were executing an APC */
+ if (Status != STATUS_KERNEL_APC) {
+
+ /* Return Status */
+ return Status;
+ }
+
+ DPRINT("Looping Again\n");
+ CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+
+ } while (TRUE);
+
+WaitDone:
+ /* Release the Lock, we are done */
+ DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
+ KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+ return Status;
}
/*
PLARGE_INTEGER Timeout,
PKWAIT_BLOCK WaitBlockArray)
{
- DISPATCHER_HEADER *hdr;
- PKWAIT_BLOCK blk;
- PKTHREAD CurrentThread;
- ULONG CountSignaled;
- ULONG i;
- NTSTATUS Status;
- KIRQL OldIrql;
- BOOLEAN Abandoned;
- NTSTATUS WaitStatus;
-
- DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
- "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
-
- ASSERT(0 < Count && Count <= MAXIMUM_WAIT_OBJECTS);
-
- CurrentThread = KeGetCurrentThread();
-
- /*
- * 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 > 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)
- {
- KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
- }
-
- do
- {
- if (CurrentThread->WaitNext)
- {
- CurrentThread->WaitNext = FALSE;
- OldIrql = CurrentThread->WaitIrql;
- }
- else
- {
- OldIrql = KeAcquireDispatcherDatabaseLock ();
- }
-
- /* Get the current Wait Status */
- WaitStatus = CurrentThread->WaitStatus;
-
- if (Alertable) {
+ PDISPATCHER_HEADER CurrentObject;
+ PKWAIT_BLOCK WaitBlock;
+ PKWAIT_BLOCK TimerWaitBlock;
+ PKTIMER ThreadTimer;
+ PKTHREAD CurrentThread = KeGetCurrentThread();
+ ULONG AllObjectsSignaled;
+ ULONG WaitIndex;
+ NTSTATUS Status;
+ NTSTATUS WaitStatus;
+
+ DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
+ "PsGetCurrentThread() %x, Timeout %x\n", Count, Object, PsGetCurrentThread(), Timeout);
+
+ /* Set the Current Thread */
+ CurrentThread = KeGetCurrentThread();
- /* If the Thread is Alerted, set the Wait Status accordingly */
- if (CurrentThread->Alerted[(int)WaitMode]) {
-
- CurrentThread->Alerted[(int)WaitMode] = FALSE;
- DPRINT("Thread was Alerted\n");
- WaitStatus = STATUS_ALERTED;
-
- /* If there are User APCs Pending, then we can't really be alertable */
- } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) &&
- (WaitMode == UserMode)) {
+ /* Check if the lock is already held */
+ if (CurrentThread->WaitNext) {
+
+ /* Lock is held, disable Wait Next */
+ DPRINT("Lock is held\n");
+ CurrentThread->WaitNext = FALSE;
+
+ } else {
+
+ /* Lock not held, acquire it */
+ DPRINT("Lock is not held, acquiring\n");
+ CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+ }
+
+ /* Make sure the Wait Count is valid for the Thread and Maximum Wait Objects */
+ if (!WaitBlockArray) {
+
+ /* Check in regards to the Thread Object Limit */
+ if (Count > THREAD_WAIT_OBJECTS) {
- DPRINT1("APCs are Pending\n");
- CurrentThread->ApcState.UserApcPending = TRUE;
- WaitStatus = STATUS_USER_APC;
+ DPRINT1("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
+ KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
}
- /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */
- } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode == UserMode)) {
- DPRINT1("APCs are Pending\n");
- WaitStatus = STATUS_USER_APC;
+ /* Use the Thread's Wait Block */
+ WaitBlockArray = &CurrentThread->WaitBlock[0];
+
+ } else {
+
+ /* Using our own Block Array. Check in regards to System Object Limit */
+ if (Count > MAXIMUM_WAIT_OBJECTS) {
+
+ DPRINT1("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
+ KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
+ }
}
-
- /*
- * 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++;
-
- if (WaitType == WaitAny)
- {
- Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
-
- KeReleaseDispatcherDatabaseLock(OldIrql);
-
- if (Timeout != NULL && Timeout->QuadPart != 0)
- {
- KeCancelTimer(&CurrentThread->Timer);
- }
-
- DPRINT("One object is (already) signaled!\n");
- if (Abandoned == TRUE)
- {
- return (STATUS_ABANDONED_WAIT_0 + i);
- }
-
- return (STATUS_WAIT_0 + i);
+
+ /* Start the actual Loop */
+ do {
+
+ /* Get the current Wait Status */
+ WaitStatus = CurrentThread->WaitStatus;
+
+ /* Append wait block to the KTHREAD wait block list */
+ CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
+
+ /* Check if the wait is (already) satisfied */
+ AllObjectsSignaled = TRUE;
+
+ /* First, we'll try to satisfy the wait directly */
+ for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) {
+
+ /* Get the Current Object */
+ CurrentObject = (PDISPATCHER_HEADER)Object[WaitIndex];
+
+ /* FIXME:
+ * Temporary hack until my Object Manager re-write. Basically some objects, like
+ * the File Object, but also LPCs and others, are actually waitable on their event.
+ * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member,
+ * by using pretty much the same kind of hack as us. Normal objects point to themselves
+ * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by
+ * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however,
+ * I will keep this hack here, since there's no need to make an interim hack until the rewrite
+ * -- Alex Ionescu 24/02/05
+ */
+ if (CurrentObject->Type == IO_TYPE_FILE) {
+
+ DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event);
+ CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event);
}
- }
- }
-
- 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;
- }
- KeReleaseDispatcherDatabaseLock(OldIrql);
-
- if (Timeout != NULL && Timeout->QuadPart != 0)
- {
- KeCancelTimer(&CurrentThread->Timer);
- }
-
- DPRINT("All objects are (already) signaled!\n");
-
- if (Abandoned == TRUE)
- {
- return (STATUS_ABANDONED_WAIT_0);
- }
+ /* Check if the Object is Signaled */
+ if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
+
+ /* Check what kind of wait this is */
+ if (WaitType == WaitAny) {
+
+ /* This is a Wait Any, so just unwait this guy and exit */
+ if (CurrentObject->SignalState != MINLONG) {
+
+ /* It has a normal signal state, so unwait it and return */
+ KiSatisfyObjectWait(CurrentObject, CurrentThread);
+ Status = STATUS_WAIT_0 | WaitIndex;
+ goto WaitDone;
+
+ } else {
+
+ /* Is this a Mutant? */
+ if (CurrentObject->Type == MutantObject) {
+
+ /* According to wasm.ru, we must raise this exception (tested and true) */
+ KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+ ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
+ }
+ }
+ }
+
+ } else {
+
+ /* One of the objects isn't signaled... if this is a WaitAll, we will fail later */
+ AllObjectsSignaled = FALSE;
+ }
- return (STATUS_WAIT_0);
- }
+ /* Set up a Wait Block for this Object */
+ WaitBlock->Object = CurrentObject;
+ WaitBlock->Thread = CurrentThread;
+ WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0 + WaitIndex);
+ WaitBlock->WaitType = (USHORT)WaitType;
+ WaitBlock->NextWaitBlock = WaitBlock + 1;
+
+ /* Move to the next Wait Block */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ }
+
+ /* Return to the Root Wait Block */
+ WaitBlock--;
+ WaitBlock->NextWaitBlock = NULL;
+
+ /* Check if this is a Wait All and all the objects are signaled */
+ if ((WaitType == WaitAll) && (AllObjectsSignaled)) {
+
+ /* Return to the Root Wait Block */
+ WaitBlock = CurrentThread->WaitBlockList;
+
+ /* Satisfy their Waits and return to the caller */
+ KiSatisifyMultipleObjectWaits(WaitBlock);
+ Status = STATUS_WAIT_0;
+ goto WaitDone;
+ }
+
+ /* Make sure we can satisfy the Alertable request */
+ KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status);
+
+ /* Set the Wait Status */
+ CurrentThread->WaitStatus = Status;
+
+ /* Enable the Timeout Timer if there was any specified */
+ if (Timeout != NULL) {
+
+ /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
+ if (!Timeout->QuadPart) {
+
+ /* Return a timeout */
+ Status = STATUS_TIMEOUT;
+ goto WaitDone;
+ }
+
+ /* Point to Timer Wait Block and Thread Timer */
+ TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
+ ThreadTimer = &CurrentThread->Timer;
- //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;
- }
+ /* Connect the Timer Wait Block */
+ WaitBlock->NextWaitBlock = TimerWaitBlock;
+
+ /* Set up the Timer Wait Block */
+ TimerWaitBlock->Object = (PVOID)ThreadTimer;
+ TimerWaitBlock->Thread = CurrentThread;
+ TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
+ TimerWaitBlock->WaitType = WaitAny;
+ TimerWaitBlock->NextWaitBlock = NULL;
+
+ /* Link the timer to this Wait Block */
+ InitializeListHead(&ThreadTimer->Header.WaitListHead);
- /*
- * Check if we have already timed out
- */
- if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
- {
- KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
- KeReleaseDispatcherDatabaseLock(OldIrql);
- KeCancelTimer(&CurrentThread->Timer);
- return (STATUS_TIMEOUT);
- }
+ /* Insert the Timer into the Timer Lists and enable it */
+ if (!KiInsertTimer(ThreadTimer, *Timeout)) {
- /* Append wait block to the KTHREAD wait block list */
- CurrentThread->WaitBlockList = blk = WaitBlockArray;
+ /* Return a timeout if we couldn't insert the timer for some reason */
+ Status = STATUS_TIMEOUT;
+ goto WaitDone;
+ }
+ }
- /*
- * Set up the wait
- */
- CurrentThread->WaitStatus = WaitStatus;;
+ /* Insert into Object's Wait List*/
+ WaitBlock = CurrentThread->WaitBlockList;
+ while (WaitBlock) {
+
+ /* Get the Current Object */
+ CurrentObject = WaitBlock->Object;
+
+ /* Link the Object to this Wait Block */
+ InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
+
+ /* Move to the next Wait Block */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ }
+
+ /* Handle Kernel Queues */
+ if (CurrentThread->Queue) {
+
+ DPRINT("Waking Queue\n");
+ KiWakeQueue(CurrentThread->Queue);
+ }
- for (i = 0; i < Count; i++)
- {
- hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
+ /* Block the Thread */
+ DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
+ PsBlockThread(&Status,
+ Alertable,
+ WaitMode,
+ (UCHAR)WaitReason);
+
+ /* Check if we were executing an APC */
+ if (Status != STATUS_KERNEL_APC) {
+
+ /* Return Status */
+ return Status;
+ }
+
+ DPRINT("Looping Again\n");
+ CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
+
+ } while (TRUE);
+
+WaitDone:
+ /* Release the Lock, we are done */
+ DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
+ KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
+ return Status;
+}
- blk->Object = KiGetWaitableObjectFromObject(Object[i]);
- blk->Thread = CurrentThread;
- blk->WaitKey = (USHORT)(STATUS_WAIT_0 + i);
- blk->WaitType = (USHORT)WaitType;
+VOID
+FASTCALL
+KiSatisfyObjectWait(PDISPATCHER_HEADER Object,
+ PKTHREAD Thread)
- if (i == (Count - 1))
- {
- if (Timeout != NULL)
- {
- blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
- }
- else
- {
- blk->NextWaitBlock = NULL;
+{
+ /* Special case for Mutants */
+ if (Object->Type == MutantObject) {
+
+ /* Decrease the Signal State */
+ Object->SignalState--;
+
+ /* Check if it's now non-signaled */
+ if (Object->SignalState == 0) {
+
+ /* Set the Owner Thread */
+ ((PKMUTANT)Object)->OwnerThread = Thread;
+
+ /* Disable APCs if needed */
+ Thread->KernelApcDisable -= ((PKMUTANT)Object)->ApcDisable;
+
+ /* Check if it's abandoned */
+ if (((PKMUTANT)Object)->Abandoned) {
+
+ /* Unabandon it */
+ ((PKMUTANT)Object)->Abandoned = FALSE;
+
+ /* Return Status */
+ Thread->WaitStatus = STATUS_ABANDONED;
}
- }
- 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;
- }
+
+ /* Insert it into the Mutant List */
+ InsertHeadList(&Thread->MutantListHead, &((PKMUTANT)Object)->MutantListEntry);
+ }
+
+ } else if ((Object->Type & TIMER_OR_EVENT_TYPE) == EventSynchronizationObject) {
+
+ /* These guys (Syncronization Timers and Events) just get un-signaled */
+ Object->SignalState = 0;
+
+ } else if (Object->Type == SemaphoreObject) {
- 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;
+ /* These ones can have multiple signalings, so we only decrease it */
+ Object->SignalState--;
+ }
+}
- InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
- &CurrentThread->WaitBlock[3].WaitListEntry);
- }
+VOID
+FASTCALL
+KiWaitTest(PDISPATCHER_HEADER Object,
+ KPRIORITY Increment)
+{
+ PLIST_ENTRY WaitEntry;
+ PLIST_ENTRY WaitList;
+ PKWAIT_BLOCK CurrentWaitBlock;
+ PKWAIT_BLOCK NextWaitBlock;
+
+ /* Loop the Wait Entries */
+ DPRINT("KiWaitTest for Object: %x\n", Object);
+ WaitList = &Object->WaitListHead;
+ WaitEntry = WaitList->Flink;
+ while ((WaitEntry != WaitList) && (Object->SignalState > 0)) {
+
+ /* Get the current wait block */
+ CurrentWaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
+
+ /* Check the current Wait Mode */
+ if (CurrentWaitBlock->WaitType == WaitAny) {
+
+ /* Easy case, satisfy only this wait */
+ DPRINT("Satisfiying a Wait any\n");
+ WaitEntry = WaitEntry->Blink;
+ KiSatisfyObjectWait(Object, CurrentWaitBlock->Thread);
+
+ } else {
+
+ /* Everything must be satisfied */
+ DPRINT("Checking for a Wait All\n");
+ NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
+
+ /* Loop first to make sure they are valid */
+ while (NextWaitBlock) {
+
+ /* Check if the object is signaled */
+ if (!KiIsObjectSignaled(Object, CurrentWaitBlock->Thread)) {
+
+ /* It's not, move to the next one */
+ DPRINT1("One of the object is non-signaled, sorry.\n");
+ goto SkipUnwait;
+ }
+
+ /* Go to the next Wait block */
+ NextWaitBlock = NextWaitBlock->NextWaitBlock;
+ }
+
+ /* All the objects are signaled, we can satisfy */
+ DPRINT("Satisfiying a Wait All\n");
+ WaitEntry = WaitEntry->Blink;
+ KiSatisifyMultipleObjectWaits(CurrentWaitBlock);
+ }
+
+ /* All waits satisfied, unwait the thread */
+ DPRINT("Unwaiting the Thread\n");
+ KiAbortWaitThread(CurrentWaitBlock->Thread, CurrentWaitBlock->WaitKey);
- //kernel queues
- if (CurrentThread->Queue && WaitReason != WrQueue)
- {
- DPRINT("queue: sleep on something else\n");
- CurrentThread->Queue->CurrentCount--;
-
- //wake another thread
- if (CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
- !IsListEmpty(&CurrentThread->Queue->EntryListHead))
- {
- KiDispatcherObjectWake(&CurrentThread->Queue->Header, IO_NO_INCREMENT);
- }
- }
+SkipUnwait:
+ /* Next entry */
+ WaitEntry = WaitEntry->Flink;
+ }
+
+ DPRINT("Done\n");
+}
- PsBlockThread(&Status, Alertable, WaitMode, TRUE, OldIrql, (UCHAR)WaitReason);
+/* Must be called with the dispatcher lock held */
+VOID
+FASTCALL
+KiAbortWaitThread(PKTHREAD Thread,
+ NTSTATUS WaitStatus)
+{
+ PKWAIT_BLOCK WaitBlock;
- //kernel queues
- OldIrql = KeAcquireDispatcherDatabaseLock ();
- if (CurrentThread->Queue && WaitReason != WrQueue)
- {
- DPRINT("queue: wake from something else\n");
- CurrentThread->Queue->CurrentCount++;
- }
- if (Status == STATUS_KERNEL_APC)
- {
- CurrentThread->WaitNext = TRUE;
- CurrentThread->WaitIrql = OldIrql;
- }
- else
- {
- KeReleaseDispatcherDatabaseLock(OldIrql);
- }
-
- } while (Status == STATUS_KERNEL_APC);
-
+ /* If we are blocked, we must be waiting on something also */
+ DPRINT("KiAbortWaitThread: %x, Status: %x, %x \n", Thread, WaitStatus, Thread->WaitBlockList);
+ ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL));
- if (Timeout != NULL)
- {
- KeCancelTimer(&CurrentThread->Timer);
- }
+ /* Remove the Wait Blocks from the list */
+ DPRINT("Removing waits\n");
+ WaitBlock = Thread->WaitBlockList;
+ while (WaitBlock) {
+
+ /* Remove it */
+ DPRINT("Removing Waitblock: %x, %x\n", WaitBlock, WaitBlock->NextWaitBlock);
+ RemoveEntryList(&WaitBlock->WaitListEntry);
+
+ /* Go to the next one */
+ WaitBlock = WaitBlock->NextWaitBlock;
+ };
+
+ /* Check if there's a Thread Timer */
+ if (Thread->Timer.Header.Inserted) {
+
+ /* Cancel the Thread Timer with the no-lock fastpath */
+ DPRINT("Removing the Thread's Timer\n");
+ Thread->Timer.Header.Inserted = FALSE;
+ RemoveEntryList(&Thread->Timer.TimerListEntry);
+ }
+
+ /* Increment the Queue's active threads */
+ if (Thread->Queue) {
+
+ DPRINT("Incrementing Queue's active threads\n");
+ Thread->Queue->CurrentCount++;
+ }
- DPRINT("Returning from KeWaitForMultipleObjects()\n");
- return (Status);
+ /* Reschedule the Thread */
+ DPRINT("Unblocking the Thread\n");
+ PsUnblockThread((PETHREAD)Thread, &WaitStatus, 0);
}
-VOID KeInitializeDispatcher(VOID)
+BOOLEAN
+inline
+FASTCALL
+KiIsObjectSignaled(PDISPATCHER_HEADER Object,
+ PKTHREAD Thread)
{
- KeInitializeSpinLock(&DispatcherDatabaseLock);
+ /* Mutants are...well...mutants! */
+ if (Object->Type == MutantObject) {
+
+ /*
+ * Because Cutler hates mutants, they are actually signaled if the Signal State is <= 0
+ * Well, only if they are recursivly acquired (i.e if we own it right now).
+ * Of course, they are also signaled if their signal state is 1.
+ */
+ if ((Object->SignalState <= 0 && ((PKMUTANT)Object)->OwnerThread == Thread) ||
+ (Object->SignalState == 1)) {
+
+ /* Signaled Mutant */
+ return (TRUE);
+
+ } else {
+
+ /* Unsignaled Mutant */
+ return (FALSE);
+ }
+ }
+
+ /* Any other object is not a mutated freak, so let's use logic */
+ return (!Object->SignalState <= 0);
}
-NTSTATUS STDCALL
-NtWaitForMultipleObjects(IN ULONG ObjectCount,
- IN PHANDLE ObjectsArray,
- IN WAIT_TYPE WaitType,
- IN BOOLEAN Alertable,
- IN PLARGE_INTEGER TimeOut OPTIONAL)
+BOOL
+inline
+FASTCALL
+KiIsObjectWaitable(PVOID Object)
{
- KWAIT_BLOCK WaitBlockArray[MAXIMUM_WAIT_OBJECTS];
- HANDLE SafeObjectsArray[MAXIMUM_WAIT_OBJECTS];
- PVOID ObjectPtrArray[MAXIMUM_WAIT_OBJECTS];
- ULONG i, j;
- KPROCESSOR_MODE PreviousMode;
- LARGE_INTEGER SafeTimeOut;
- NTSTATUS Status = STATUS_SUCCESS;
-
- DPRINT("NtWaitForMultipleObjects(ObjectCount %lu ObjectsArray[] %x, Alertable %d, "
- "TimeOut %x)\n", ObjectCount,ObjectsArray,Alertable,TimeOut);
-
- PreviousMode = ExGetPreviousMode();
-
- if (ObjectCount > MAXIMUM_WAIT_OBJECTS)
- return STATUS_UNSUCCESSFUL;
- if (0 == ObjectCount)
- return STATUS_INVALID_PARAMETER;
-
- if(PreviousMode != KernelMode)
- {
- _SEH_TRY
- {
- ProbeForRead(ObjectsArray,
- ObjectCount * sizeof(ObjectsArray[0]),
- sizeof(ULONG));
- /* make a copy so we don't have to guard with SEH later and keep track of
- what objects we referenced in case dereferencing pointers suddenly fails */
- RtlCopyMemory(SafeObjectsArray, ObjectsArray, ObjectCount * sizeof(ObjectsArray[0]));
- ObjectsArray = SafeObjectsArray;
-
- if(TimeOut != NULL)
- {
- ProbeForRead(TimeOut,
- sizeof(LARGE_INTEGER),
- sizeof(ULONG));
- /* make a local copy of the timeout on the stack */
- SafeTimeOut = *TimeOut;
- TimeOut = &SafeTimeOut;
- }
- }
- _SEH_HANDLE
- {
- Status = _SEH_GetExceptionCode();
- }
- _SEH_END;
-
- if(!NT_SUCCESS(Status))
- {
- return Status;
- }
- }
-
- /* reference all objects */
- for (i = 0; i < ObjectCount; i++)
- {
- Status = ObReferenceObjectByHandle(ObjectsArray[i],
- SYNCHRONIZE,
- NULL,
- PreviousMode,
- &ObjectPtrArray[i],
- NULL);
- if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i]))
- {
- if (NT_SUCCESS(Status))
- {
- DPRINT1("Waiting for object type '%wZ' is not supported\n",
- &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName);
- Status = STATUS_HANDLE_NOT_WAITABLE;
- i++;
- }
- /* dereference all referenced objects */
- for (j = 0; j < i; j++)
- {
- ObDereferenceObject(ObjectPtrArray[j]);
- }
-
- return(Status);
- }
- }
-
- Status = KeWaitForMultipleObjects(ObjectCount,
- ObjectPtrArray,
- WaitType,
- UserRequest,
- PreviousMode,
- Alertable,
- TimeOut,
- WaitBlockArray);
-
- /* dereference all objects */
- for (i = 0; i < ObjectCount; i++)
- {
- ObDereferenceObject(ObjectPtrArray[i]);
- }
-
- return(Status);
+ POBJECT_HEADER Header;
+ Header = BODY_TO_HEADER(Object);
+
+ if (Header->ObjectType == ExEventObjectType ||
+ Header->ObjectType == ExIoCompletionType ||
+ Header->ObjectType == ExMutantObjectType ||
+ Header->ObjectType == ExSemaphoreObjectType ||
+ Header->ObjectType == ExTimerType ||
+ Header->ObjectType == PsProcessType ||
+ Header->ObjectType == PsThreadType ||
+ Header->ObjectType == IoFileObjectType) {
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+ }
}
-
-/*
- * @implemented
- */
-NTSTATUS STDCALL
-NtWaitForSingleObject(IN HANDLE ObjectHandle,
- IN BOOLEAN Alertable,
- IN PLARGE_INTEGER TimeOut OPTIONAL)
+VOID
+inline
+FASTCALL
+KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock)
{
- PVOID ObjectPtr;
- KPROCESSOR_MODE PreviousMode;
- LARGE_INTEGER SafeTimeOut;
- NTSTATUS Status = STATUS_SUCCESS;
-
- DPRINT("NtWaitForSingleObject(ObjectHandle %x, Alertable %d, TimeOut %x)\n",
- ObjectHandle,Alertable,TimeOut);
-
- PreviousMode = ExGetPreviousMode();
-
- if(TimeOut != NULL && PreviousMode != KernelMode)
- {
- _SEH_TRY
- {
- ProbeForRead(TimeOut,
- sizeof(LARGE_INTEGER),
- sizeof(ULONG));
- /* make a copy on the stack */
- SafeTimeOut = *TimeOut;
- TimeOut = &SafeTimeOut;
- }
- _SEH_HANDLE
- {
- Status = _SEH_GetExceptionCode();
- }
- _SEH_END;
-
- if(!NT_SUCCESS(Status))
- {
- return Status;
- }
- }
-
- Status = ObReferenceObjectByHandle(ObjectHandle,
- SYNCHRONIZE,
- NULL,
- PreviousMode,
- &ObjectPtr,
- NULL);
- if (!NT_SUCCESS(Status))
- {
- return(Status);
- }
- if (!KiIsObjectWaitable(ObjectPtr))
- {
- DPRINT1("Waiting for object type '%wZ' is not supported\n",
- &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName);
- Status = STATUS_HANDLE_NOT_WAITABLE;
- }
- else
- {
- Status = KeWaitForSingleObject(ObjectPtr,
- UserRequest,
- PreviousMode,
- Alertable,
- TimeOut);
- }
-
- ObDereferenceObject(ObjectPtr);
-
- return(Status);
+ PKTHREAD WaitThread = WaitBlock->Thread;
+
+ /* Loop through all the Wait Blocks, and wake each Object */
+ while (WaitBlock) {
+
+ /* Wake the Object */
+ KiSatisfyObjectWait(WaitBlock->Object, WaitThread);
+ WaitBlock = WaitBlock->NextWaitBlock;
+ }
}
-
-NTSTATUS STDCALL
-NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,
- IN HANDLE WaitableObjectHandle,
- IN BOOLEAN Alertable,
- IN PLARGE_INTEGER TimeOut OPTIONAL)
+VOID
+inline
+FASTCALL
+KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
+ ULONG Type,
+ ULONG Size,
+ ULONG SignalState)
{
- KPROCESSOR_MODE PreviousMode;
- DISPATCHER_HEADER* hdr;
- PVOID SignalObj;
- PVOID WaitObj;
- LARGE_INTEGER SafeTimeOut;
- NTSTATUS Status = STATUS_SUCCESS;
-
- PreviousMode = ExGetPreviousMode();
-
- if(TimeOut != NULL && PreviousMode != KernelMode)
- {
- _SEH_TRY
- {
- ProbeForRead(TimeOut,
- sizeof(LARGE_INTEGER),
- sizeof(ULONG));
- /* make a copy on the stack */
- SafeTimeOut = *TimeOut;
- TimeOut = &SafeTimeOut;
- }
- _SEH_HANDLE
- {
- Status = _SEH_GetExceptionCode();
- }
- _SEH_END;
-
- if(!NT_SUCCESS(Status))
- {
- return Status;
- }
- }
-
- Status = ObReferenceObjectByHandle(ObjectHandleToSignal,
- 0,
- NULL,
- PreviousMode,
- &SignalObj,
- NULL);
- if (!NT_SUCCESS(Status))
- {
- return Status;
- }
-
- Status = ObReferenceObjectByHandle(WaitableObjectHandle,
- SYNCHRONIZE,
- NULL,
- PreviousMode,
- &WaitObj,
- NULL);
- if (!NT_SUCCESS(Status))
- {
- ObDereferenceObject(SignalObj);
- return Status;
- }
-
- hdr = (DISPATCHER_HEADER *)SignalObj;
- switch (hdr->Type)
- {
- case EventNotificationObject:
- case EventSynchronizationObject:
- KeSetEvent(SignalObj,
- EVENT_INCREMENT,
- TRUE);
- break;
+ Header->Type = (UCHAR)Type;
+ Header->Absolute = 0;
+ Header->Inserted = 0;
+ Header->Size = (UCHAR)Size;
+ Header->SignalState = SignalState;
+ InitializeListHead(&(Header->WaitListHead));
+}
- case MutantObject:
- KeReleaseMutex(SignalObj,
- TRUE);
- break;
+KIRQL
+inline
+FASTCALL
+KeAcquireDispatcherDatabaseLock(VOID)
+{
+ KIRQL OldIrql;
- case SemaphoreObject:
- KeReleaseSemaphore(SignalObj,
- SEMAPHORE_INCREMENT,
- 1,
- TRUE);
- break;
+ KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
+ return OldIrql;
+}
- default:
- ObDereferenceObject(SignalObj);
- ObDereferenceObject(WaitObj);
- return STATUS_OBJECT_TYPE_MISMATCH;
- }
+VOID
+inline
+FASTCALL
+KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
+{
+ KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
+}
- Status = KeWaitForSingleObject(WaitObj,
- UserRequest,
- PreviousMode,
- Alertable,
- TimeOut);
+VOID
+inline
+FASTCALL
+KeInitializeDispatcher(VOID)
+{
+ /* Initialize the Dispatcher Lock */
+ KeInitializeSpinLock(&DispatcherDatabaseLock);
+}
- ObDereferenceObject(SignalObj);
- ObDereferenceObject(WaitObj);
+VOID
+inline
+FASTCALL
+KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
+{
+ /* If it's the idle thread, dispatch */
+ if (!KeIsExecutingDpc() && OldIrql < DISPATCH_LEVEL && KeGetCurrentThread() != NULL &&
+ KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread) {
+
+ PsDispatchThreadNoLock(THREAD_STATE_READY);
+ KeLowerIrql(OldIrql);
+
+ } else {
+
+ /* Just release the spin lock */
+ KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
+ }
+}
- return Status;
+VOID
+inline
+FASTCALL
+KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
+{
+ KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
}
+
+/* EOF */