- Multiple fixes to ERESOURCE implementation.

svn path=/trunk/; revision=25632
This commit is contained in:
Alex Ionescu 2007-01-25 23:48:48 +00:00
parent c4f9542125
commit 32a29de195

View file

@ -20,16 +20,13 @@
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, ExpResourceInitialization)
#endif
#include <debug.h>
/* Macros for reading resource flags */
#define IsExclusiveWaiting(r) (r->NumberOfExclusiveWaiters)
#define IsSharedWaiting(r) (r->NumberOfSharedWaiters)
#define IsOwnedExclusive(r) (r->Flag & ResourceOwnedExclusive)
#define IsBoostAllowed(r) (r->Flag & ResourceHasDisabledPriorityBoost)
/* DATA***********************************************************************/
@ -258,6 +255,7 @@ ExpExpandResourceOwnerTable(IN PERESOURCE_XP Resource,
IN PKIRQL OldIrql)
{
POWNER_ENTRY Owner, Table;
KIRQL OldIrql2;
ULONG NewSize, OldSize;
DPRINT("ExpExpandResourceOwnerTable: %p\n", Resource);
@ -285,7 +283,7 @@ ExpExpandResourceOwnerTable(IN PERESOURCE_XP Resource,
TAG_RESOURCE_TABLE);
/* Zero the table */
RtlZeroMemory((PVOID)(Table + OldSize),
RtlZeroMemory(Table + OldSize,
(NewSize - OldSize) * sizeof(OWNER_ENTRY));
/* Lock the resource */
@ -302,22 +300,24 @@ ExpExpandResourceOwnerTable(IN PERESOURCE_XP Resource,
else
{
/* Copy the table */
RtlCopyMemory((PVOID)Table,
RtlCopyMemory(Table,
Owner,
OldSize * sizeof(OWNER_ENTRY));
/* Acquire dispatcher lock to prevent thread boosting */
KiAcquireDispatcherLockAtDpcLevel();
OldIrql2 = KiAcquireDispatcherLock();
/* Set the new table data */
Table->TableSize = NewSize;
Resource->OwnerTable = Table;
/* Release dispatcher lock */
KiReleaseDispatcherLock(OldIrql2);
/* Sanity check */
ExpVerifyResource(Resource);
/* Release locks */
KiReleaseDispatcherLockFromDpcLevel();
/* Release lock */
ExReleaseResourceLock(&Resource->SpinLock, *OldIrql);
/* Free the old table */
@ -358,9 +358,7 @@ ExpFindFreeEntry(IN PERESOURCE_XP Resource,
IN PKIRQL OldIrql)
{
POWNER_ENTRY Owner, Limit;
ULONG Size;
POWNER_ENTRY FreeEntry = NULL;
DPRINT("ExpFindFreeEntry: %p\n", Resource);
/* Sanity check */
ASSERT(OldIrql != 0);
@ -378,34 +376,24 @@ ExpFindFreeEntry(IN PERESOURCE_XP Resource,
Owner = Resource->OwnerTable;
if (Owner)
{
/* Loop every entry */
Size = Owner->TableSize;
Limit = &Owner[Size];
/* Go to the next entry and loop */
/* Set the limit, move to the next owner and loop owner entries */
Limit = &Owner[Owner->TableSize];
Owner++;
do
{
/* Check for a free entry */
/* Check if the entry is free */
if (!Owner->OwnerThread)
{
/* Found one, return it!*/
FreeEntry = Owner;
break;
/* Update the resource entry and return it */
KeGetCurrentThread()->ResourceIndex = (UCHAR)(Owner -
Resource->
OwnerTable);
return Owner;
}
/* Move on */
/* Move to the next one */
Owner++;
} while (Owner != Limit);
/* 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 */
@ -445,63 +433,39 @@ ExpFindEntryForThread(IN PERESOURCE_XP Resource,
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];
}
Owner = &Resource->OwnerThreads[0];
if (Owner->OwnerThread == Thread) return Owner;
Owner++;
if (Owner->OwnerThread == Thread) return Owner;
/* 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];
/* Check if this is a free entry */
FreeEntry = !Owner->OwnerThread ? Owner : NULL;
/* Go to the next entry and loop */
Owner++;
do
/* Loop the table */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Calculate the limit, skip the first entry, and start looping */
Limit = &Owner[Owner->TableSize];
Owner++;
do
{
/* Check if we found a match */
if (Owner->OwnerThread == Thread)
{
/* Check for a match */
if (Owner->OwnerThread == Thread)
{
/* Match found! Set the resource index */
KeGetCurrentThread()->ResourceIndex =
(UCHAR)(Owner - Resource->OwnerTable);
return Owner;
}
/* We did, update the index and return it */
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);
}
/* If we don't yet have a free entry and this one is, choose it */
if (!(FreeEntry) && !(Owner->OwnerThread)) FreeEntry = Owner;
Owner++;
} while (Owner != Limit);
}
/* Check if it's OK to do an expansion */
@ -511,8 +475,8 @@ ExpFindEntryForThread(IN PERESOURCE_XP Resource,
if (FreeEntry)
{
/* Set the resource index */
KeGetCurrentThread()->ResourceIndex = (UCHAR)
(Owner - Resource->OwnerTable);
KeGetCurrentThread()->ResourceIndex = (UCHAR)(FreeEntry -
Resource->OwnerTable);
return FreeEntry;
}
@ -538,19 +502,11 @@ ExpFindEntryForThread(IN PERESOURCE_XP Resource,
* @remarks None.
*
*--*/
#if 0
/*
* Disabled, read the comments bellow.
*/
VOID
FASTCALL
ExpBoostOwnerThread(IN PKTHREAD Thread,
IN PKTHREAD OwnerThread)
{
BOOLEAN Released = FALSE;
DPRINT("ExpBoostOwnerThread: %p\n", Thread);
/* Make sure the owner thread is a pointer, not an ID */
if (!((ULONG_PTR)OwnerThread & 0x3))
{
@ -558,8 +514,8 @@ ExpBoostOwnerThread(IN PKTHREAD Thread,
if ((OwnerThread->Priority < Thread->Priority) &&
(OwnerThread->Priority < 14))
{
/* Make sure we're at dispatch */
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
/* Acquire the thread lock */
KiAcquireThreadLock(Thread);
/* Set the new priority */
OwnerThread->PriorityDecrement += 14 - OwnerThread->Priority;
@ -568,17 +524,13 @@ ExpBoostOwnerThread(IN PKTHREAD Thread,
OwnerThread->Quantum = OwnerThread->QuantumReset;
/* Update the kernel state */
KiSetPriorityThread(OwnerThread, 14, &Released);
KiSetPriorityThread(OwnerThread, 14);
/* Reacquire lock if it got releases */
if (Released) KiAcquireDispatcherLockFromDpcLevel();
/* Make sure we're still at dispatch */
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
/* Release the thread lock */
KiReleaseThreadLock(Thread);
}
}
}
#endif
/*++
* @name ExpWaitForResource
@ -601,20 +553,21 @@ FASTCALL
ExpWaitForResource(IN PERESOURCE_XP Resource,
IN PVOID Object)
{
#if DBG
ULONG i;
ULONG Size;
KIRQL OldIrql;
POWNER_ENTRY Owner;
#endif
ULONG WaitCount = 0;
NTSTATUS Status;
LARGE_INTEGER Timeout;
PKTHREAD Thread, OwnerThread;
#if DBG
KIRQL OldIrql;
#endif
/* Increase contention count and use a 5 second timeout */
Resource->ContentionCount++;
Timeout.QuadPart = 500 * -10000;
for(;;)
for (;;)
{
/* Wait for ownership */
Status = KeWaitForSingleObject(Object,
@ -675,20 +628,10 @@ ExpWaitForResource(IN PERESOURCE_XP Resource,
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
#endif
}
#if 0
/*
* Disabled because:
* - We cannot access the OwnerTable without locking the resource.
* - The shared waiters may wait also on the semaphore. It makes no sense to boost a waiting thread.
* - The thread header is initialized like KeWaitForSingleObject (?, ?, ?, TRUE, ?).
* During the boost, possible the dispatcher lock is released but the thread block (WaitNext) isn't changed.
*/
/* Check if we can boost */
if (!(Resource->Flag & ResourceHasDisabledPriorityBoost))
if (IsBoostAllowed(Resource))
{
PKTHREAD Thread, OwnerThread;
/* Get the current kernel thread and lock the dispatcher */
Thread = KeGetCurrentThread();
Thread->WaitIrql = KiAcquireDispatcherLock();
@ -725,7 +668,6 @@ ExpWaitForResource(IN PERESOURCE_XP Resource,
}
}
}
#endif
}
}
@ -791,8 +733,6 @@ ExAcquireResourceExclusiveLite(PERESOURCE resource,
/* Check if there is a shared owner or exclusive owner */
TryAcquire:
DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n",
Resource, Wait);
if (Resource->ActiveCount)
{
/* Check if it's exclusively owned, and we own it */
@ -831,7 +771,7 @@ TryAcquire:
ExpWaitForResource(Resource, Resource->ExclusiveWaiters);
/* Set owner and return success */
Resource->OwnerThreads[0].OwnerThread = Thread;
Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread();
return TRUE;
}
}
@ -905,21 +845,10 @@ ExAcquireResourceSharedLite(PERESOURCE resource,
/* See if nobody owns us */
TryAcquire:
DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n",
Resource, Wait);
if (!Resource->ActiveCount)
{
if (Resource->NumberOfSharedWaiters == 0)
{
Owner = &Resource->OwnerThreads[1];
}
else
{
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &OldIrql);
if (!Owner) goto TryAcquire;
}
/* Setup the owner entry */
Owner = &Resource->OwnerThreads[1];
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
Resource->ActiveCount = 1;
@ -1066,8 +995,6 @@ ExAcquireSharedStarveExclusive(PERESOURCE resource,
/* See if nobody owns us */
TryAcquire:
DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n",
Resource, Wait);
if (!Resource->ActiveCount)
{
/* Nobody owns it, so let's take control */
@ -1097,22 +1024,6 @@ TryAcquire:
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &OldIrql);
if (!Owner) goto TryAcquire;
/* If we got here, then we need to wait. Are we allowed? */
if (!Wait)
{
/* Release the lock and return */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
return TRUE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate one and try again */
ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
goto TryAcquire;
}
}
else
{
@ -1194,8 +1105,8 @@ TryAcquire:
*--*/
BOOLEAN
NTAPI
ExAcquireSharedWaitForExclusive(PERESOURCE resource,
BOOLEAN Wait)
ExAcquireSharedWaitForExclusive(IN PERESOURCE resource,
IN BOOLEAN Wait)
{
KIRQL OldIrql;
ERESOURCE_THREAD Thread;
@ -1214,8 +1125,6 @@ ExAcquireSharedWaitForExclusive(PERESOURCE resource,
/* See if nobody owns us */
TryAcquire:
DPRINT("ExAcquireSharedWaitForExclusive(Resource 0x%p, Wait %d)\n",
Resource, Wait);
if (!Resource->ActiveCount)
{
/* Nobody owns it, so let's take control */
@ -1241,38 +1150,10 @@ TryAcquire:
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
return TRUE;
}
else
{
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &OldIrql);
if (!Owner) goto TryAcquire;
/* If we got here, then we need to wait. Are we allowed? */
if (!Wait)
{
/* Release the lock and return */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
return TRUE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate one and try again */
ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
goto TryAcquire;
}
/* Now take control of the resource */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
Resource->NumberOfSharedWaiters++;
/* Release the lock and wait for it to be ours */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
ExpWaitForResource(Resource, Resource->SharedWaiters);
return TRUE;
}
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &OldIrql);
if (!Owner) goto TryAcquire;
}
else
{
@ -1304,7 +1185,7 @@ TryAcquire:
ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
/* Find who owns it now */
Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql);
while (!(Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql)));
/* Sanity checks */
ASSERT(IsOwnedExclusive(Resource) == FALSE);
@ -1347,6 +1228,32 @@ TryAcquire:
return TRUE;
}
}
/* We have to wait for the exclusive waiter to be done */
if (!Wait)
{
/* So bail out if we're not allowed */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
return TRUE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate one and try again */
ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
goto TryAcquire;
}
/* Take control */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
Resource->NumberOfSharedWaiters++;
/* Release the lock and return */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
ExpWaitForResource(Resource, Resource->SharedWaiters);
return TRUE;
}
/*++
@ -1372,10 +1279,6 @@ ExConvertExclusiveToSharedLite(PERESOURCE resource)
ULONG OldWaiters;
KIRQL OldIrql;
PERESOURCE_XP Resource = (PERESOURCE_XP)resource;
DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource);
/* Lock the resource */
ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
/* Sanity checks */
ASSERT(KeIsExecutingDpc() == FALSE);
@ -1383,14 +1286,17 @@ ExConvertExclusiveToSharedLite(PERESOURCE resource)
ASSERT(IsOwnedExclusive(Resource));
ASSERT(Resource->OwnerThreads[0].OwnerThread == (ERESOURCE_THREAD)PsGetCurrentThread());
/* Lock the resource */
ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
/* Erase the exclusive flag */
Resource->Flag &= ~ResourceOwnedExclusive;
/* Check if we have shared waiters */
OldWaiters = Resource->NumberOfSharedWaiters;
if (OldWaiters)
if (IsSharedWaiting(Resource))
{
/* Make the waiters active owners */
OldWaiters = Resource->NumberOfSharedWaiters;
Resource->ActiveCount = Resource->ActiveCount + (USHORT)OldWaiters;
Resource->NumberOfSharedWaiters = 0;
@ -1427,7 +1333,6 @@ ExDeleteResourceLite(PERESOURCE resource)
{
KIRQL OldIrql;
PERESOURCE_XP Resource = (PERESOURCE_XP)resource;
DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource);
/* Sanity checks */
ASSERT(IsSharedWaiting(Resource) == FALSE);
@ -1559,7 +1464,6 @@ NTAPI
ExInitializeResourceLite(PERESOURCE resource)
{
PERESOURCE_XP Resource = (PERESOURCE_XP)resource;
DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource);
/* Clear the structure */
RtlZeroMemory(Resource, sizeof(ERESOURCE_XP));
@ -1806,7 +1710,6 @@ ExReleaseResourceLite(PERESOURCE resource)
KIRQL OldIrql;
POWNER_ENTRY Owner, Limit;
PERESOURCE_XP Resource = (PERESOURCE_XP)resource;
DPRINT("ExReleaseResourceLite: %p\n", Resource);
/* Sanity check */
ExpVerifyResource(Resource);
@ -1832,42 +1735,45 @@ ExReleaseResourceLite(PERESOURCE resource)
/* Clear the owner */
Resource->OwnerThreads[0].OwnerThread = 0;
/* Check if there are shared waiters */
if (IsSharedWaiting(Resource))
/* Check the active count */
ASSERT(Resource->ActiveCount > 0);
if (!(--Resource->ActiveCount))
{
/* Remove the exclusive flag */
Resource->Flag &= ~ResourceOwnedExclusive;
/* Check if there are shared waiters */
if (IsSharedWaiting(Resource))
{
/* Remove the exclusive flag */
Resource->Flag &= ~ResourceOwnedExclusive;
/* Give ownage to another thread */
Count = Resource->NumberOfSharedWaiters;
Resource->ActiveCount = (USHORT)Count;
Resource->NumberOfSharedWaiters = 0;
/* Give ownage to another thread */
Count = Resource->NumberOfSharedWaiters;
Resource->ActiveCount = (SHORT)Count;
Resource->NumberOfSharedWaiters = 0;
/* Release lock and let someone else have it */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE);
return;
}
else if (IsExclusiveWaiting(Resource))
{
/* Give exclusive access */
Resource->OwnerThreads[0].OwnerThread = 1;
Resource->OwnerThreads[0].OwnerCount = 1;
Resource->ActiveCount = 1;
Resource->NumberOfExclusiveWaiters--;
/* Release lock and let someone else have it */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE);
return;
}
else if (IsExclusiveWaiting(Resource))
{
/* Give exclusive access */
Resource->OwnerThreads[0].OwnerThread = 1;
Resource->OwnerThreads[0].OwnerCount = 1;
Resource->ActiveCount = 1;
Resource->NumberOfExclusiveWaiters--;
/* Release the lock and give it away */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
KeSetEventBoostPriority(Resource->ExclusiveWaiters,
(PKTHREAD*)
&Resource->OwnerThreads[0].OwnerThread);
return;
}
/* Release the lock and give it away */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
KeSetEventBoostPriority(Resource->ExclusiveWaiters,
(PKTHREAD*)
&Resource->OwnerThreads[0].OwnerThread);
return;
}
}
/* Remove the exclusive flag */
Resource->Flag &= ~ResourceOwnedExclusive;
Resource->ActiveCount = 0;
}
else
{
@ -1877,6 +1783,11 @@ ExReleaseResourceLite(PERESOURCE resource)
/* Found it, get owner */
Owner = &Resource->OwnerThreads[1];
}
else if (Resource->OwnerThreads[0].OwnerThread == Thread)
{
/* Found it, get owner */
Owner = &Resource->OwnerThreads[0];
}
else
{
/* Not in the list, do a full table look up */
@ -1965,7 +1876,6 @@ ExReleaseResourceLite(PERESOURCE resource)
/* Release lock */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
return;
}
/*++
@ -1998,7 +1908,6 @@ ExReleaseResourceForThreadLite(PERESOURCE resource,
POWNER_ENTRY Owner;
PERESOURCE_XP Resource = (PERESOURCE_XP)resource;
ASSERT(Thread != 0);
DPRINT("ExReleaseResourceForThreadLite: %p\n", Resource);
/* Get the thread and lock the resource */
ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
@ -2034,7 +1943,7 @@ ExReleaseResourceForThreadLite(PERESOURCE resource,
/* Give ownage to another thread */
Count = Resource->NumberOfSharedWaiters;
Resource->ActiveCount = (USHORT)Count;
Resource->ActiveCount = (SHORT)Count;
Resource->NumberOfSharedWaiters = 0;
/* Release lock and let someone else have it */
@ -2147,7 +2056,6 @@ ExReleaseResourceForThreadLite(PERESOURCE resource,
/* Release lock */
ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
return;
}
/*++
@ -2266,7 +2174,6 @@ ExTryToAcquireResourceExclusiveLite(PERESOURCE resource)
KIRQL OldIrql;
BOOLEAN Acquired = FALSE;
PERESOURCE_XP Resource = (PERESOURCE_XP)resource;
DPRINT("ExTryToAcquireResourceExclusiveLite: %p\n", Resource);
/* Sanity check */
ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);