- Optimized the dispatcher lock. It is now gone on non-SMP systems and IRQL is raised or lowered instead. - Made inlined functions in ke/wait.c macros, because they weren't being inlined. - Created separate cases for satisfying mutant, non-mutant and generic objects, to optimize wait satisfaction. - Fixed some places which werne't setting the dispatcher header's size member correctly. - Fixed formatting in ke/wait.c - Fixed a case in KiCheckAlertability: we also need to check if the thread is alerted in Kernel-Mode, even if the wait mode given was user and user-mode is not alerted. - Fixed signaling checks across the wait code and removed KiCheckIfObjectSignaled. We must not consider the mutant as signaled if SignalState is = 1. - Fix code to check if the wait blocks' status is STATUS_TIMEOUT, because we do not need to check for signal state in that case. - Removed the exports for internal dispatcher lock routines.
** Thanks to Waxdragon for stress-testing this for an hour :) Modified: trunk/reactos/ntoskrnl/include/internal/ke.h Modified: trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S Modified: trunk/reactos/ntoskrnl/ke/kthread.c Modified: trunk/reactos/ntoskrnl/ke/wait.c Modified: trunk/reactos/ntoskrnl/ntoskrnl.def Modified: trunk/reactos/ntoskrnl/ps/psmgr.c _____
Modified: trunk/reactos/ntoskrnl/include/internal/ke.h --- trunk/reactos/ntoskrnl/include/internal/ke.h 2006-01-05 01:49:00 UTC (rev 20567) +++ trunk/reactos/ntoskrnl/include/internal/ke.h 2006-01-05 04:26:55 UTC (rev 20568) @@ -44,31 +44,174 @@
/* MACROS ************************************************************************ */
-#define KeEnterCriticalRegion() \ -{ \ - PKTHREAD _Thread = KeGetCurrentThread(); \ - if (_Thread) _Thread->KernelApcDisable--; \ +/* + * On UP machines, we don't actually have a spinlock, we merely raise + * IRQL to DPC level. + */ +#ifndef CONFIG_SMP +#define KeInitializeDispatcher() +#define KeAcquireDispatcherDatabaseLock() KeRaiseIrqlToDpcLevel(); +#define KeAcquireDispatcherDatabaseLockAtDpcLevel() +#define KeReleaseDispatcherDatabaseLockFromDpcLevel() +#else +#define KeInitializeDispatcher() KeInitializeSpinLock(&DispatcherDatabaseLock); +#define KeAcquireDispatcherDatabaseLock() KfAcquireSpinLock(&DispatcherDatabaseLock); +#define KeAcquireDispatcherDatabaseLockAtDpcLevel() \ + KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock); +#define KeReleaseDispatcherDatabaseLockFromDpcLevel() \ + KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock); +#endif + +/* The following macro initializes a dispatcher object's header */ +#define KeInitializeDispatcherHeader(Header, t, s, State) \ +{ \ + (Header)->Type = t; \ + (Header)->Absolute = 0; \ + (Header)->Inserted = 0; \ + (Header)->Size = s; \ + (Header)->SignalState = State; \ + InitializeListHead(&((Header)->WaitListHead)); \ }
-#define KeLeaveCriticalRegion() \ -{ \ - PKTHREAD _Thread = KeGetCurrentThread(); \ - if((_Thread) && (++_Thread->KernelApcDisable == 0)) \ - { \ - if (!IsListEmpty(&_Thread->ApcState.ApcListHead[KernelMode]) && \ - (_Thread->SpecialApcDisable == 0)) \ - { \ - KiCheckForKernelApcDelivery(); \ - } \ - } \ +/* The following macro satisfies the wait of any dispatcher object */ +#define KiSatisfyObjectWait(Object, Thread) \ +{ \ + /* Special case for Mutants */ \ + if ((Object)->Header.Type == MutantObject) \ + { \ + /* Decrease the Signal State */ \ + (Object)->Header.SignalState--; \ + \ + /* Check if it's now non-signaled */ \ + if (!(Object)->Header.SignalState) \ + { \ + /* Set the Owner Thread */ \ + (Object)->OwnerThread = Thread; \ + \ + /* Disable APCs if needed */ \ + Thread->KernelApcDisable -= (Object)->ApcDisable; \ + \ + /* Check if it's abandoned */ \ + if ((Object)->Abandoned) \ + { \ + /* Unabandon it */ \ + (Object)->Abandoned = FALSE; \ + \ + /* Return Status */ \ + Thread->WaitStatus = STATUS_ABANDONED; \ + } \ + \ + /* Insert it into the Mutant List */ \ + InsertHeadList(&Thread->MutantListHead, \ + &(Object)->MutantListEntry); \ + } \ + } \ + else if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \ + EventSynchronizationObject) \ + { \ + /* Synchronization Timers and Events just get un-signaled */ \ + (Object)->Header.SignalState = 0; \ + } \ + else if ((Object)->Header.Type == SemaphoreObject) \ + { \ + /* These ones can have multiple states, so we only decrease it */ \ + (Object)->Header.SignalState--; \ + } \ }
-#define KEBUGCHECKWITHTF(a,b,c,d,e,f) \ - DbgPrint("KeBugCheckWithTf at %s:%i\n",__FILE__,__LINE__), KeBugCheckWithTf(a,b,c,d,e,f) +/* The following macro satisfies the wait of a mutant dispatcher object */ +#define KiSatisfyMutantWait(Object, Thread) \ +{ \ + /* Decrease the Signal State */ \ + (Object)->Header.SignalState--; \ + \ + /* Check if it's now non-signaled */ \ + if (!(Object)->Header.SignalState) \ + { \ + /* Set the Owner Thread */ \ + (Object)->OwnerThread = Thread; \ + \ + /* Disable APCs if needed */ \ + Thread->KernelApcDisable -= (Object)->ApcDisable; \ + \ + /* Check if it's abandoned */ \ + if ((Object)->Abandoned) \ + { \ + /* Unabandon it */ \ + (Object)->Abandoned = FALSE; \ + \ + /* Return Status */ \ + Thread->WaitStatus = STATUS_ABANDONED; \ + } \ + \ + /* Insert it into the Mutant List */ \ + InsertHeadList(&Thread->MutantListHead, \ + &(Object)->MutantListEntry); \ + } \ +}
-#define MAXIMUM_PROCESSORS 32 +/* The following macro satisfies the wait of any nonmutant dispatcher object */ +#define KiSatisfyNonMutantWait(Object, Thread) \ +{ \ + if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \ + EventSynchronizationObject) \ + { \ + /* Synchronization Timers and Events just get un-signaled */ \ + (Object)->Header.SignalState = 0; \ + } \ + else if ((Object)->Header.Type == SemaphoreObject) \ + { \ + /* These ones can have multiple states, so we only decrease it */ \ + (Object)->Header.SignalState--; \ + } \ +}
+/* The following macro satisfies multiple objects in a wait state */ +#define KiSatisifyMultipleObjectWaits(FirstBlock) \ +{ \ + PKWAIT_BLOCK WaitBlock = FirstBlock; \ + PKTHREAD WaitThread = WaitBlock->Thread; \ + \ + /* Loop through all the Wait Blocks, and wake each Object */ \ + do \ + { \ + /* Make sure it hasn't timed out */ \ + if (WaitBlock->WaitKey != STATUS_TIMEOUT) \ + { \ + /* Wake the Object */ \ + KiSatisfyObjectWait((PKMUTANT)WaitBlock->Object, WaitThread); \ + } \ + \ + /* Move to the next block */ \ + WaitBlock = WaitBlock->NextWaitBlock; \ + } while (WaitBlock != FirstBlock); \ +}
+extern KSPIN_LOCK DispatcherDatabaseLock; + +#define KeEnterCriticalRegion() \ +{ \ + PKTHREAD _Thread = KeGetCurrentThread(); \ + if (_Thread) _Thread->KernelApcDisable--; \ +} + +#define KeLeaveCriticalRegion() \ +{ \ + PKTHREAD _Thread = KeGetCurrentThread(); \ + if((_Thread) && (++_Thread->KernelApcDisable == 0)) \ + { \ + if (!IsListEmpty(&_Thread->ApcState.ApcListHead[KernelMode]) && \ + (_Thread->SpecialApcDisable == 0)) \ + { \ + KiCheckForKernelApcDelivery(); \ + } \ + } \ +} + +#define KEBUGCHECKWITHTF(a,b,c,d,e,f) \ + DbgPrint("KeBugCheckWithTf at %s:%i\n",__FILE__,__LINE__), \ + KeBugCheckWithTf(a,b,c,d,e,f) + /* INTERNAL KERNEL FUNCTIONS ************************************************/
/* threadsch.c ********************************************************************/ @@ -243,27 +386,11 @@ PVOID SystemArgument2 );
-KIRQL -__inline -FASTCALL -KeAcquireDispatcherDatabaseLock(VOID); - VOID -__inline FASTCALL -KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID); - -VOID -__inline -FASTCALL KeReleaseDispatcherDatabaseLock(KIRQL Irql);
VOID -__inline -FASTCALL -KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID); - -VOID STDCALL KeInitializeThread( struct _KPROCESS* Process, @@ -313,16 +440,6 @@ );
VOID -__inline -FASTCALL -KeInitializeDispatcherHeader( - DISPATCHER_HEADER* Header, - ULONG Type, - ULONG Size, - ULONG SignalState -); - -VOID NTAPI KeDumpStackFrames(PULONG Frame);
@@ -362,14 +479,6 @@ LARGE_INTEGER DueTime );
-VOID -__inline -FASTCALL -KiSatisfyObjectWait( - PDISPATCHER_HEADER Object, - PKTHREAD Thread -); - BOOLEAN __inline FASTCALL @@ -379,14 +488,9 @@ );
VOID -__inline FASTCALL -KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock); - -VOID -FASTCALL KiWaitTest( - PDISPATCHER_HEADER Object, + PVOID Object, KPRIORITY Increment );
@@ -507,11 +611,6 @@ KeInitDispatcher(VOID);
VOID -__inline -FASTCALL -KeInitializeDispatcher(VOID); - -VOID NTAPI KiInitializeSystemClock(VOID);
_____
Modified: trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S --- trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S 2006-01-05 01:49:00 UTC (rev 20567) +++ trunk/reactos/ntoskrnl/ke/i386/ctxswitch.S 2006-01-05 04:26:55 UTC (rev 20568) @@ -21,6 +21,8 @@
/* GLOBALS ****************************************************************/
+.extern _DispatcherDatabaseLock + /* FUNCTIONS ****************************************************************/
/*++ @@ -244,7 +246,10 @@ pop [ebx+KPCR_EXCEPTION_LIST]
/* Return */ - call @KeReleaseDispatcherDatabaseLockFromDpcLevel@0 +//#ifdef CONFIG_SMP + mov ecx, _DispatcherDatabaseLock + call @KefReleaseSpinLockFromDpcLevel@4 +//#endif ret
/*++ _____
Modified: trunk/reactos/ntoskrnl/ke/kthread.c --- trunk/reactos/ntoskrnl/ke/kthread.c 2006-01-05 01:49:00 UTC (rev 20567) +++ trunk/reactos/ntoskrnl/ke/kthread.c 2006-01-05 04:26:55 UTC (rev 20568) @@ -819,7 +819,7 @@
DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread, Process); KeInitializeDispatcherHeader(&Thread->DispatcherHeader, ThreadObject, - sizeof(KTHREAD), + sizeof(KTHREAD) / sizeof(LONG), FALSE);
DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n", _____
Modified: trunk/reactos/ntoskrnl/ke/wait.c --- trunk/reactos/ntoskrnl/ke/wait.c 2006-01-05 01:49:00 UTC (rev 20567) +++ trunk/reactos/ntoskrnl/ke/wait.c 2006-01-05 04:26:55 UTC (rev 20568) @@ -2,7 +2,7 @@
* COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel * FILE: ntoskrnl/ke/wait.c - * PURPOSE: Manages dispatch level wait-related code + * PURPOSE: Manages waiting for Dispatcher Objects * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) * Gunnar Dalsnes */ @@ -16,7 +16,7 @@
/* GLOBALS ******************************************************************/
-static KSPIN_LOCK DispatcherDatabaseLock; +KSPIN_LOCK DispatcherDatabaseLock;
/* Tells us if the Timer or Event is a Syncronization or Notification Object */ #define TIMER_OR_EVENT_TYPE 0x7L @@ -30,38 +30,58 @@ __inline FASTCALL KiCheckAlertability(BOOLEAN Alertable, - PKTHREAD CurrentThread, + PKTHREAD Thread, KPROCESSOR_MODE WaitMode, PNTSTATUS Status) { - /* At this point, we have to do a wait, so make sure we can make the thread Alertable if requested */ - if (Alertable) { - + /* + * 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"); + if (Thread->Alerted[(int)WaitMode]) + { + Thread->Alerted[(int)WaitMode] = FALSE; + DPRINT("Thread was Alerted in the specified Mode\n"); *Status = STATUS_ALERTED; return TRUE; - - /* If there are User APCs Pending, then we can't really be alertable */ - } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) && - (WaitMode != KernelMode)) { - + } + else if ((WaitMode != KernelMode) && + (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) + { + /* If there are User APCs Pending, then we can't really be alertable */ DPRINT("APCs are Pending\n"); - CurrentThread->ApcState.UserApcPending = TRUE; + Thread->ApcState.UserApcPending = TRUE; *Status = STATUS_USER_APC; return TRUE; } - - /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */ - } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode != KernelMode)) { - DPRINT("APCs are Pending\n"); - *Status = STATUS_USER_APC; + else if (Thread->Alerted[KernelMode]) + { + /* + * The thread is not alerted in the mode given, but it is alerted + * in kernel-mode. + */ + Thread->Alerted[KernelMode] = FALSE; + DPRINT("Thread was Alerted in Kernel-Mode\n"); + *Status = STATUS_ALERTED; return TRUE; + } } + else if ((WaitMode != KernelMode) && + (Thread->ApcState.UserApcPending)) + { + /* + * If there are User APCs Pending and we are waiting in usermode, + * then we must notify the caller + */ + DPRINT("APCs are Pending\n"); + *Status = STATUS_USER_APC; + return TRUE; + }
+ /* Stay in the loop */ return FALSE; }
@@ -90,14 +110,14 @@ DPRINT("Entering KeDelayExecutionThread\n");
/* Check if the lock is already held */ - if (CurrentThread->WaitNext) { - + if (CurrentThread->WaitNext) + { /* Lock is held, disable Wait Next */ DPRINT("Lock is held\n"); CurrentThread->WaitNext = FALSE; - - } else { - + } + else + { /* Lock not held, acquire it */ DPRINT("Lock is not held, acquiring\n"); CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); @@ -107,12 +127,11 @@ TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
/* Start Wait Loop */ - do { + do + { + /* Chceck if we can do an alertable wait, if requested */ + if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status)) break;
- /* We are going to wait no matter what (that's the point), so test Alertability */ - if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status)) - break; - /* Set Timer */ ThreadTimer = &CurrentThread->Timer;
@@ -129,30 +148,31 @@ InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
/* Insert the Timer into the Timer Lists and enable it */ - if (!KiInsertTimer(ThreadTimer, *Interval)) { - + if (!KiInsertTimer(ThreadTimer, *Interval)) + { /* FIXME: The timer already expired, we should find a new ready thread */ Status = STATUS_SUCCESS; break; }
/* Handle Kernel Queues */ - if (CurrentThread->Queue) { - + if (CurrentThread->Queue) + { DPRINT("Waking Queue\n"); KiWakeQueue(CurrentThread->Queue); }
/* Block the Thread */ - DPRINT("Blocking the Thread: %d, %d, %x\n", Alertable, WaitMode, KeGetCurrentThread()); + DPRINT("Blocking the Thread: %d, %d, %x\n", + Alertable, WaitMode, KeGetCurrentThread()); KiBlockThread(&Status, Alertable, WaitMode, DelayExecution);
/* Check if we were executing an APC or if we timed out */ - if (Status != STATUS_KERNEL_APC) { - + if (Status != STATUS_KERNEL_APC) + { /* This is a good thing */ if (Status == STATUS_TIMEOUT) Status = STATUS_SUCCESS;
@@ -160,13 +180,14 @@ return Status; }
- DPRINT("Looping Again\n"); + DPRINT("Looping Again\n"); // FIXME: Need to modify interval CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + } + while (TRUE);
- } while (TRUE); - /* Release the Lock, we are done */ - DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n", KeGetCurrentThread(), Status); + DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n", + KeGetCurrentThread(), Status); KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); return Status; } @@ -194,33 +215,32 @@ BOOLEAN Alertable, PLARGE_INTEGER Timeout) { - PDISPATCHER_HEADER CurrentObject; + PKMUTANT 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) { - + if (CurrentThread->WaitNext) + { /* Lock is held, disable Wait Next */ DPRINT("Lock is held\n"); CurrentThread->WaitNext = FALSE; - - } else { - + } + else + { /* Lock not held, acquire it */ DPRINT("Lock is not held, acquiring\n"); CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); }
/* Start the actual Loop */ - do { - + do + { /* Get the current Wait Status */ WaitStatus = CurrentThread->WaitStatus;
@@ -228,30 +248,38 @@ CurrentThread->WaitBlockList = WaitBlock = &CurrentThread->WaitBlock[0];
/* Get the Current Object */ - CurrentObject = (PDISPATCHER_HEADER)Object; + CurrentObject = (PKMUTANT)Object;
- /* Check if the Object is Signaled */ - if (KiIsObjectSignaled(CurrentObject, CurrentThread)) { - - /* Just unwait this guy and exit */ - if (CurrentObject->SignalState != (LONG)MINLONG) { - - /* It has a normal signal state, so unwait it and return */ - KiSatisfyObjectWait(CurrentObject, CurrentThread); - Status = STATUS_WAIT_0; - goto DontWait; - - } else { - - /* Is this a Mutant? */ - if (CurrentObject->Type == MutantObject) { - + /* Check if it's a mutant */ + if (CurrentObject->Header.Type == MutantObject) + { + /* Check its signal state or if we own it */ + if ((CurrentObject->Header.SignalState > 0) || + (CurrentThread == CurrentObject->OwnerThread)) + { + /* Just unwait this guy and exit */ + if (CurrentObject->Header.SignalState != (LONG)MINLONG) + { + /* It has a normal signal state, so unwait it and return */ + KiSatisfyMutantWait(CurrentObject, CurrentThread); + Status = STATUS_WAIT_0; + goto DontWait; + } + else + { /* According to wasm.ru, we must raise this exception (tested and true) */
KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); } } } + else if (CurrentObject->Header.SignalState > 0) + { + /* Another satisfied object */ + KiSatisfyNonMutantWait(CurrentObject, CurrentThread); + Status = STATUS_WAIT_0; + goto DontWait; + }
/* Set up the Wait Block */ WaitBlock->Object = CurrentObject; @@ -261,18 +289,17 @@ WaitBlock->NextWaitBlock = WaitBlock;
/* Make sure we can satisfy the Alertable request */ - if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status)) - break; + if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status)) break;
/* Set the Wait Status */ CurrentThread->WaitStatus = Status;
/* Enable the Timeout Timer if there was any specified */ - if (Timeout != NULL) { - - /* However if 0 timeout was specified, then we must fail since we need to peform a wait */ - if (!Timeout->QuadPart) { - + if (Timeout) + { + /* Fail if the timeout interval is actually 0 */ + if (!Timeout->QuadPart) + { /* Return a timeout */ Status = STATUS_TIMEOUT; goto DontWait; @@ -294,48 +321,53 @@
/* Link the timer to this Wait Block */ InitializeListHead(&ThreadTimer->Header.WaitListHead); - InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry); + InsertTailList(&ThreadTimer->Header.WaitListHead, + &TimerWaitBlock->WaitListEntry);
/* Insert the Timer into the Timer Lists and enable it */ - if (!KiInsertTimer(ThreadTimer, *Timeout)) { - - /* Return a timeout if we couldn't insert the timer for some reason */ + if (!KiInsertTimer(ThreadTimer, *Timeout)) + { + /* Return a timeout if we couldn't insert the timer */ Status = STATUS_TIMEOUT; goto DontWait; } }
/* Link the Object to this Wait Block */ - InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry); + InsertTailList(&CurrentObject->Header.WaitListHead, + &WaitBlock->WaitListEntry);
/* Handle Kernel Queues */ - if (CurrentThread->Queue) { - + 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()); + DPRINT("Blocking the Thread: %d, %d, %d, %x\n", + Alertable, WaitMode, WaitReason, KeGetCurrentThread()); KiBlockThread(&Status, Alertable, WaitMode, (UCHAR)WaitReason);
/* Check if we were executing an APC */ - if (Status != STATUS_KERNEL_APC) { - + if (Status != STATUS_KERNEL_APC) + { /* Return Status */ return Status; }
- DPRINT("Looping Again\n"); + /* Loop again and acquire the dispatcher lock */ + DPRINT("Looping Again\n"); // FIXME: Change interval CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + } + while (TRUE);
- } while (TRUE); - /* Release the Lock, we are done */ - DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status); + DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", + KeGetCurrentThread(), Status); KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); return Status;
@@ -344,7 +376,8 @@ KiAdjustQuantumThread(CurrentThread);
/* Release & Return */ - DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n. We did not wait.", KeGetCurrentThread(), Status); + DPRINT("Quick-return from KeWaitForMultipleObjects(), %x. Status: %d\n.", + KeGetCurrentThread(), Status); KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); return Status; } @@ -362,7 +395,7 @@ PLARGE_INTEGER Timeout, PKWAIT_BLOCK WaitBlockArray) { - PDISPATCHER_HEADER CurrentObject; + PKMUTANT CurrentObject; PKWAIT_BLOCK WaitBlock; PKWAIT_BLOCK TimerWaitBlock; PKTIMER ThreadTimer; @@ -371,51 +404,45 @@ ULONG WaitIndex; NTSTATUS Status; NTSTATUS WaitStatus; - DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) " - "PsGetCurrentThread() %x, Timeout %x\n", Count, Object, PsGetCurrentThread(), Timeout); + "PsGetCurrentThread() %x, Timeout %x\n", + Count, Object, PsGetCurrentThread(), Timeout);
/* Set the Current Thread */ CurrentThread = KeGetCurrentThread();
/* Check if the lock is already held */ - if (CurrentThread->WaitNext) { - + if (CurrentThread->WaitNext) + { /* Lock is held, disable Wait Next */ DPRINT("Lock is held\n"); CurrentThread->WaitNext = FALSE; - - } else { - + } + 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) { - + if (!WaitBlockArray) + { /* Check in regards to the Thread Object Limit */ - if (Count > THREAD_WAIT_OBJECTS) { + if (Count > THREAD_WAIT_OBJECTS) KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
- KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); - } - /* Use the Thread's Wait Block */ WaitBlockArray = &CurrentThread->WaitBlock[0]; - - } else { - + } + else + { /* Using our own Block Array. Check in regards to System Object Limit */ - if (Count > MAXIMUM_WAIT_OBJECTS) { - - KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); - } + if (Count > MAXIMUM_WAIT_OBJECTS) KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); }
/* Start the actual Loop */ - do { - + do + { /* Get the current Wait Status */ WaitStatus = CurrentThread->WaitStatus;
@@ -426,47 +453,76 @@ AllObjectsSignaled = TRUE;
/* First, we'll try to satisfy the wait directly */ - for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) { - + for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) + { /* Get the Current Object */ - CurrentObject = (PDISPATCHER_HEADER)Object[WaitIndex]; + CurrentObject = (PKMUTANT)Object[WaitIndex];
- /* Check if the Object is Signaled */ - if (KiIsObjectSignaled(CurrentObject, CurrentThread)) { - - /* Check what kind of wait this is */ - if (WaitType == WaitAny) { - - /* This is a Wait Any, so just unwait this guy and exit */ - if (CurrentObject->SignalState != (LONG)MINLONG) { - - /* It has a normal signal state, so unwait it and return */ - KiSatisfyObjectWait(CurrentObject, CurrentThread); - Status = STATUS_WAIT_0 | WaitIndex; - goto DontWait; - - } else { - - /* Is this a Mutant? */ - if (CurrentObject->Type == MutantObject) { - + /* Check the type of wait */ + if (WaitType == WaitAny) + { + /* Check if the Object is a mutant */ + if (CurrentObject->Header.Type == MutantObject) + { + /* Check if it's signaled */ + if ((CurrentObject->Header.SignalState > 0) || + (CurrentThread == CurrentObject->OwnerThread)) + { + /* This is a Wait Any, so just unwait this and exit */ + if (CurrentObject->Header.SignalState != (LONG)MINLONG) + { + /* Normal signal state, so unwait it and return */ + KiSatisfyMutantWait(CurrentObject, CurrentThread); + Status = STATUS_WAIT_0 | WaitIndex; + goto DontWait; + } + else + { /* 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; + else if (CurrentObject->Header.SignalState > 0) + { + /* Another signaled object, unwait and return */ + KiSatisfyNonMutantWait(CurrentObject, CurrentThread); + Status = WaitIndex; + goto DontWait; + } } + else + { + /* Check if we're dealing with a mutant again */ + if (CurrentObject->Header.Type == MutantObject) + { + /* Check if it has an invalid count */ + if ((CurrentThread == CurrentObject->OwnerThread) && + (CurrentObject->Header.SignalState == MINLONG)) + { + /* Raise an exception */ + KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); + ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + } + else if ((CurrentObject->Header.SignalState <= 0) && + (CurrentThread != CurrentObject->OwnerThread)) + { + /* We don't own it, can't satisfy the wait */ + AllObjectsSignaled = FALSE; + } + } + else if (CurrentObject->Header.SignalState <= 0) + { + /* Not signaled, can't satisfy */ + AllObjectsSignaled = FALSE; + } + }
/* Set up a Wait Block for this Object */ WaitBlock->Object = CurrentObject; WaitBlock->Thread = CurrentThread; - WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0 + WaitIndex); + WaitBlock->WaitKey = (USHORT)WaitIndex; WaitBlock->WaitType = (USHORT)WaitType; WaitBlock->NextWaitBlock = WaitBlock + 1;
@@ -479,8 +535,8 @@ WaitBlock->NextWaitBlock = WaitBlockArray;
/* Check if this is a Wait All and all the objects are signaled */ - if ((WaitType == WaitAll) && (AllObjectsSignaled)) { - + if ((WaitType == WaitAll) && (AllObjectsSignaled)) + { /* Return to the Root Wait Block */ WaitBlock = CurrentThread->WaitBlockList;
@@ -491,18 +547,17 @@ }
/* Make sure we can satisfy the Alertable request */ - if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status)) - break; + if (KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status)) break;
/* Set the Wait Status */ CurrentThread->WaitStatus = Status;
/* Enable the Timeout Timer if there was any specified */ - if (Timeout != NULL) { - - /* However if 0 timeout was specified, then we must fail since we need to peform a wait */ - if (!Timeout->QuadPart) { - + if (Timeout) + { + /* Make sure the timeout interval isn't actually 0 */ + if (!Timeout->QuadPart) + { /* Return a timeout */ Status = STATUS_TIMEOUT; goto DontWait; @@ -526,9 +581,9 @@ InitializeListHead(&ThreadTimer->Header.WaitListHead);
/* Insert the Timer into the Timer Lists and enable it */ - if (!KiInsertTimer(ThreadTimer, *Timeout)) { - - /* Return a timeout if we couldn't insert the timer for some reason */ + if (!KiInsertTimer(ThreadTimer, *Timeout)) + { + /* Return a timeout if we couldn't insert the timer */ Status = STATUS_TIMEOUT; goto DontWait; } @@ -536,28 +591,30 @@
/* Insert into Object's Wait List*/ WaitBlock = CurrentThread->WaitBlockList; - do { - + do + { /* Get the Current Object */ CurrentObject = WaitBlock->Object;
/* Link the Object to this Wait Block */ - InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry); + InsertTailList(&CurrentObject->Header.WaitListHead, + &WaitBlock->WaitListEntry);
/* Move to the next Wait Block */ WaitBlock = WaitBlock->NextWaitBlock; - } while (WaitBlock != WaitBlockArray); + } + while (WaitBlock != WaitBlockArray);
/* Handle Kernel Queues */ - if (CurrentThread->Queue) { - + 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()); + DPRINT("Blocking the Thread: %d, %d, %d, %x\n", + Alertable, WaitMode, WaitReason, KeGetCurrentThread()); KiBlockThread(&Status, Alertable, WaitMode, @@ -565,17 +622,18 @@
/* Check if we were executing an APC */ DPRINT("Thread is back\n"); - if (Status != STATUS_KERNEL_APC) { - + if (Status != STATUS_KERNEL_APC) + { /* Return Status */ return Status; } [truncated at 1000 lines; 408 more skipped]