- New ERESOURCE implementation: fixes the return value of some functions
(VOID vs NTSTATUS, USHORT vs ULONG), as well as optimized the code loops
and general structure of the code. Additionnaly, functions do not simply
call other functions with similar names; the exact implementation of
each function has now been properly separated (see the DDK for more
information on this) to have the most optimized scenarios.
- Also, the spinlock is not actually acquired on non-SMP builds;
instead, interrupts are blocked and unblocked for acquire/release, this
optimizes locking.
- Added many asserts and bugcheck scenarios.
- Added thread priority boosting.
- Added some debugging helpers and deadlock detection.
- Added RESOURCE_NOT_OWNED bugcehck message.
* Thanks again to Waxdragon (Andrew) for testing this build.
Modified: trunk/reactos/include/ndk/extypes.h
Modified: trunk/reactos/ntoskrnl/ex/init.c
Modified: trunk/reactos/ntoskrnl/ex/resource.c
Modified: trunk/reactos/ntoskrnl/include/internal/ex.h
Modified: trunk/reactos/ntoskrnl/include/internal/tag.h
Modified: trunk/reactos/ntoskrnl/ntoskrnl.mc
Modified: trunk/reactos/w32api/include/ddk/winddk.h
_____
Modified: trunk/reactos/include/ndk/extypes.h
--- trunk/reactos/include/ndk/extypes.h 2006-01-05 15:32:08 UTC (rev
20579)
+++ trunk/reactos/include/ndk/extypes.h 2006-01-05 16:24:32 UTC (rev
20580)
@@ -118,6 +118,11 @@
#define EX_PUSH_LOCK_FLAGS_WAIT 2
//
+// Resource (ERESOURCE) Flags
+//
+#define ResourceHasDisabledPriorityBoost 0x08
+
+//
// Shutdown types for NtShutdownSystem
//
typedef enum _SHUTDOWN_ACTION
_____
Modified: trunk/reactos/ntoskrnl/ex/init.c
--- trunk/reactos/ntoskrnl/ex/init.c 2006-01-05 15:32:08 UTC (rev
20579)
+++ trunk/reactos/ntoskrnl/ex/init.c 2006-01-05 16:24:32 UTC (rev
20580)
@@ -489,7 +489,7 @@
DPRINT("Process created successfully\n");
return STATUS_SUCCESS;
}
-
+
VOID
INIT_FUNCTION
STDCALL
@@ -543,6 +543,9 @@
InitializeListHead(&KiProfileSourceListHead);
KeInitializeSpinLock(&KiProfileLock);
+ /* Initialize resources */
+ ExpResourceInitialization();
+
/* Load basic Security for other Managers */
if (!SeInit1()) KEBUGCHECK(SECURITY_INITIALIZATION_FAILED);
_____
Modified: trunk/reactos/ntoskrnl/ex/resource.c
--- trunk/reactos/ntoskrnl/ex/resource.c 2006-01-05 15:32:08 UTC
(rev 20579)
+++ trunk/reactos/ntoskrnl/ex/resource.c 2006-01-05 16:24:32 UTC
(rev 20580)
@@ -1,955 +1,2315 @@
-/* $Id$
- *
+/*
* COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS kernel
+ * PROJECT: ReactOS Kernel
* FILE: ntoskrnl/ex/resource.c
- * PURPOSE: Resource synchronization construct
- *
- * PROGRAMMERS: No programmer listed.
+ * PURPOSE: ERESOURCE Implementation
+ * PROGRAMMERS: Alex Ionescu (alex(a)relsoft.net)
*/
-
-/*
- * Usage of ERESOURCE members is not documented.
- * From names of members and functionnalities, we can assume :
- *
- * OwnerTable = list of threads who have shared access(if more than
one)
- * ActiveCount = number of threads who have access to the resource
- * Flag = bits : ResourceOwnedExclusive=0x80
- * ResourceNeverExclusive=0x10
- * ResourceReleaseByOtherThread=0x20
- * ResourceDisableBoost=0x08
- * SharedWaiters = semaphore, used to manage wait list of shared
waiters.
- * ExclusiveWaiters = event, used to manage wait list of exclusive
waiters.
- * OwnerThreads[0]= thread who have exclusive access
- * OwnerThreads[1]= if only one thread own the resource
- * thread who have shared access
- * else
- * OwnerThread=0
- * and TableSize = number of entries in the owner
table
- * NumberOfExclusiveWaiters = number of threads waiting for exclusive
access.
- * NumberOfSharedWaiters = number of threads waiting for exclusive
access.
- *
+/* WARNING:
+ * This implementation is the Windows NT 5.x one.
+ * NT 6.0 beta has optimized the OwnerThread entry array
+ * and the internals of ExpFindEntryForThread and ExpFindFreeEntry
+ * need to be modified accordingly in order to support the WDK.
+ * These changes will not be made here until NT 6.0 reaches RTM status
since
+ * there is a possibility that they will be removed; as such, do NOT
+ * update ERESOURCE/relevant code to the WDK definition.
*/
-#define ResourceDisableBoost 0x08
-
/* INCLUDES
*****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
-/* FUNCTIONS
*****************************************************************/
+#if defined (ALLOC_PRAGMA)
+#pragma alloc_text(INIT, ExpResourceInitialization)
+#endif
+/* Macros for reading resource flags */
+#define IsExclusiveWaiting(r) (r->NumberOfExclusiveWaiters)
+#define IsSharedWaiting(r) (r->NumberOfSharedWaiters)
+#define IsOwnedExclusive(r) (r->Flag & ResourceOwnedExclusive)
-BOOLEAN
-STDCALL
-ExTryToAcquireResourceExclusiveLite (
- PERESOURCE Resource
- )
-/*
- * FUNCTION: Attempts to require the resource for exclusive access
- * ARGUMENTS:
- * Resource = Points to the resource of be acquired
- * RETURNS: TRUE if the resource was acquired for the caller
- * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
- */
+/*
DATA********************************************************************
***/
+
+LARGE_INTEGER ExpTimeout;
+ULONG ExpResourceTimeoutCount = 90 * 3600 / 2;
+KSPIN_LOCK ExpResourceSpinLock;
+LIST_ENTRY ExpSystemResourcesList;
+BOOLEAN ExResourceStrict = FALSE; /* FIXME */
+
+/* PRIVATE FUNCTIONS
*********************************************************/
+
+#if DBG
+/*++
+ * @name ExpVerifyResource
+ *
+ * The ExpVerifyResource routine verifies the correctness of an
ERESOURCE
+ *
+ * @param Resource
+ * Pointer to the resource being verified.
+ *
+ * @return None.
+ *
+ * @remarks Only present on DBG builds.
+ *
+ *--*/
+VOID
+NTAPI
+ExpVerifyResource(IN PERESOURCE Resource)
{
- return(ExAcquireResourceExclusiveLite(Resource,FALSE));
+ /* Verify the resource data */
+ ASSERT((((ULONG_PTR)Resource) & (sizeof(ULONG_PTR) - 1)) == 0);
+ ASSERT(!Resource->SharedWaiters ||
+ Resource->SharedWaiters->Header.Type == SemaphoreObject);
+ ASSERT(!Resource->SharedWaiters ||
+ Resource->SharedWaiters->Header.Size == (sizeof(KSEMAPHORE)
/ sizeof(ULONG)));
+ ASSERT(!Resource->ExclusiveWaiters ||
+ Resource->ExclusiveWaiters->Header.Type ==
SynchronizationEvent);
+ ASSERT(!Resource->ExclusiveWaiters ||
+ Resource->ExclusiveWaiters->Header.Size == (sizeof(KEVENT)
/ sizeof(ULONG)));
}
-#ifdef ExAcquireResourceExclusive
-#undef ExAcquireResourceExclusive
+/*++
+ * @name ExpCheckForApcsDisabled
+ *
+ * The ExpCheckForApcsDisabled routine checks if Kernel APCs are
still
+ * enabled when they should be disabled, and optionally
breakpoints.
+ *
+ * @param BreakIfTrue
+ * Specifies if we should break if an invalid APC State is
detected.
+ *
+ * @param Resource
+ * Pointer to the resource being checked.
+ *
+ * @param Thread
+ * Pointer to the thread being checked.
+ *
+ * @return None.
+ *
+ * @remarks Only present on DBG builds. Depends on ExResourceStrict
value.
+ *
+ *--*/
+VOID
+NTAPI
+ExpCheckForApcsDisabled(IN BOOLEAN BreakIfTrue,
+ IN PERESOURCE Resource,
+ IN PETHREAD Thread)
+{
+ /* Check if we should care and check if we should break */
+ if ((ExResourceStrict) &&
+ (BreakIfTrue) &&
+ !(Thread->SystemThread) &&
+ !(Thread->Tcb.CombinedApcDisable))
+ {
+ /* Bad! */
+ DPRINT1("EX: resource: APCs still enabled before resource %p
acquire "
+ "!!!\n", Resource);
+ DbgBreakPoint();
+ }
+}
#endif
-/*
- * @implemented
- */
-BOOLEAN
-STDCALL
-ExAcquireResourceExclusive (
- PERESOURCE Resource,
- BOOLEAN Wait
- )
+/*++
+ * @name ExpResourceInitialization
+ *
+ * The ExpResourceInitialization routine initializes resources for
use.
+ *
+ * @param None.
+ *
+ * @return None.
+ *
+ * @remarks This routine should only be called once, during system
startup.
+ *
+ *--*/
+VOID
+NTAPI
+INIT_FUNCTION
+ExpResourceInitialization(VOID)
{
- return(ExAcquireResourceExclusiveLite(Resource,Wait));
+ /* Setup the timeout */
+ ExpTimeout.QuadPart = Int32x32To64(4, -10000000);
+ InitializeListHead(&ExpSystemResourcesList);
+ KeInitializeSpinLock(&ExpResourceSpinLock);
}
-
-/*
- * @implemented
- */
-BOOLEAN
-STDCALL
-ExAcquireResourceExclusiveLite (
- PERESOURCE Resource,
- BOOLEAN Wait
- )
-/*
- * FUNCTION: Acquires a resource exclusively for the calling thread
- * ARGUMENTS:
- * Resource = Points to the resource to acquire
- * Wait = Is set to TRUE if the caller should wait to acquire the
- * resource if it can't be acquired immediately
- * RETURNS: TRUE if the resource was acquired,
- * FALSE otherwise
- * NOTES: Must be called at IRQL < DISPATCH_LEVEL
- */
+/*++
+ * @name ExpAllocateExclusiveWaiterEvent
+ *
+ * The ExpAllocateExclusiveWaiterEvent routine creates the event
that will
+ * be used by exclusive waiters on the resource.
+ *
+ * @param Resource
+ * Pointer to the resource.
+ *
+ * @param OldIrql
+ * Pointer to current IRQL. TBC: Pointer to in-stack queued
spinlock.
+ *
+ * @return None.
+ *
+ * @remarks The pointer to the event must be atomically set.
+ *
+ *--*/
+VOID
+NTAPI
+ExpAllocateExclusiveWaiterEvent(IN PERESOURCE Resource,
+ IN PKIRQL OldIrql)
{
- KIRQL oldIrql;
+ PKEVENT Event;
- DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n",
- Resource, Wait);
+ /* Release the lock */
+ ExReleaseResourceLock(&Resource->SpinLock, *OldIrql);
- ASSERT_IRQL_LESS(DISPATCH_LEVEL);
+ /* Allocate the event */
+ Event = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(KEVENT),
+ TAG_RESOURCE_EVENT);
-/* undefed for now, since cdfs must be fixed first */
-#if 0
- /* At least regular kmode APC's must be disabled
- * Note that this requirement is missing in old DDK's */
- ASSERT(KeGetCurrentThread() == NULL || /* <-Early in the boot
process the current thread is obseved to be NULL */
- KeGetCurrentThread()->KernelApcDisable ||
- KeGetCurrentIrql() == APC_LEVEL);
-#endif
+ /* Initialize it */
+ KeInitializeEvent(Event, SynchronizationEvent, FALSE);
- KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
+ /* Set it */
+ if (InterlockedCompareExchangePointer(&Resource->ExclusiveWaiters,
+ Event,
+ NULL))
+ {
+ /* Someone already set it, free our event */
+ DPRINT1("WARNING: Handling race condition\n");
+ ExFreePool(Event);
+ }
- /* resource already locked */
- if((Resource->Flag & ResourceOwnedExclusive)
- && Resource->OwnerThreads[0].OwnerThread ==
ExGetCurrentResourceThread())
- {
- /* it's ok : same lock for same thread */
- Resource->OwnerThreads[0].OwnerCount++;
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
- return(TRUE);
- }
-
- if (Resource->ActiveCount && !Wait)
- {
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
- return(FALSE);
- }
-
- /*
- * This is slightly better than it looks because other exclusive
- * threads who are waiting won't be woken up but there is a race
- * with new threads trying to grab the resource so we must have
- * the spinlock, still normally this loop will only be executed
- * once
- * NOTE: We might want to set a timeout to detect deadlock
- * (10 minutes?)
- */
- while (Resource->ActiveCount)
- {
- Resource->NumberOfExclusiveWaiters++;
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- KeWaitForSingleObject(Resource->ExclusiveWaiters,
- Executive,
- KernelMode,
- FALSE,
- NULL);
- KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
- Resource->NumberOfExclusiveWaiters--;
- }
- Resource->Flag |= ResourceOwnedExclusive;
- Resource->ActiveCount = 1;
- Resource->OwnerThreads[0].OwnerThread =
ExGetCurrentResourceThread();
- Resource->OwnerThreads[0].OwnerCount = 1;
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
- return(TRUE);
+ /* Re-acquire the lock */
+ ExAcquireResourceLock(&Resource->SpinLock, OldIrql);
}
-static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
- ERESOURCE_THREAD ResourceThreadId)
-/*
- * FUNCTION: Removes the current thread from the shared owners of the
resource
- * ARGUMENTS:
- * Resource = Pointer to the resource for which the thread is to
be
- * added
- * NOTE: Must be called with the resource spinlock held
- */
+/*++
+ * @name ExpAllocateSharedWaiterSemaphore
+ *
+ * The ExpAllocateSharedWaiterSemaphore routine creates the
semaphore that
+ * will be used by shared waiters on the resource.
+ *
+ * @param Resource
+ * Pointer to the resource.
+ *
+ * @param OldIrql
+ * Pointer to current IRQL. TBC: Pointer to in-stack queued
spinlock.
+ *
+ * @return None.
+ *
+ * @remarks The pointer to the semaphore must be atomically set.
+ *
+ *--*/
+VOID
+NTAPI
+ExpAllocateSharedWaiterSemaphore(IN PERESOURCE Resource,
+ IN PKIRQL OldIrql)
{
- ULONG i;
+ PKSEMAPHORE Semaphore;
- if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
- {
- Resource->OwnerThreads[1].OwnerCount--;
- if (Resource->OwnerThreads[1].OwnerCount == 0)
- {
- Resource->ActiveCount--;
- Resource->OwnerThreads[1].OwnerThread = 0;
- }
- return(TRUE);
- }
+ /* Release the lock */
+ ExReleaseResourceLock(&Resource->SpinLock, *OldIrql);
- if (Resource->OwnerThreads[1].OwnerThread)
- {
- /* Oh dear, the caller didn't own the resource after all */
- return(FALSE);
- }
+ /* Allocate the semaphore */
+ Semaphore = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(KSEMAPHORE),
+ TAG_RESOURCE_SEMAPHORE);
- for (i=0; i<Resource->OwnerThreads[1].TableSize; i++)
- {
- if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
- {
- Resource->OwnerTable[i].OwnerCount--;
- if (Resource->OwnerTable[i].OwnerCount == 0)
- {
- Resource->ActiveCount--;
- Resource->OwnerTable[i].OwnerThread = 0;
- }
- return TRUE;
- }
- }
- return(FALSE);
+ /* Initialize it */
+ KeInitializeSemaphore(Semaphore, 0, MAXLONG);
+
+ /* Set it */
+ if (InterlockedCompareExchangePointer(&Resource->SharedWaiters,
+ Semaphore,
+ NULL))
+ {
+ /* Someone already set it, free our semaphore */
+ DPRINT1("WARNING: Handling race condition\n");
+ ExFreePool(Semaphore);
+ }
+
+ /* Re-acquire the lock */
+ ExAcquireResourceLock(&Resource->SpinLock, OldIrql);
}
-static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
-/*
- * FUNCTION: Adds the current thread to the shared owners of the
resource
- * ARGUMENTS:
- * Resource = Pointer to the resource for which the thread is
to be
- * added
- * NOTE: Must be called with the resource spinlock held
- */
+/*++
+ * @name ExpExpandResourceOwnerTable
+ *
+ * The ExpExpandResourceOwnerTable routine expands the owner table
of the
+ * specified resource.
+ *
+ * @param Resource
+ * Pointer to the resource.
+ *
+ * @param OldIrql
+ * Pointer to current IRQL. TBC: Pointer to in-stack queued
spinlock.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+ExpExpandResourceOwnerTable(IN PERESOURCE Resource,
+ IN PKIRQL OldIrql)
{
- ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
- POWNER_ENTRY freeEntry;
- ULONG i = 0;
+ POWNER_ENTRY Owner, Table;
+ ULONG NewSize, OldSize;
+ DPRINT("ExpExpandResourceOwnerTable: %p\n", Resource);
- DPRINT("EiAddSharedOwner(Resource 0x%p)\n", Resource);
+ /* Get the owner table */
+ Owner = Resource->OwnerTable;
+ if (!Owner)
+ {
+ /* Start with the default size of 3 */
+ OldSize = 0;
+ NewSize = 3;
+ }
+ else
+ {
+ /* Add 4 more entries */
+ OldSize = Owner->TableSize;
+ NewSize = OldSize + 4;
+ }
- if (Resource->ActiveCount == 0)
- {
- /* no owner, it's easy */
- Resource->OwnerThreads[1].OwnerThread =
ExGetCurrentResourceThread();
- Resource->OwnerThreads[1].OwnerCount = 1;
- if (Resource->OwnerTable != NULL)
- {
- ExFreePool(Resource->OwnerTable);
- }
- Resource->OwnerTable = NULL;
- Resource->ActiveCount = 1;
- DPRINT("EiAddSharedOwner() = TRUE\n");
- return(TRUE);
- }
+ /* Release the lock */
+ ExReleaseResourceLock(&Resource->SpinLock, *OldIrql);
- /*
- * now, we must search if this thread has already acquired this
resource
- * then increase ownercount if found, else create new entry or reuse
free
- * entry
- */
- if (Resource->OwnerTable == NULL)
- {
- DPRINT("Creating owner table\n");
+ /* Allocate memory for the table */
+ Table = ExAllocatePoolWithTag(NonPagedPool,
+ NewSize * sizeof(OWNER_ENTRY),
+ TAG_RESOURCE_TABLE);
- /* allocate ownertable,memset to 0, initialize first entry */
- Resource->OwnerTable =
- ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3,
- TAG_OWNER_TABLE);
- if (Resource->OwnerTable == NULL)
- {
- KEBUGCHECK(0);
- return(FALSE);
- }
- memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
- memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
- sizeof(OWNER_ENTRY));
+ /* Zero the table */
+ RtlZeroMemory((PVOID)(Table + OldSize),
+ (NewSize - OldSize) * sizeof(OWNER_ENTRY));
- Resource->OwnerThreads[1].OwnerThread = 0;
- Resource->OwnerThreads[1].TableSize = 3;
+ /* Lock the resource */
+ ExAcquireResourceLock(&Resource->SpinLock, OldIrql);
- Resource->OwnerTable[1].OwnerThread = CurrentThread;
- Resource->OwnerTable[1].OwnerCount = 1;
- Resource->ActiveCount++;
+ /* Make sure nothing has changed */
+ if ((Owner != Resource->OwnerTable) ||
+ ((Owner) && (OldSize != Resource->OwnerTable->TableSize)))
+ {
+ /* Resource changed while we weren't holding the lock; bail out
*/
+ ExReleaseResourceLock(&Resource->SpinLock, *OldIrql);
+ ExFreePool(Table);
+ }
+ else
+ {
+ /* Copy the table */
+ RtlCopyMemory((PVOID)Table,
+ Owner,
+ OldSize * sizeof(OWNER_ENTRY));
- return(TRUE);
- }
+ /* Acquire dispatcher lock to prevent thread boosting */
+ //KeAcquireDispatcherDatabaseLockAtDpcLevel();
- DPRINT("Search free entries\n");
+ /* Set the new table data */
+ Table->TableSize = NewSize;
+ Resource->OwnerTable = Table;
- DPRINT("Number of entries %d\n",
- Resource->OwnerThreads[1].TableSize);
+ /* Sanity check */
+ ExpVerifyResource(Resource);
- freeEntry = NULL;
- for (i=0; i<Resource->OwnerThreads[1].TableSize; i++)
- {
- if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
- {
- DPRINT("Thread already owns resource\n");
- Resource->OwnerTable[i].OwnerCount++;
- return(TRUE);
- }
- if (Resource->OwnerTable[i].OwnerThread == 0)
- {
- freeEntry = &Resource->OwnerTable[i];
- break;
- }
- }
+ /* Release locks */
+ //KeReleaseDispatcherDatabaseLockFromDpcLevel();
+ ExReleaseResourceLock(&Resource->SpinLock, *OldIrql);
- DPRINT("Found free entry 0x%p\n", freeEntry);
+ /* Free the old table */
+ if (Owner) ExFreePool(Owner);
- if (!freeEntry)
- {
- DPRINT("Allocating new entry\n");
+ /* Set the resource index */
+ if (!OldSize) OldSize = 1;
+ }
- /* reallocate ownertable with one more entry */
- freeEntry =
- ExAllocatePoolWithTag(NonPagedPool,
- sizeof(OWNER_ENTRY)*
- (Resource->OwnerThreads[1].TableSize+1),
- TAG_OWNER_TABLE);
- if (freeEntry == NULL)
- {
- KEBUGCHECK(0);
- return(FALSE);
- }
- memcpy(freeEntry,Resource->OwnerTable,
- sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].TableSize));
- ExFreePool(Resource->OwnerTable);
- Resource->OwnerTable=freeEntry;
-
freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].TableSize];
- Resource->OwnerThreads[1].TableSize++;
- }
- DPRINT("Creating entry\n");
- freeEntry->OwnerThread=ExGetCurrentResourceThread();
- freeEntry->OwnerCount=1;
- Resource->ActiveCount++;
- return(TRUE);
+ /* Set the resource index */
+ KeGetCurrentThread()->ResourceIndex = (UCHAR)OldSize;
+
+ /* Lock the resource again */
+ ExAcquireResourceLock(&Resource->SpinLock, OldIrql);
}
-/*
- * @implemented
- */
-BOOLEAN
-STDCALL
-ExAcquireResourceSharedLite (
- PERESOURCE Resource,
- BOOLEAN Wait
- )
-/*
- * FUNCTION: Acquires the given resource for shared access by the
calling
- * thread
- * ARGUMENTS:
- * Resource = Points to the resource to acquire
- * Wait = Is set to TRUE if the caller should be put into wait
state
- * until the resource can be acquired if it cannot be
acquired
- * immediately
- * RETURNS: TRUE, if the resource is acquire
- * FALSE otherwise
- */
+/*++
+ * @name ExpFindFreeEntry
+ *
+ * The ExpFindFreeEntry routine locates an empty owner entry in the
+ * specified resource. If none was found, then the owner table is
+ * expanded.
+ *
+ * @param Resource
+ * Pointer to the resource.
+ *
+ * @param OldIrql
+ * Pointer to current IRQL. TBC: Pointer to in-stack queued
spinlock.
+ *
+ * @return Pointer to an empty OWNER_ENTRY structure.
+ *
+ * @remarks None.
+ *
+ *--*/
+POWNER_ENTRY
+FASTCALL
+ExpFindFreeEntry(IN PERESOURCE Resource,
+ IN PKIRQL OldIrql)
{
- KIRQL oldIrql;
+ POWNER_ENTRY Owner, Limit;
+ ULONG Size;
+ POWNER_ENTRY FreeEntry = NULL;
+ DPRINT("ExpFindFreeEntry: %p\n", Resource);
- DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n",
- Resource, Wait);
+ /* Sanity check */
+ ASSERT(OldIrql != 0);
+ ASSERT(Resource->OwnerThreads[0].OwnerThread != 0);
- ASSERT_IRQL_LESS(DISPATCH_LEVEL);
+ /* Check if the next built-in entry is free */
+ if (!Resource->OwnerThreads[1].OwnerThread)
+ {
+ /* Return it */
+ FreeEntry = &Resource->OwnerThreads[1];
+ }
+ else
+ {
+ /* Get the current table pointer */
+ Owner = Resource->OwnerTable;
+ if (Owner)
+ {
+ /* Loop every entry */
+ Size = Owner->TableSize;
+ Limit = &Owner[Size];
-/* undefed for now, since cdfs must be fixed first */
-#if 0
- /* At least regular kmode APC's must be disabled
- * Note that this requirement is missing in old DDK's
- */
- ASSERT(KeGetCurrentThread() == NULL || /* <-Early in the boot
process the current thread is obseved to be NULL */
- KeGetCurrentThread()->KernelApcDisable ||
- KeGetCurrentIrql() == APC_LEVEL);
-#endif
+ /* Go to the next entry and loop */
+ Owner++;
+ do
+ {
+ /* Check for a free entry */
+ if (!Owner->OwnerThread)
+ {
+ /* Found one, return it!*/
+ FreeEntry = Owner;
+ break;
+ }
- KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
+ /* Move on */
+ Owner++;
+ } while (Owner != Limit);
- /* first, resolve trivial cases */
- if (Resource->ActiveCount == 0)
- {
- EiAddSharedOwner(Resource);
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
- return(TRUE);
- }
+ /* If we found a free entry by now, return it */
+ if (FreeEntry)
+ {
+ /* Set the resource index */
+ KeGetCurrentThread()->ResourceIndex =
+ (UCHAR)(Owner - Resource->OwnerTable);
+ return FreeEntry;
+ }
+ }
- if ((Resource->Flag & ResourceOwnedExclusive)
- &&
Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
- {
- /* exclusive, but by same thread : it's ok */
- /*
- * NOTE: Is this correct? Seems the same as
ExConvertExclusiveToShared
- */
- Resource->OwnerThreads[0].OwnerCount++;
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
- return(TRUE);
- }
+ /* No free entry, expand the table */
+ ExpExpandResourceOwnerTable(Resource, OldIrql);
+ FreeEntry = NULL;
+ }
- if ((Resource->Flag & ResourceOwnedExclusive)
- || Resource->NumberOfExclusiveWaiters)
- {
- /* exclusive by another thread , or thread waiting for exclusive
*/
- if (!Wait)
- {
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
- return(FALSE);
- }
- else
- {
- Resource->NumberOfSharedWaiters++;
- do
- {
- /* wait for the semaphore */
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- KeWaitForSingleObject(Resource->SharedWaiters,0,
KernelMode, FALSE, NULL);
- KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
- /* the spin lock was released we must check again */
- }
- while ((Resource->Flag & ResourceOwnedExclusive)
- || Resource->NumberOfExclusiveWaiters);
- Resource->NumberOfSharedWaiters--;
- }
- }
+ /* Return the entry found */
+ return FreeEntry;
+}
- EiAddSharedOwner(Resource);
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
- return(TRUE);
+/*++
+ * @name ExpFindEntryForThread
+ *
+ * The ExpFindEntryForThread routine locates the owner entry
associated with
+ * the specified thread in the given resource. If none was found,
then the
+ * owner table is expanded.
+ *
+ * @param Resource
+ * Pointer to the resource.
+ *
+ * @param Thread
+ * Pointer to the thread to find.
+ *
+ * @param OldIrql
+ * Pointer to current IRQL. TBC: Pointer to in-stack queued
spinlock.
+ *
+ * @return Pointer to an empty OWNER_ENTRY structure.
+ *
+ * @remarks None.
+ *
+ *--*/
+POWNER_ENTRY
+FASTCALL
+ExpFindEntryForThread(IN PERESOURCE Resource,
+ IN ERESOURCE_THREAD Thread,
+ IN PKIRQL OldIrql)
+{
+ POWNER_ENTRY FreeEntry, Owner, Limit;
+ ULONG Size;
+ DPRINT("ExpFindEntryForThread: %p\n", Resource);
+
+ /* Start by looking in the static array */
+ if (Resource->OwnerThreads[0].OwnerThread == Thread)
+ {
+ /* Found it, return it! */
+ return &Resource->OwnerThreads[0];
+ }
+ else if (Resource->OwnerThreads[1].OwnerThread == Thread)
+ {
+ /* Return it */
+ return &Resource->OwnerThreads[1];
+ }
+ else
+ {
+ /* Check if the first array is empty for our use */
+ FreeEntry = NULL;
+ if (!Resource->OwnerThreads[1].OwnerThread)
+ {
+ /* Use this as the first free entry */
+ FreeEntry = &Resource->OwnerThreads[1];
+ }
+
+ /* Get the current table pointer */
+ Owner = Resource->OwnerTable;
+ if (!Owner)
+ {
+ /* The current table is empty, so no size */
+ Size = 0;
+ }
+ else
+ {
+ /* We have a table, get it's size and limit */
+ Size = Owner->TableSize;
+ Limit = &Owner[Size];
+
+ /* Go to the next entry and loop */
+ Owner++;
+ do
+ {
+ /* Check for a match */
+ if (Owner->OwnerThread == Thread)
+ {
+ /* Match found! Set the resource index */
+ KeGetCurrentThread()->ResourceIndex =
+ (UCHAR)(Owner - Resource->OwnerTable);
+ return Owner;
+ }
+
+ /* If we don't have a free entry yet, make this one
free */
+ if (!(FreeEntry) && !(Owner->OwnerThread)) FreeEntry =
Owner;
+
+ /* Move on */
+ Owner++;
+ } while (Owner != Limit);
+ }
+ }
+
+ /* Check if it's OK to do an expansion */
+ if (!OldIrql) return NULL;
+
+ /* If we found a free entry by now, return it */
+ if (FreeEntry)
+ {
+ /* Set the resource index */
+ KeGetCurrentThread()->ResourceIndex = (UCHAR)
+ (Owner -
Resource->OwnerTable);
+ return FreeEntry;
+ }
+
+ /* No free entry, expand the table */
+ ExpExpandResourceOwnerTable(Resource, OldIrql);
+ return NULL;
}
-/*
- * @implemented
- */
+/*++
+ * @name ExpBoostOwnerThread
+ *
+ * The ExpBoostOwnerThread routine increases the priority of a
waiting
+ * thread in an attempt to fight a possible deadlock.
+ *
+ * @param Thread
+ * Pointer to the current thread.
+ *
+ * @param OwnerThread
+ * Pointer to thread that owns the resource.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
VOID
-STDCALL
-ExConvertExclusiveToSharedLite (
- PERESOURCE Resource
- )
-/*
- * FUNCTION: Converts a given resource from acquired for exclusive
access
- * to acquire for shared access
- * ARGUMENTS:
- * Resource = Points to the resource for which the access should
be
- * converted
- * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
- */
+FASTCALL
+ExpBoostOwnerThread(IN PKTHREAD Thread,
+ IN PKTHREAD OwnerThread)
{
- ULONG oldWaiters;
- KIRQL oldIrql;
+ BOOLEAN Released = FALSE;
+ DPRINT("ExpBoostOwnerThread: %p\n", Thread);
- DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource);
+ /* Make sure the owner thread is a pointer, not an ID */
+ if (!((ULONG_PTR)OwnerThread & 0x3))
+ {
+ /* Check if we can actually boost it */
+ if ((OwnerThread->Priority < Thread->Priority) &&
+ (OwnerThread->Priority < 14))
+ {
+ /* Make sure we're at dispatch */
+ ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
- KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
+ /* Set the new priority */
+ OwnerThread->PriorityDecrement += 14 -
OwnerThread->Priority;
- oldWaiters = Resource->NumberOfSharedWaiters;
+ /* Update quantum */
+ OwnerThread->Quantum = OwnerThread->QuantumReset;
- if (!(Resource->Flag & ResourceOwnedExclusive))
- {
- /* Might not be what the caller expects, better bug check */
- KEBUGCHECK(0);
- KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
- return;
- }
+ /* Update the kernel state */
+ KiSetPriorityThread(OwnerThread, 14, &Released);
- //transfer infos from entry 0 to entry 1 and erase entry 0
-
Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThr
ead;
-
Resource->OwnerThreads[1].OwnerCount=Resource->OwnerThreads[0].OwnerCoun
t;
- Resource->OwnerThreads[0].OwnerThread=0;
- Resource->OwnerThreads[0].OwnerCount=0;
- /* erase exclusive flag */
- Resource->Flag &= (~ResourceOwnedExclusive);
- /* if no shared waiters, that's all */
- if (!oldWaiters)
[truncated at 1000 lines; 2257 more skipped]