--- 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@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].OwnerThread;
- Resource->OwnerThreads[1].OwnerCount=Resource->OwnerThreads[0].OwnerCount;
- 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]