reactos/ntoskrnl/ex/resource.c

2291 lines
65 KiB
C
Raw Blame History

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ex/resource.c
* PURPOSE: Executive Resource Implementation
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* Macros for reading resource flags */
#define IsExclusiveWaiting(r) (r->NumberOfExclusiveWaiters > 0)
#define IsSharedWaiting(r) (r->NumberOfSharedWaiters > 0)
#define IsOwnedExclusive(r) (r->Flag & ResourceOwnedExclusive)
#define IsBoostAllowed(r) (!(r->Flag & ResourceHasDisabledPriorityBoost))
#if !defined(CONFIG_SMP) && !DBG
FORCEINLINE
VOID
ExAcquireResourceLock(IN PERESOURCE Resource,
IN PKLOCK_QUEUE_HANDLE LockHandle)
{
UNREFERENCED_PARAMETER(Resource);
UNREFERENCED_PARAMETER(LockHandle);
/* Simply disable interrupts */
_disable();
}
FORCEINLINE
VOID
ExReleaseResourceLock(IN PERESOURCE Resource,
IN PKLOCK_QUEUE_HANDLE LockHandle)
{
UNREFERENCED_PARAMETER(Resource);
UNREFERENCED_PARAMETER(LockHandle);
/* Simply enable interrupts */
_enable();
}
#else
FORCEINLINE
VOID
ExAcquireResourceLock(IN PERESOURCE Resource,
IN PKLOCK_QUEUE_HANDLE LockHandle)
{
/* Acquire the lock */
KeAcquireInStackQueuedSpinLock(&Resource->SpinLock, LockHandle);
}
FORCEINLINE
VOID
ExReleaseResourceLock(IN PERESOURCE Resource,
IN PKLOCK_QUEUE_HANDLE LockHandle)
{
UNREFERENCED_PARAMETER(Resource);
/* Release the lock */
KeReleaseInStackQueuedSpinLock(LockHandle);
}
#endif // !defined(CONFIG_SMP) && !DBG
/* DATA***********************************************************************/
LARGE_INTEGER ExShortTime = {{-100000, -1}};
LARGE_INTEGER ExpTimeout;
/* Timeout value for resources in 4-second units (7 days) */
ULONG ExpResourceTimeoutCount = 90 * 3600 / 2; // NT value: 648000 (30 days)
KSPIN_LOCK ExpResourceSpinLock;
LIST_ENTRY ExpSystemResourcesList;
BOOLEAN ExResourceStrict = TRUE;
/* 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)
{
/* 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)));
}
/*++
* @name ExpCheckForApcsDisabled
*
* The ExpCheckForApcsDisabled routine checks if Kernel APCs are still
* enabled when they should be disabled, and optionally breakpoints.
*
* @param Irql
* Specifies the IRQL during the acquire attempt.
*
* @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 KIRQL Irql,
IN PERESOURCE Resource,
IN PKTHREAD Thread)
{
/* Check if we should care and check if we should break */
if ((ExResourceStrict) &&
(Irql < APC_LEVEL) &&
!(((PETHREAD)Thread)->SystemThread) &&
!(Thread->CombinedApcDisable))
{
/* Bad! */
DPRINT1("EX: resource: APCs still enabled before resource %p acquire/release "
"!!!\n", Resource);
DbgBreakPoint();
}
}
#else
#define ExpVerifyResource(r)
#define ExpCheckForApcsDisabled(b,r,t)
#endif
/*++
* @name ExpResourceInitialization
*
* The ExpResourceInitialization routine initializes resources for use.
*
* @param None.
*
* @return None.
*
* @remarks This routine should only be called once, during system startup.
*
*--*/
CODE_SEG("INIT")
VOID
NTAPI
ExpResourceInitialization(VOID)
{
/* Setup the timeout */
ExpTimeout.QuadPart = Int32x32To64(4, -10000000);
InitializeListHead(&ExpSystemResourcesList);
KeInitializeSpinLock(&ExpResourceSpinLock);
}
/*++
* @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 LockHandle
* 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 PKLOCK_QUEUE_HANDLE LockHandle)
{
PKEVENT Event;
/* Release the lock */
ExReleaseResourceLock(Resource, LockHandle);
/* Loop as long as we keep running out of memory */
do
{
/* Allocate the event */
Event = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KEVENT),
TAG_RESOURCE_EVENT);
if (Event)
{
/* Initialize it */
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
/* Set it */
if (InterlockedCompareExchangePointer((PVOID*)&Resource->ExclusiveWaiters,
Event,
NULL))
{
/* Someone already set it, free our event */
DPRINT1("WARNING: Handling race condition\n");
ExFreePoolWithTag(Event, TAG_RESOURCE_EVENT);
}
break;
}
/* Wait a bit before trying again */
KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
} while (TRUE);
/* Re-acquire the lock */
ExAcquireResourceLock(Resource, LockHandle);
}
/*++
* @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 LockHandle
* 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 PKLOCK_QUEUE_HANDLE LockHandle)
{
PKSEMAPHORE Semaphore;
/* Release the lock */
ExReleaseResourceLock(Resource, LockHandle);
/* Loop as long as we keep running out of memory */
do
{
/* Allocate the semaphore */
Semaphore = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KSEMAPHORE),
TAG_RESOURCE_SEMAPHORE);
if (Semaphore)
{
/* Initialize it */
KeInitializeSemaphore(Semaphore, 0, MAXLONG);
/* Set it */
if (InterlockedCompareExchangePointer((PVOID*)&Resource->SharedWaiters,
Semaphore,
NULL))
{
/* Someone already set it, free our semaphore */
DPRINT1("WARNING: Handling race condition\n");
ExFreePoolWithTag(Semaphore, TAG_RESOURCE_SEMAPHORE);
}
break;
}
/* Wait a bit before trying again */
KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
} while (TRUE);
/* Re-acquire the lock */
ExAcquireResourceLock(Resource, LockHandle);
}
/*++
* @name ExpExpandResourceOwnerTable
*
* The ExpExpandResourceOwnerTable routine expands the owner table of the
* specified resource.
*
* @param Resource
* Pointer to the resource.
*
* @param LockHandle
* Pointer to in-stack queued spinlock.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
ExpExpandResourceOwnerTable(IN PERESOURCE Resource,
IN PKLOCK_QUEUE_HANDLE LockHandle)
{
POWNER_ENTRY Owner, Table;
KIRQL OldIrql;
ULONG NewSize, OldSize;
/* 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;
}
/* Release the lock */
ExReleaseResourceLock(Resource, LockHandle);
/* Allocate memory for the table */
Table = ExAllocatePoolWithTag(NonPagedPool,
NewSize * sizeof(OWNER_ENTRY),
TAG_RESOURCE_TABLE);
/* Zero the table */
RtlZeroMemory(Table + OldSize,
(NewSize - OldSize) * sizeof(OWNER_ENTRY));
/* Lock the resource */
ExAcquireResourceLock(Resource, LockHandle);
/* Make sure nothing has changed */
if ((Owner != Resource->OwnerTable) ||
((Owner) && (OldSize != Owner->TableSize)))
{
/* Resource changed while we weren't holding the lock; bail out */
ExReleaseResourceLock(Resource, LockHandle);
ExFreePoolWithTag(Table, TAG_RESOURCE_TABLE);
}
else
{
/* Copy the table */
if (Owner) RtlCopyMemory(Table, Owner, OldSize * sizeof(OWNER_ENTRY));
/* Acquire dispatcher lock to prevent thread boosting */
OldIrql = KiAcquireDispatcherLock();
/* Set the new table data */
Table->TableSize = NewSize;
Resource->OwnerTable = Table;
/* Release dispatcher lock */
KiReleaseDispatcherLock(OldIrql);
/* Sanity check */
ExpVerifyResource(Resource);
/* Release lock */
ExReleaseResourceLock(Resource, LockHandle);
/* Free the old table */
if (Owner) ExFreePoolWithTag(Owner, TAG_RESOURCE_TABLE);
/* Set the resource index */
if (!OldSize) OldSize = 1;
}
/* Set the resource index */
KeGetCurrentThread()->ResourceIndex = (UCHAR)OldSize;
/* Lock the resource again */
ExAcquireResourceLock(Resource, LockHandle);
}
/*++
* @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 LockHandle
* Pointer to in-stack queued spinlock.
*
* @return Pointer to an empty OWNER_ENTRY structure.
*
* @remarks None.
*
*--*/
POWNER_ENTRY
FASTCALL
ExpFindFreeEntry(IN PERESOURCE Resource,
IN PKLOCK_QUEUE_HANDLE LockHandle)
{
POWNER_ENTRY Owner, Limit;
/* Sanity check */
ASSERT(LockHandle != 0);
ASSERT(Resource->OwnerEntry.OwnerThread != 0);
/* Get the current table pointer */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Set the limit, move to the next owner and loop owner entries */
Limit = &Owner[Owner->TableSize];
Owner++;
while (Owner->OwnerThread)
{
/* Move to the next one */
Owner++;
/* Check if the entry is free */
if (Owner == Limit) goto Expand;
}
/* Update the resource entry */
KeGetCurrentThread()->ResourceIndex = (UCHAR)(Owner - Resource->OwnerTable);
}
else
{
Expand:
/* No free entry, expand the table */
ExpExpandResourceOwnerTable(Resource, LockHandle);
Owner = NULL;
}
/* Return the entry found */
return Owner;
}
/*++
* @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 LockHandle
* 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 PKLOCK_QUEUE_HANDLE LockHandle,
IN BOOLEAN FirstEntryInelligible)
{
POWNER_ENTRY FreeEntry, Owner, Limit;
/* Start by looking in the static array */
Owner = &Resource->OwnerEntry;
if (Owner->OwnerThread == Thread) return Owner;
/* Check if this is a free entry */
if ((FirstEntryInelligible) || (Owner->OwnerThread))
{
/* No free entry */
FreeEntry = NULL;
}
else
{
/* Use the first entry as our free entry */
FreeEntry = Owner;
}
/* Get the current table pointer */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Set the limit, move to the next owner and loop owner entries */
Limit = &Owner[Owner->TableSize];
Owner++;
while (Owner->OwnerThread != Thread)
{
/* Check if we don't have a free entry */
if (!FreeEntry)
{
/* Check if this entry is free */
if (!Owner->OwnerThread)
{
/* Save it as our free entry */
FreeEntry = Owner;
}
}
/* Move to the next one */
Owner++;
/* Check if the entry is free */
if (Owner == Limit) goto Expand;
}
/* Update the resource entry */
KeGetCurrentThread()->ResourceIndex = (UCHAR)(Owner - Resource->OwnerTable);
return Owner;
}
else
{
Expand:
/* Check if it's OK to do an expansion */
if (!LockHandle) return NULL;
/* If we found a free entry by now, return it */
if (FreeEntry)
{
/* Set the resource index */
KeGetCurrentThread()->ResourceIndex = (UCHAR)(FreeEntry - Resource->OwnerTable);
return FreeEntry;
}
/* No free entry, expand the table */
ExpExpandResourceOwnerTable(Resource, LockHandle);
return NULL;
}
}
/*++
* @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
FASTCALL
ExpBoostOwnerThread(IN PKTHREAD Thread,
IN PKTHREAD OwnerThread)
{
/* 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))
{
/* Acquire the thread lock */
KiAcquireThreadLock(Thread);
/* Set the new priority */
OwnerThread->PriorityDecrement += 14 - OwnerThread->Priority;
/* Update quantum */
OwnerThread->Quantum = OwnerThread->QuantumReset;
/* Update the kernel state */
KiSetPriorityThread(OwnerThread, 14);
/* Release the thread lock */
KiReleaseThreadLock(Thread);
}
}
}
/*++
* @name ExpWaitForResource
*
* The ExpWaitForResource routine performs a wait on the specified resource.
*
* @param Resource
* Pointer to the resource to wait on.
*
* @param OwnerThread
* Pointer to object (exclusive event or shared semaphore) to wait on.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
FASTCALL
ExpWaitForResource(IN PERESOURCE Resource,
IN PVOID Object)
{
ULONG i;
ULONG Size;
POWNER_ENTRY Owner;
ULONG WaitCount = 0;
NTSTATUS Status;
LARGE_INTEGER Timeout;
PKTHREAD Thread, OwnerThread;
#if DBG
KLOCK_QUEUE_HANDLE LockHandle;
#endif
/* Increase contention count and use a 5 second timeout */
Resource->ContentionCount++;
Timeout.QuadPart = 500 * -10000LL;
for (;;)
{
/* Wait for ownership */
Status = KeWaitForSingleObject(Object,
WrResource,
KernelMode,
FALSE,
&Timeout);
if (Status != STATUS_TIMEOUT)
break;
/* Increase wait count */
WaitCount++;
Timeout = ExpTimeout;
/* Check if we've exceeded the limit */
if (WaitCount > ExpResourceTimeoutCount)
{
/* Reset wait count */
WaitCount = 0;
#if DBG
/* Lock the resource */
ExAcquireResourceLock(Resource, &LockHandle);
/* Dump debug information */
DPRINT1("Resource @ %p\n", Resource);
DPRINT1(" ActiveEntries = %04lx Flags = %s%s%s\n",
Resource->ActiveEntries,
IsOwnedExclusive(Resource) ? "IsOwnedExclusive " : "",
IsSharedWaiting(Resource) ? "SharedWaiter " : "",
IsExclusiveWaiting(Resource) ? "ExclusiveWaiter " : "");
DPRINT1(" NumberOfExclusiveWaiters = %04lx\n",
Resource->NumberOfExclusiveWaiters);
DPRINT1(" Thread = %08lx, Count = %02x\n",
Resource->OwnerEntry.OwnerThread,
Resource->OwnerEntry.OwnerCount);
/* Dump out the table too */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Loop every entry */
Size = Owner->TableSize;
for (i = 1; i < Size; i++)
{
/* Print the data */
Owner++;
DPRINT1(" Thread = %08lx, Count = %02x\n",
Owner->OwnerThread,
Owner->OwnerCount);
}
}
/* Break */
DbgBreakPoint();
DPRINT1("EX - Rewaiting\n");
ExReleaseResourceLock(Resource, &LockHandle);
#endif
}
/* Check if we can boost */
if (IsBoostAllowed(Resource))
{
/* Get the current kernel thread and lock the dispatcher */
Thread = KeGetCurrentThread();
Thread->WaitIrql = KiAcquireDispatcherLock();
Thread->WaitNext = TRUE;
/* Get the owner thread and boost it */
OwnerThread = (PKTHREAD)Resource->OwnerEntry.OwnerThread;
if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
/* If it's a shared resource */
if (!IsOwnedExclusive(Resource))
{
/* Get the table */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Loop every entry */
Size = Owner->TableSize;
for (i = 1; i < Size; i++)
{
/* Move to next entry */
Owner++;
/* Get the thread */
OwnerThread = (PKTHREAD)Owner->OwnerThread;
/* Boost it */
if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
}
}
}
}
}
}
/* FUNCTIONS *****************************************************************/
/*++
* @name ExAcquireResourceExclusiveLite
* @implemented NT4
*
* The ExAcquireResourceExclusiveLite routine acquires the given resource
* for exclusive access by the calling thread.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @param Wait
* Specifies the routine's behavior whenever the resource cannot be
* acquired immediately.
*
* @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
* and exclusive access cannot be granted immediately.
*
* @remarks The caller can release the resource by calling either
* ExReleaseResourceLite or ExReleaseResourceForThreadLite.
*
* Normal kernel APC delivery must be disabled before calling this
* routine. Disable normal kernel APC delivery by calling
* KeEnterCriticalRegion. Delivery must remain disabled until the
* resource is released, at which point it can be reenabled by calling
* KeLeaveCriticalRegion.
*
* For better performance, call ExTryToAcquireResourceExclusiveLite,
* rather than calling ExAcquireResourceExclusiveLite with Wait set
* to FALSE.
*
* Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
* DISPATCH_LEVEL.
*
*--*/
BOOLEAN
NTAPI
ExAcquireResourceExclusiveLite(IN PERESOURCE Resource,
IN BOOLEAN Wait)
{
KLOCK_QUEUE_HANDLE LockHandle;
ERESOURCE_THREAD Thread;
BOOLEAN Success;
/* Sanity check */
ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Sanity check and validation */
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
/* Acquire the lock */
ExAcquireResourceLock(Resource, &LockHandle);
ExpCheckForApcsDisabled(LockHandle.OldIrql, Resource, (PKTHREAD)Thread);
/* Check if there is a shared owner or exclusive owner */
TryAcquire:
if (Resource->ActiveEntries)
{
/* Check if it's exclusively owned, and we own it */
if ((IsOwnedExclusive(Resource)) &&
(Resource->OwnerEntry.OwnerThread == Thread))
{
/* Increase the owning count */
Resource->OwnerEntry.OwnerCount++;
Success = TRUE;
}
else
{
/*
* If the caller doesn't want us to wait, we can't acquire the
* resource because someone else then us owns it. If we can wait,
* then we'll wait.
*/
if (!Wait)
{
Success = FALSE;
}
else
{
/* Check if it has exclusive waiters */
if (!Resource->ExclusiveWaiters)
{
/* It doesn't, allocate the event and try acquiring again */
ExpAllocateExclusiveWaiterEvent(Resource, &LockHandle);
goto TryAcquire;
}
/* Has exclusive waiters, wait on it */
Resource->NumberOfExclusiveWaiters++;
ExReleaseResourceLock(Resource, &LockHandle);
ExpWaitForResource(Resource, Resource->ExclusiveWaiters);
/* Set owner and return success */
Resource->OwnerEntry.OwnerThread = ExGetCurrentResourceThread();
return TRUE;
}
}
}
else
{
/* Nobody owns it, so let's! */
ASSERT(Resource->ActiveEntries == 0);
ASSERT(Resource->ActiveCount == 0);
Resource->Flag |= ResourceOwnedExclusive;
Resource->ActiveEntries = 1;
Resource->ActiveCount = 1;
Resource->OwnerEntry.OwnerThread = Thread;
Resource->OwnerEntry.OwnerCount = 1;
Success = TRUE;
}
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return Success;
}
/*++
* @name ExAcquireResourceSharedLite
* @implemented NT4
*
* The ExAcquireResourceSharedLite routine acquires the given resource
* for shared access by the calling thread.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @param Wait
* Specifies the routine's behavior whenever the resource cannot be
* acquired immediately.
*
* @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
* and exclusive access cannot be granted immediately.
*
* @remarks The caller can release the resource by calling either
* ExReleaseResourceLite or ExReleaseResourceForThreadLite.
*
* Normal kernel APC delivery must be disabled before calling this
* routine. Disable normal kernel APC delivery by calling
* KeEnterCriticalRegion. Delivery must remain disabled until the
* resource is released, at which point it can be reenabled by calling
* KeLeaveCriticalRegion.
*
* Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
* DISPATCH_LEVEL.
*
*--*/
BOOLEAN
NTAPI
ExAcquireResourceSharedLite(IN PERESOURCE Resource,
IN BOOLEAN Wait)
{
KLOCK_QUEUE_HANDLE LockHandle;
ERESOURCE_THREAD Thread;
POWNER_ENTRY Owner = NULL;
BOOLEAN FirstEntryBusy;
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Sanity check and validation */
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
/* Acquire the lock */
ExAcquireResourceLock(Resource, &LockHandle);
ExpCheckForApcsDisabled(LockHandle.OldIrql, Resource, (PKTHREAD)Thread);
/* Check how many active entries we've got */
while (Resource->ActiveEntries != 0)
{
/* Check if it's exclusively owned */
if (IsOwnedExclusive(Resource))
{
/* Check if we own it */
if (Resource->OwnerEntry.OwnerThread == Thread)
{
/* Increase the owning count */
Resource->OwnerEntry.OwnerCount++;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &LockHandle);
if (!Owner) continue;
}
else
{
/* Resource is shared, find who owns it */
FirstEntryBusy = IsExclusiveWaiting(Resource);
Owner = ExpFindEntryForThread(Resource,
Thread,
&LockHandle,
FirstEntryBusy);
if (!Owner) continue;
/* Is it us? */
if (Owner->OwnerThread == Thread)
{
/* Increase acquire count and return */
Owner->OwnerCount++;
ASSERT(Owner->OwnerCount != 0);
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Try to find if there are exclusive waiters */
if (!FirstEntryBusy)
{
/* There are none, so acquire it */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
/* Check how many active entries we had */
if (Resource->ActiveEntries == 0)
{
/* Set initial counts */
ASSERT(Resource->ActiveCount == 0);
Resource->ActiveEntries = 1;
Resource->ActiveCount = 1;
}
else
{
/* Increase active entries */
ASSERT(Resource->ActiveCount == 1);
Resource->ActiveEntries++;
}
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
}
/* If we got here, then we need to wait. Are we allowed? */
if (!Wait)
{
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return FALSE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate it and try another acquire */
ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
}
else
{
/* We have shared waiters, wait for it */
break;
}
}
/* Did we get here because we don't have active entries? */
if (Resource->ActiveEntries == 0)
{
/* Acquire it */
ASSERT(Resource->ActiveEntries == 0);
ASSERT(Resource->ActiveCount == 0);
Resource->ActiveEntries = 1;
Resource->ActiveCount = 1;
Resource->OwnerEntry.OwnerThread = Thread;
Resource->OwnerEntry.OwnerCount = 1;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Now wait for the resource */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
Resource->NumberOfSharedWaiters++;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
ExpWaitForResource(Resource, Resource->SharedWaiters);
return TRUE;
}
/*++
* @name ExAcquireSharedStarveExclusive
* @implemented NT4
*
* The ExAcquireSharedStarveExclusive routine acquires the given resource
* shared access without waiting for any pending attempts to acquire
* exclusive access to the same resource.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @param Wait
* Specifies the routine's behavior whenever the resource cannot be
* acquired immediately.
*
* @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
* and exclusive access cannot be granted immediately.
*
* @remarks The caller can release the resource by calling either
* ExReleaseResourceLite or ExReleaseResourceForThreadLite.
*
* Normal kernel APC delivery must be disabled before calling this
* routine. Disable normal kernel APC delivery by calling
* KeEnterCriticalRegion. Delivery must remain disabled until the
* resource is released, at which point it can be reenabled by calling
* KeLeaveCriticalRegion.
*
* Callers of ExAcquireSharedStarveExclusive usually need quick access
* to a shared resource in order to save an exclusive accessor from
* doing redundant work. For example, a file system might call this
* routine to modify a cached resource, such as a BCB pinned in the
* cache, before the Cache Manager can acquire exclusive access to the
* resource and write the cache out to disk.
*
* Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
* DISPATCH_LEVEL.
*
*--*/
BOOLEAN
NTAPI
ExAcquireSharedStarveExclusive(IN PERESOURCE Resource,
IN BOOLEAN Wait)
{
KLOCK_QUEUE_HANDLE LockHandle;
ERESOURCE_THREAD Thread;
POWNER_ENTRY Owner;
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Sanity check and validation */
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
/* Acquire the lock */
ExAcquireResourceLock(Resource, &LockHandle);
/* See if anyone owns it */
TryAcquire:
if (Resource->ActiveEntries == 0)
{
/* Nobody owns it, so let's take control */
ASSERT(Resource->ActiveEntries == 0);
ASSERT(Resource->ActiveCount == 0);
Resource->ActiveCount = 1;
Resource->ActiveEntries = 1;
Resource->OwnerEntry.OwnerThread = Thread;
Resource->OwnerEntry.OwnerCount = 1;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Check if it's exclusively owned */
if (IsOwnedExclusive(Resource))
{
/* Check if we own it */
if (Resource->OwnerEntry.OwnerThread == Thread)
{
/* Increase the owning count */
Resource->OwnerEntry.OwnerCount++;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &LockHandle);
if (!Owner) goto TryAcquire;
}
else
{
/* Resource is shared, find who owns it */
Owner = ExpFindEntryForThread(Resource, Thread, &LockHandle, FALSE);
if (!Owner) goto TryAcquire;
/* Is it us? */
if (Owner->OwnerThread == Thread)
{
/* Increase acquire count and return */
Owner->OwnerCount++;
ASSERT(Owner->OwnerCount != 0);
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Acquire it */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
/* Check how many active entries we had */
if (Resource->ActiveEntries == 0)
{
/* Set initial counts */
ASSERT(Resource->ActiveCount == 0);
Resource->ActiveEntries = 1;
Resource->ActiveCount = 1;
}
else
{
/* Increase active entries */
ASSERT(Resource->ActiveCount == 1);
Resource->ActiveEntries++;
}
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* If we got here, then we need to wait. Are we allowed? */
if (!Wait)
{
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return FALSE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate it and try another acquire */
ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
goto TryAcquire;
}
/* Now wait for the resource */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
Resource->NumberOfSharedWaiters++;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
ExpWaitForResource(Resource, Resource->SharedWaiters);
return TRUE;
}
/*++
* @name ExAcquireSharedWaitForExclusive
* @implemented NT4
*
* The ExAcquireSharedWaitForExclusive routine acquires the given resource
* for shared access if shared access can be granted and there are no
* exclusive waiters.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @param Wait
* Specifies the routine's behavior whenever the resource cannot be
* acquired immediately.
*
* @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
* and exclusive access cannot be granted immediately.
*
* @remarks The caller can release the resource by calling either
* ExReleaseResourceLite or ExReleaseResourceForThreadLite.
*
* Normal kernel APC delivery must be disabled before calling this
* routine. Disable normal kernel APC delivery by calling
* KeEnterCriticalRegion. Delivery must remain disabled until the
* resource is released, at which point it can be reenabled by calling
* KeLeaveCriticalRegion.
*
* Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
* DISPATCH_LEVEL.
*
*--*/
BOOLEAN
NTAPI
ExAcquireSharedWaitForExclusive(IN PERESOURCE Resource,
IN BOOLEAN Wait)
{
KLOCK_QUEUE_HANDLE LockHandle;
ERESOURCE_THREAD Thread;
POWNER_ENTRY Owner;
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Sanity check and validation */
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
/* Acquire the lock */
ExAcquireResourceLock(Resource, &LockHandle);
/* See if nobody owns us */
TryAcquire:
if (!Resource->ActiveEntries)
{
/* Nobody owns it, so let's take control */
ASSERT(Resource->ActiveEntries == 0);
ASSERT(Resource->ActiveCount == 0);
Resource->ActiveCount = 1;
Resource->ActiveEntries = 1;
Resource->OwnerEntry.OwnerThread = Thread;
Resource->OwnerEntry.OwnerCount = 1;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Check if it's exclusively owned */
if (IsOwnedExclusive(Resource))
{
/* Check if we own it */
if (Resource->OwnerEntry.OwnerThread == Thread)
{
/* Increase the owning count */
Resource->OwnerEntry.OwnerCount++;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* Find a free entry */
Owner = ExpFindFreeEntry(Resource, &LockHandle);
if (!Owner) goto TryAcquire;
}
else
{
/* Try to find if there are exclusive waiters */
if (IsExclusiveWaiting(Resource))
{
/* We have to wait for the exclusive waiter to be done */
if (!Wait)
{
/* So bail out if we're not allowed */
ExReleaseResourceLock(Resource, &LockHandle);
return FALSE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate one and try again */
ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
goto TryAcquire;
}
/* Now wait for the resource */
Resource->NumberOfSharedWaiters++;
ExReleaseResourceLock(Resource, &LockHandle);
ExpWaitForResource(Resource, Resource->SharedWaiters);
/* Get the lock back */
ExAcquireResourceLock(Resource, &LockHandle);
/* Find who owns it now */
while (!(Owner = ExpFindEntryForThread(Resource, Thread, &LockHandle, TRUE)));
/* Sanity checks */
ASSERT(IsOwnedExclusive(Resource) == FALSE);
ASSERT(Resource->ActiveEntries > 0);
ASSERT(Owner->OwnerThread != Thread);
/* Take control */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
else
{
/* Resource is shared, find who owns it */
Owner = ExpFindEntryForThread(Resource, Thread, &LockHandle, FALSE);
if (!Owner) goto TryAcquire;
/* Is it us? */
if (Owner->OwnerThread == Thread)
{
/* Increase acquire count and return */
Owner->OwnerCount++;
ASSERT(Owner->OwnerCount != 0);
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
return TRUE;
}
/* No exclusive waiters, so acquire it */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
/* Check how many active entries we had */
if (Resource->ActiveEntries == 0)
{
/* Set initial counts */
ASSERT(Resource->ActiveCount == 0);
Resource->ActiveEntries = 1;
Resource->ActiveCount = 1;
}
else
{
/* Increase active entries */
ASSERT(Resource->ActiveCount == 1);
Resource->ActiveEntries++;
}
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
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, &LockHandle);
return FALSE;
}
/* Check if we have a shared waiters semaphore */
if (!Resource->SharedWaiters)
{
/* Allocate one and try again */
ExpAllocateSharedWaiterSemaphore(Resource,&LockHandle);
goto TryAcquire;
}
/* Take control */
Owner->OwnerThread = Thread;
Owner->OwnerCount = 1;
Resource->NumberOfSharedWaiters++;
/* Release the lock and return */
ExReleaseResourceLock(Resource, &LockHandle);
ExpWaitForResource(Resource, Resource->SharedWaiters);
return TRUE;
}
/*++
* @name ExConvertExclusiveToSharedLite
* @implemented NT4
*
* The ExConvertExclusiveToSharedLite routine converts an exclusively
* acquired resource into a resource that can be acquired shared.
*
* @param Resource
* Pointer to the resource to convert.
*
* @return None.
*
* @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL <
* DISPATCH_LEVEL.
*
*--*/
VOID
NTAPI
ExConvertExclusiveToSharedLite(IN PERESOURCE Resource)
{
ULONG OldWaiters;
KLOCK_QUEUE_HANDLE LockHandle;
/* Sanity checks */
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
ASSERT(IsOwnedExclusive(Resource));
ASSERT(Resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)PsGetCurrentThread());
/* Lock the resource */
ExAcquireResourceLock(Resource, &LockHandle);
/* Erase the exclusive flag */
Resource->Flag &= ~ResourceOwnedExclusive;
/* Check if we have shared waiters */
if (IsSharedWaiting(Resource))
{
/* Make the waiters active owners */
OldWaiters = Resource->NumberOfSharedWaiters;
Resource->ActiveEntries += OldWaiters;
Resource->NumberOfSharedWaiters = 0;
/* Release lock and wake the waiters */
ExReleaseResourceLock(Resource, &LockHandle);
KeReleaseSemaphore(Resource->SharedWaiters, 0, OldWaiters, FALSE);
}
else
{
/* Release lock */
ExReleaseResourceLock(Resource, &LockHandle);
}
}
/*++
* @name ExDeleteResourceLite
* @implemented NT4
*
* The ExConvertExclusiveToSharedLite routine deletes a given resource
* from the system<65>s resource list.
*
* @param Resource
* Pointer to the resource to delete.
*
* @return STATUS_SUCCESS if the resource was deleted.
*
* @remarks Callers of ExDeleteResourceLite must be running at IRQL <
* DISPATCH_LEVEL.
*
*--*/
NTSTATUS
NTAPI
ExDeleteResourceLite(IN PERESOURCE Resource)
{
KLOCK_QUEUE_HANDLE LockHandle;
/* Sanity checks */
ASSERT(IsSharedWaiting(Resource) == FALSE);
ASSERT(IsExclusiveWaiting(Resource) == FALSE);
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
/* Lock the resource */
KeAcquireInStackQueuedSpinLock(&ExpResourceSpinLock, &LockHandle);
/* Remove the resource */
RemoveEntryList(&Resource->SystemResourcesList);
/* Release the lock */
KeReleaseInStackQueuedSpinLock(&LockHandle);
/* Free every structure */
if (Resource->OwnerTable) ExFreePoolWithTag(Resource->OwnerTable, TAG_RESOURCE_TABLE);
if (Resource->SharedWaiters) ExFreePoolWithTag(Resource->SharedWaiters, TAG_RESOURCE_SEMAPHORE);
if (Resource->ExclusiveWaiters) ExFreePoolWithTag(Resource->ExclusiveWaiters, TAG_RESOURCE_EVENT);
/* Return success */
return STATUS_SUCCESS;
}
/*++
* @name ExDisableResourceBoostLite
* @implemented NT4
*
* The ExDisableResourceBoostLite routine disables thread boosting for
* the given resource.
*
* @param Resource
* Pointer to the resource whose thread boosting will be disabled.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
ExDisableResourceBoostLite(IN PERESOURCE Resource)
{
KLOCK_QUEUE_HANDLE LockHandle;
/* Sanity check */
ExpVerifyResource(Resource);
/* Lock the resource */
ExAcquireResourceLock(Resource, &LockHandle);
/* Remove the flag */
Resource->Flag |= ResourceHasDisabledPriorityBoost;
/* Release the lock */
ExReleaseResourceLock(Resource, &LockHandle);
}
/*++
* @name ExGetExclusiveWaiterCount
* @implemented NT4
*
* The ExGetExclusiveWaiterCount routine returns the number of exclusive
* waiters for the given resource.
*
* @param Resource
* Pointer to the resource to check.
*
* @return The number of exclusive waiters.
*
* @remarks None.
*
*--*/
ULONG
NTAPI
ExGetExclusiveWaiterCount(IN PERESOURCE Resource)
{
/* Return the count */
return Resource->NumberOfExclusiveWaiters;
}
/*++
* @name ExGetSharedWaiterCount
* @implemented NT4
*
* The ExGetSharedWaiterCount routine returns the number of shared
* waiters for the given resource.
*
* @param Resource
* Pointer to the resource to check.
*
* @return The number of shared waiters.
*
* @remarks None.
*
*--*/
ULONG
NTAPI
ExGetSharedWaiterCount(IN PERESOURCE Resource)
{
/* Return the count */
return Resource->NumberOfSharedWaiters;
}
/*++
* @name ExInitializeResourceLite
* @implemented NT4
*
* The ExInitializeResourceLite routine initializes a resource variable.
*
* @param Resource
* Pointer to the resource to check.
*
* @return STATUS_SUCCESS.
*
* @remarks The storage for ERESOURCE must not be allocated from paged pool.
*
* The storage must be 8-byte aligned.
*
*--*/
NTSTATUS
NTAPI
ExInitializeResourceLite(IN PERESOURCE Resource)
{
KLOCK_QUEUE_HANDLE LockHandle;
/* Clear the structure */
RtlZeroMemory(Resource, sizeof(ERESOURCE));
/* Initialize the lock */
KeInitializeSpinLock(&Resource->SpinLock);
/* Add it into the system list */
KeAcquireInStackQueuedSpinLock(&ExpResourceSpinLock, &LockHandle);
InsertTailList(&ExpSystemResourcesList, &Resource->SystemResourcesList);
KeReleaseInStackQueuedSpinLock(&LockHandle);
/* Return success */
return STATUS_SUCCESS;
}
/*++
* @name ExIsResourceAcquiredExclusiveLite
* @implemented NT4
*
* The ExIsResourceAcquiredExclusiveLite routine returns whether the
* current thread has exclusive access to a given resource.
*
* @param Resource
* Pointer to the resource to check.
*
* @return TRUE if the caller already has exclusive access to the given resource.
*
* @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
* IRQL <= DISPATCH_LEVEL.
*
*--*/
BOOLEAN
NTAPI
ExIsResourceAcquiredExclusiveLite(IN PERESOURCE Resource)
{
BOOLEAN IsAcquired = FALSE;
/* Sanity check */
ExpVerifyResource(Resource);
/* Check if it's exclusively acquired */
if ((IsOwnedExclusive(Resource)) &&
(Resource->OwnerEntry.OwnerThread == ExGetCurrentResourceThread()))
{
/* It is acquired */
IsAcquired = TRUE;
}
/* Return if it's acquired */
return IsAcquired;
}
/*++
* @name ExIsResourceAcquiredSharedLite
* @implemented NT4
*
* The ExIsResourceAcquiredSharedLite routine returns whether the
* current thread has has access (either shared or exclusive) to a
* given resource.
*
* @param Resource
* Pointer to the resource to check.
*
* @return Number of times the caller has acquired the given resource for
* shared or exclusive access.
*
* @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
* IRQL <= DISPATCH_LEVEL.
*
*--*/
ULONG
NTAPI
ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource)
{
ERESOURCE_THREAD Thread;
ULONG i, Size;
ULONG Count = 0;
KLOCK_QUEUE_HANDLE LockHandle;
POWNER_ENTRY Owner;
/* Sanity check */
ExpVerifyResource(Resource);
/* Check if nobody owns us */
if (!Resource->ActiveEntries) return 0;
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Check if we are in the thread list */
if (Resource->OwnerEntry.OwnerThread == Thread)
{
/* Found it, return count */
Count = Resource->OwnerEntry.OwnerCount;
}
else
{
/* We can't own an exclusive resource at this point */
if (IsOwnedExclusive(Resource)) return 0;
/* Lock the resource */
ExAcquireResourceLock(Resource, &LockHandle);
/* Not in the list, do a full table look up */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Get the resource index */
i = ((PKTHREAD)Thread)->ResourceIndex;
Size = Owner->TableSize;
/* Check if the index is valid and check if we don't match */
if ((i >= Size) || (Owner[i].OwnerThread != Thread))
{
/* Sh*t! We need to do a full search */
for (i = 1; i < Size; i++)
{
/* Move to next owner */
Owner++;
/* Try to find a match */
if (Owner->OwnerThread == Thread)
{
/* Finally! */
Count = Owner->OwnerCount;
break;
}
}
}
else
{
/* We found the match directlry */
Count = Owner[i].OwnerCount;
}
}
/* Release the lock */
ExReleaseResourceLock(Resource, &LockHandle);
}
/* Return count */
return Count;
}
/*++
* @name ExReinitializeResourceLite
* @implemented NT4
*
* The ExReinitializeResourceLite routine routine reinitializes
* an existing resource variable.
*
* @param Resource
* Pointer to the resource to be reinitialized.
*
* @return STATUS_SUCCESS.
*
* @remarks With a single call to ExReinitializeResource, a driver writer can
* replace three calls: one to ExDeleteResourceLite, another to
* ExAllocatePool, and a third to ExInitializeResourceLite. As
* contention for a resource variable increases, memory is dynamically
* allocated and attached to the resource in order to track this
* contention. As an optimization, ExReinitializeResourceLite retains
* and zeroes this previously allocated memory.
*
* Callers of ExReinitializeResourceLite must be running at
* IRQL <= DISPATCH_LEVEL.
*
*--*/
NTSTATUS
NTAPI
ExReinitializeResourceLite(IN PERESOURCE Resource)
{
PKEVENT Event;
PKSEMAPHORE Semaphore;
ULONG i, Size;
POWNER_ENTRY Owner;
/* Get the owner table */
Owner = Resource->OwnerTable;
if (Owner)
{
/* Get the size and loop it */
Size = Owner->TableSize;
for (i = 0; i < Size; i++)
{
/* Zero the table */
Owner[i].OwnerThread = 0;
Owner[i].OwnerCount = 0;
}
}
/* Zero the flags and count */
Resource->Flag = 0;
Resource->ActiveCount = 0;
Resource->ActiveEntries = 0;
/* Reset the semaphore */
Semaphore = Resource->SharedWaiters;
if (Semaphore) KeInitializeSemaphore(Semaphore, 0, MAXLONG);
/* Reset the event */
Event = Resource->ExclusiveWaiters;
if (Event) KeInitializeEvent(Event, SynchronizationEvent, FALSE);
/* Clear the resource data */
Resource->OwnerEntry.OwnerThread = 0;
Resource->OwnerEntry.OwnerCount = 0;
Resource->ContentionCount = 0;
Resource->NumberOfSharedWaiters = 0;
Resource->NumberOfExclusiveWaiters = 0;
return STATUS_SUCCESS;
}
/*++
* @name ExReleaseResourceLite
* @implemented NT4
*
* The ExReleaseResourceLite routine routine releases
* a specified executive resource owned by the current thread.
*
* @param Resource
* Pointer to the resource to be released.
*
* @return None.
*
* @remarks Callers of ExReleaseResourceLite must be running at
* IRQL <= DISPATCH_LEVEL.
*
*--*/
VOID
FASTCALL
ExReleaseResourceLite(IN PERESOURCE Resource)
{
/* Just call the For-Thread function */
ExReleaseResourceForThreadLite(Resource, ExGetCurrentResourceThread());
}
/*++
* @name ExReleaseResourceForThreadLite
* @implemented NT4
*
* The ExReleaseResourceForThreadLite routine routine releases
* the input resource of the indicated thread.
*
* @param Resource
* Pointer to the resource to be released.
*
* @param Thread
* Identifies the thread that originally acquired the resource.
*
* @return None.
*
* @remarks Callers of ExReleaseResourceForThreadLite must be running at
* IRQL <= DISPATCH_LEVEL.
*
*--*/
VOID
NTAPI
ExReleaseResourceForThreadLite(IN PERESOURCE Resource,
IN ERESOURCE_THREAD Thread)
{
ULONG i;
ULONG Count;
KLOCK_QUEUE_HANDLE LockHandle;
POWNER_ENTRY Owner, Limit;
ASSERT(Thread != 0);
/* Get the thread and lock the resource */
ExAcquireResourceLock(Resource, &LockHandle);
/* Sanity checks */
ExpVerifyResource(Resource);
ExpCheckForApcsDisabled(LockHandle.OldIrql, Resource, KeGetCurrentThread());
/* Check if it's exclusively owned */
if (IsOwnedExclusive(Resource))
{
/* Decrement owner count and check if we're done */
ASSERT(Resource->OwnerEntry.OwnerThread == Thread);
if (--Resource->OwnerEntry.OwnerCount)
{
/* Done, release lock! */
ExReleaseResourceLock(Resource, &LockHandle);
return;
}
/* Clear the owner */
Resource->OwnerEntry.OwnerThread = 0;
/* Decrement the number of active entries */
ASSERT(Resource->ActiveEntries == 1);
Resource->ActiveEntries--;
/* 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->ActiveEntries = Count;
Resource->NumberOfSharedWaiters = 0;
/* Release lock and let someone else have it */
ASSERT(Resource->ActiveCount == 1);
ExReleaseResourceLock(Resource, &LockHandle);
KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE);
return;
}
else if (IsExclusiveWaiting(Resource))
{
/* Give exclusive access */
Resource->OwnerEntry.OwnerThread = 1;
Resource->OwnerEntry.OwnerCount = 1;
Resource->ActiveEntries = 1;
Resource->NumberOfExclusiveWaiters--;
/* Release the lock and give it away */
ASSERT(Resource->ActiveCount == 1);
ExReleaseResourceLock(Resource, &LockHandle);
KeSetEventBoostPriority(Resource->ExclusiveWaiters,
(PKTHREAD*)&Resource->OwnerEntry.OwnerThread);
return;
}
/* Remove the exclusive flag */
Resource->Flag &= ~ResourceOwnedExclusive;
Resource->ActiveCount = 0;
}
else
{
/* Check if we are in the thread list */
if (Resource->OwnerEntry.OwnerThread == Thread)
{
/* Found it, get owner */
Owner = &Resource->OwnerEntry;
}
else
{
/* Assume no valid index */
i = 1;
/* If we got a valid pointer, try to get the resource index */
if (!((ULONG)Thread & 3)) i = ((PKTHREAD)Thread)->ResourceIndex;
/* Do a table lookup */
Owner = Resource->OwnerTable;
ASSERT(Owner != NULL);
/* Check if we're out of the size and don't match */
if ((i >= Owner->TableSize) || (Owner[i].OwnerThread != Thread))
{
/* Get the last entry */
Limit = &Owner[Owner->TableSize];
for (;;)
{
/* Move to the next entry */
Owner++;
/* Make sure we're not out of bounds */
if (Owner >= Limit)
{
/* Bugcheck, nobody owns us */
KeBugCheckEx(RESOURCE_NOT_OWNED,
(ULONG_PTR)Resource,
(ULONG_PTR)Thread,
(ULONG_PTR)Resource->OwnerTable,
(ULONG_PTR)3);
}
/* Check for a match */
if (Owner->OwnerThread == Thread) break;
}
}
else
{
/* Get the entry directly */
Owner = &Owner[i];
}
}
/* Sanity checks */
ASSERT(Owner->OwnerThread == Thread);
ASSERT(Owner->OwnerCount > 0);
/* Check if we are the last owner */
if (--Owner->OwnerCount)
{
/* There are other owners, release lock */
ExReleaseResourceLock(Resource, &LockHandle);
return;
}
/* Clear owner */
Owner->OwnerThread = 0;
/* See if the resource isn't being owned anymore */
ASSERT(Resource->ActiveEntries > 0);
if (!(--Resource->ActiveEntries))
{
/* Check if there's an exclusive waiter */
if (IsExclusiveWaiting(Resource))
{
/* Give exclusive access */
Resource->Flag |= ResourceOwnedExclusive;
Resource->OwnerEntry.OwnerThread = 1;
Resource->OwnerEntry.OwnerCount = 1;
Resource->ActiveEntries = 1;
Resource->NumberOfExclusiveWaiters--;
/* Release the lock and give it away */
ASSERT(Resource->ActiveCount == 1);
ExReleaseResourceLock(Resource, &LockHandle);
KeSetEventBoostPriority(Resource->ExclusiveWaiters,
(PKTHREAD*)&Resource->OwnerEntry.OwnerThread);
return;
}
/* Clear the active count */
Resource->ActiveCount = 0;
}
}
/* Release lock */
ExReleaseResourceLock(Resource, &LockHandle);
}
/*++
* @name ExSetResourceOwnerPointer
* @implemented NT4
*
* The ExSetResourceOwnerPointer routine routine sets the owner thread
* thread pointer for an executive resource.
*
* @param Resource
* Pointer to the resource whose owner to change.
*
* @param OwnerPointer
* Pointer to an owner thread pointer of type ERESOURCE_THREAD.
*
* @return None.
*
* @remarks ExSetResourceOwnerPointer, used in conjunction with
* ExReleaseResourceForThreadLite, provides a means for one thread
* (acting as an resource manager thread) to acquire and release
* resources for use by another thread (acting as a resource user
* thread).
*
* After calling ExSetResourceOwnerPointer for a specific resource,
* the only other routine that can be called for that resource is
* ExReleaseResourceForThreadLite.
*
* Callers of ExSetResourceOwnerPointer must be running at
* IRQL <= DISPATCH_LEVEL.
*
*--*/
VOID
NTAPI
ExSetResourceOwnerPointer(IN PERESOURCE Resource,
IN PVOID OwnerPointer)
{
ERESOURCE_THREAD Thread;
KLOCK_QUEUE_HANDLE LockHandle;
POWNER_ENTRY Owner, ThisOwner;
/* Sanity check */
ASSERT((OwnerPointer != 0) && (((ULONG_PTR)OwnerPointer & 3) == 3));
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Sanity check */
ExpVerifyResource(Resource);
/* Lock the resource */
ExAcquireResourceLock(Resource, &LockHandle);
/* Check if it's exclusive */
if (IsOwnedExclusive(Resource))
{
/* If it's exclusive, set the first entry no matter what */
ASSERT(Resource->OwnerEntry.OwnerThread == Thread);
ASSERT(Resource->OwnerEntry.OwnerCount > 0);
Resource->OwnerEntry.OwnerThread = (ULONG_PTR)OwnerPointer;
}
else
{
/* Set the thread in both entries */
ThisOwner = ExpFindEntryForThread(Resource,
(ERESOURCE_THREAD)OwnerPointer,
0,
FALSE);
Owner = ExpFindEntryForThread(Resource, Thread, 0, FALSE);
if (!Owner)
{
/* Nobody owns it, crash */
KeBugCheckEx(RESOURCE_NOT_OWNED,
(ULONG_PTR)Resource,
Thread,
(ULONG_PTR)Resource->OwnerTable,
3);
}
/* Set if we are the owner */
if (ThisOwner)
{
/* Update data */
ThisOwner->OwnerCount += Owner->OwnerCount;
Owner->OwnerCount = 0;
Owner->OwnerThread = 0;
ASSERT(Resource->ActiveEntries >= 2);
Resource->ActiveEntries--;
}
else
{
/* Update the owner entry instead */
Owner->OwnerThread = (ERESOURCE_THREAD)OwnerPointer;
}
}
/* Release the resource */
ExReleaseResourceLock(Resource, &LockHandle);
}
/*++
* @name ExTryToAcquireResourceExclusiveLite
* @implemented NT4
*
* The ExTryToAcquireResourceExclusiveLite routine routine attemps to
* acquire the given resource for exclusive access.
*
* @param Resource
* Pointer to the resource to be acquired.
*
* @return TRUE if the given resource has been acquired for the caller.
*
* @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at
* IRQL < DISPATCH_LEVEL.
*
*--*/
BOOLEAN
NTAPI
ExTryToAcquireResourceExclusiveLite(IN PERESOURCE Resource)
{
ERESOURCE_THREAD Thread;
KLOCK_QUEUE_HANDLE LockHandle;
BOOLEAN Acquired = FALSE;
/* Sanity check */
ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
/* Get the thread */
Thread = ExGetCurrentResourceThread();
/* Sanity check and validation */
ASSERT(KeIsExecutingDpc() == FALSE);
ExpVerifyResource(Resource);
/* Acquire the lock */
ExAcquireResourceLock(Resource, &LockHandle);
/* Check if there is an owner */
if (!Resource->ActiveCount)
{
/* No owner, give exclusive access */
Resource->Flag |= ResourceOwnedExclusive;
Resource->OwnerEntry.OwnerThread = Thread;
Resource->OwnerEntry.OwnerCount = 1;
Resource->ActiveCount = 1;
Resource->ActiveEntries = 1;
Acquired = TRUE;
}
else if ((IsOwnedExclusive(Resource)) &&
(Resource->OwnerEntry.OwnerThread == Thread))
{
/* Do a recursive acquire */
Resource->OwnerEntry.OwnerCount++;
Acquired = TRUE;
}
/* Release the resource */
ExReleaseResourceLock(Resource, &LockHandle);
return Acquired;
}
/*++
* @name ExEnterCriticalRegionAndAcquireResourceExclusive
* @implemented NT5.1
*
* The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical
* region and then exclusively acquires a resource.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @return Pointer to the Win32K thread pointer of the current thread.
*
* @remarks See ExAcquireResourceExclusiveLite.
*
*--*/
PVOID
NTAPI
ExEnterCriticalRegionAndAcquireResourceExclusive(IN PERESOURCE Resource)
{
/* Enter critical region */
KeEnterCriticalRegion();
/* Acquire the resource */
NT_VERIFY(ExAcquireResourceExclusiveLite(Resource, TRUE));
/* Return the Win32 Thread */
return KeGetCurrentThread()->Win32Thread;
}
/*++
* @name ExEnterCriticalRegionAndAcquireResourceShared
* @implemented NT5.2
*
* The ExEnterCriticalRegionAndAcquireResourceShared routine
* enters a critical region and then acquires a resource shared.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @return Pointer to the Win32K thread pointer of the current thread.
*
* @remarks See ExAcquireResourceSharedLite.
*
*--*/
PVOID
NTAPI
ExEnterCriticalRegionAndAcquireResourceShared(IN PERESOURCE Resource)
{
/* Enter critical region */
KeEnterCriticalRegion();
/* Acquire the resource */
NT_VERIFY(ExAcquireResourceSharedLite(Resource, TRUE));
/* Return the Win32 Thread */
return KeGetCurrentThread()->Win32Thread;
}
/*++
* @name ExEnterCriticalRegionAndAcquireSharedWaitForExclusive
* @implemented NT5.2
*
* The ExEnterCriticalRegionAndAcquireSharedWaitForExclusive routine
* enters a critical region and then acquires a resource shared if
* shared access can be granted and there are no exclusive waiters.
* It then acquires the resource exclusively.
*
* @param Resource
* Pointer to the resource to acquire.
*
* @return Pointer to the Win32K thread pointer of the current thread.
*
* @remarks See ExAcquireSharedWaitForExclusive.
*
*--*/
PVOID
NTAPI
ExEnterCriticalRegionAndAcquireSharedWaitForExclusive(IN PERESOURCE Resource)
{
/* Enter critical region */
KeEnterCriticalRegion();
/* Acquire the resource */
NT_VERIFY(ExAcquireSharedWaitForExclusive(Resource, TRUE));
/* Return the Win32 Thread */
return KeGetCurrentThread()->Win32Thread;
}
/*++
* @name ExReleaseResourceAndLeaveCriticalRegion
* @implemented NT5.1
*
* The ExReleaseResourceAndLeaveCriticalRegion release a resource and
* then leaves a critical region.
*
* @param Resource
* Pointer to the resource to release.
*
* @return None
*
* @remarks See ExReleaseResourceLite.
*
*--*/
VOID
FASTCALL
ExReleaseResourceAndLeaveCriticalRegion(IN PERESOURCE Resource)
{
/* Release the resource */
ExReleaseResourceLite(Resource);
/* Leave critical region */
KeLeaveCriticalRegion();
}