mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
854 lines
21 KiB
854 lines
21 KiB
/*++
|
|
|
|
Copyright (c) 1999-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
objtable.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the object ID table. The ID table is
|
|
implemented as a two-level array.
|
|
|
|
The first level is an array of pointers to the second-level arrays.
|
|
This first-level array is growable, but this should happen very
|
|
infrequently.
|
|
|
|
The second level is an array of OBJECT_TABLE_ENTRY structures.
|
|
These structures contain a cyclic (to detect stale IDs) and a caller-
|
|
supplied context value.
|
|
|
|
The data structures may be diagrammed as follows:
|
|
|
|
g_FirstLevelTable
|
|
|
|
|
| +-----+
|
|
| | | +-----+-----+-----+-----+-----+-----+
|
|
| | | | OBJECT_ | OBJECT_ | OBJECT_ |
|
|
+-->| *-------->| TABLE_ | TABLE_ | TABLE_ |
|
|
| | | ENTRY | ENTRY | ENTRY |
|
|
| | +-----+-----+-----+-----+-----+-----+
|
|
+-----+
|
|
| | +-----+-----+-----+-----+-----+-----+
|
|
| | | OBJECT_ | OBJECT_ | OBJECT_ |
|
|
| *-------->| TABLE_ | TABLE_ | TABLE_ |
|
|
| | | ENTRY | ENTRY | ENTRY |
|
|
| | +-----+-----+-----+-----+-----+-----+
|
|
+-----+
|
|
| .
|
|
| .
|
|
. .
|
|
. |
|
|
. |
|
|
+-----+
|
|
| |
|
|
| |
|
|
| / |
|
|
| |
|
|
| |
|
|
+-----+
|
|
| |
|
|
| |
|
|
| / |
|
|
| |
|
|
| |
|
|
+-----+
|
|
|
|
Note that all free OBJECT_TABLE_ENTRY structures are kept on a single
|
|
(global) free list. Whenever a new ID needs to be allocated, the free
|
|
list is consulted. If it's not empty, an item is popped from the list
|
|
and used. If the list is empty, then new space must be allocated. Best
|
|
case, this will involve the allocation of a new second-level array.
|
|
Worst case, this will also involve a reallocation of the first-level
|
|
array. Reallocation of the first-level array should be relatively rare.
|
|
|
|
A UL_OPAQUE_ID is opaque at user-mode. Internally, it consists of three
|
|
fields:
|
|
|
|
1) An index into the first-level array.
|
|
2) An index into the second-level array referenced by the
|
|
first-level index.
|
|
3) A cyclic, used to detect stale IDs.
|
|
|
|
See the OPAQUE_ID_INTERNAL structure definition (below) for details.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 20-Apr-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
#define FIRST_LEVEL_TABLE_GROWTH 32 // entries
|
|
#define SECOND_LEVEL_TABLE_SIZE 256 // entries
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
POPAQUE_ID_TABLE_ENTRY
|
|
UlpMapOpaqueIdToTableEntry(
|
|
IN UL_OPAQUE_ID OpaqueId
|
|
);
|
|
|
|
NTSTATUS
|
|
UlpReallocOpaqueIdTables(
|
|
IN ULONG CapturedFirstTableSize,
|
|
IN ULONG CapturedFirstTableInUse
|
|
);
|
|
|
|
__inline
|
|
CYCLIC
|
|
UlpGetNextCyclic(
|
|
VOID
|
|
)
|
|
{
|
|
OPAQUE_ID_TABLE_ENTRY entry;
|
|
|
|
entry.Cyclic = (CYCLIC)InterlockedIncrement( &g_OpaqueIdCyclic );
|
|
entry.EntryType = ENTRY_TYPE_IN_USE;
|
|
|
|
return entry.Cyclic;
|
|
|
|
} // UlpGetNextCyclic
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
LIST_ENTRY g_UlObjectTypeListHead;
|
|
UL_SPIN_LOCK g_UlObjectLock;
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( INIT, UlInitializeObjectTablePackage )
|
|
#pragma alloc_text( PAGE, UlTerminateObjectTablePackage )
|
|
#pragma alloc_text( INIT, UlInitializeObjectType )
|
|
#pragma alloc_text( PAGE, UlCreateObjectTable )
|
|
#pragma alloc_text( PAGE, UlDestroyObjectTable )
|
|
#pragma alloc_text( PAGE, UlCreateObject )
|
|
#pragma alloc_text( PAGE, UlCloseObject )
|
|
#pragma alloc_text( PAGE, UlReferenceObjectByOpaqueId )
|
|
#pragma alloc_text( PAGE, UlReferenceObjectByPointer )
|
|
#pragma alloc_text( PAGE, UlDereferenceObject )
|
|
#endif // ALLOC_PRAGMA
|
|
#if 0
|
|
NOT PAGEABLE -- UlpMapOpaqueIdToTableEntry
|
|
NOT PAGEABLE -- UlpReallocOpaqueIdTables
|
|
#endif
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Performs global initialization of the object table package.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlInitializeObjectTablePackage(
|
|
VOID
|
|
)
|
|
{
|
|
LARGE_INTEGER timeNow;
|
|
|
|
INITIALIZE_LOCK( &g_OpaqueIdTableLock );
|
|
KeInitializeSpinLock( &g_FreeOpaqueIdSListLock );
|
|
ExInitializeSListHead( &g_FreeOpaqueIdSListHead );
|
|
|
|
KeQuerySystemTime( &timeNow );
|
|
g_OpaqueIdCyclic = (LONG)( timeNow.HighPart + timeNow.LowPart );
|
|
|
|
g_FirstLevelTableInUse = 0;
|
|
g_FirstLevelTableSize = FIRST_LEVEL_TABLE_GROWTH;
|
|
|
|
//
|
|
// Go ahead and allocate the first-level table now. This makes the
|
|
// normal runtime path a little cleaner because it doesn't have to
|
|
// deal with the "first time" case.
|
|
//
|
|
|
|
g_FirstLevelTable = UL_ALLOCATE_ARRAY(
|
|
NonPagedPool,
|
|
POPAQUE_ID_TABLE_ENTRY,
|
|
g_FirstLevelTableSize,
|
|
UL_OPAQUE_ID_TABLE_POOL_TAG
|
|
);
|
|
|
|
if (g_FirstLevelTable == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
g_FirstLevelTable,
|
|
g_FirstLevelTableSize * sizeof(POPAQUE_ID_TABLE_ENTRY)
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // InitializeOpaqueIdTable
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Performs global termination of the object table package.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlTerminateObjectTablePackage(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
if (g_FirstLevelTable != NULL)
|
|
{
|
|
//
|
|
// Free the second-level tables.
|
|
//
|
|
|
|
for (i = 0 ; i < g_FirstLevelTableInUse ; i++)
|
|
{
|
|
if (g_FirstLevelTable[i] != NULL)
|
|
{
|
|
UL_FREE_POOL(
|
|
g_FirstLevelTable[i],
|
|
UL_OPAQUE_ID_TABLE_POOL_TAG
|
|
);
|
|
}
|
|
|
|
g_FirstLevelTable[i] = NULL;
|
|
}
|
|
|
|
//
|
|
// Free the first-level table.
|
|
//
|
|
|
|
UL_FREE_POOL(
|
|
g_FirstLevelTable,
|
|
UL_OPAQUE_ID_TABLE_POOL_TAG
|
|
);
|
|
|
|
g_FirstLevelTable = NULL;
|
|
}
|
|
|
|
ExInitializeSListHead( &g_FreeOpaqueIdSListHead );
|
|
|
|
g_OpaqueIdCyclic = 0;
|
|
g_FirstLevelTableSize = 0;
|
|
g_FirstLevelTableInUse = 0;
|
|
|
|
} // TerminateOpaqueIdTable
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a new opaque ID and associates it with the specified context.
|
|
|
|
Arguments:
|
|
|
|
pOpaqueId - Receives the newly allocated opaque ID if successful.
|
|
|
|
OpaqueIdType - Supplies an uninterpreted
|
|
|
|
pContext - Supplies the context to associate with the new opaque ID.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlAllocateOpaqueId(
|
|
OUT PUL_OPAQUE_ID pOpaqueId,
|
|
IN UL_OPAQUE_ID_TYPE OpaqueIdType,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
CYCLIC cyclic;
|
|
ULONG firstIndex;
|
|
ULONG secondIndex;
|
|
PSINGLE_LIST_ENTRY listEntry;
|
|
OPAQUE_ID_INTERNAL internal;
|
|
POPAQUE_ID_TABLE_ENTRY tableEntry;
|
|
ULONG capturedFirstTableSize;
|
|
ULONG capturedFirstTableInUse;
|
|
|
|
#if HACK_OPAQUE_ID
|
|
KIRQL oldIrql;
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
#endif
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Allocate a new cyclic while we're outside the lock.
|
|
//
|
|
|
|
cyclic = UlpGetNextCyclic();
|
|
|
|
//
|
|
// Loop, trying to allocate an item from the table.
|
|
//
|
|
|
|
do
|
|
{
|
|
ACQUIRE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
|
|
listEntry = ExInterlockedPopEntrySList(
|
|
&g_FreeOpaqueIdSListHead,
|
|
&g_FreeOpaqueIdSListLock
|
|
);
|
|
|
|
if (listEntry != NULL)
|
|
{
|
|
//
|
|
// The free list isn't empty, so we can just use this
|
|
// entry. We'll calculate the indices for this entry,
|
|
// initialize the entry, then release the table lock.
|
|
//
|
|
|
|
tableEntry = CONTAINING_RECORD(
|
|
listEntry,
|
|
OPAQUE_ID_TABLE_ENTRY,
|
|
FreeListEntry
|
|
);
|
|
|
|
firstIndex = tableEntry->FirstLevelIndex;
|
|
secondIndex = (ULONG)(tableEntry - g_FirstLevelTable[firstIndex]);
|
|
|
|
tableEntry->pContext = pContext;
|
|
tableEntry->Cyclic = cyclic;
|
|
|
|
RELEASE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
|
|
//
|
|
// Pack the cyclic & indices into the opaque ID and return it.
|
|
//
|
|
|
|
internal.Cyclic = cyclic;
|
|
internal.FirstIndex = firstIndex;
|
|
internal.SecondIndex = secondIndex;
|
|
|
|
*pOpaqueId = internal.OpaqueId;
|
|
|
|
#if HACK_OPAQUE_ID
|
|
if (oldIrql != DISPATCH_LEVEL)
|
|
{
|
|
KeLowerIrql( oldIrql );
|
|
}
|
|
#endif
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// We only make it to this point if the free list is empty,
|
|
// meaning we need to do some memory allocations before
|
|
// we can continue. We'll put this off into a separate routine
|
|
// to keep this one small (to avoid cache thrash). The realloc
|
|
// routine returns STATUS_SUCCESS if it (or another thread)
|
|
// managed to successfully reallocate the tables. Otherwise, it
|
|
// returns a failure code.
|
|
//
|
|
|
|
capturedFirstTableSize = g_FirstLevelTableSize;
|
|
capturedFirstTableInUse = g_FirstLevelTableInUse;
|
|
RELEASE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
|
|
status = UlpReallocOpaqueIdTables(
|
|
capturedFirstTableSize,
|
|
capturedFirstTableInUse
|
|
);
|
|
|
|
} while( status == STATUS_SUCCESS );
|
|
|
|
return status;
|
|
|
|
} // UlAllocateOpaqueId
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the specified opaque ID.
|
|
|
|
Arguments:
|
|
|
|
OpaqueId - Supplies the connection ID to free.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Returns the HTTP_CONNECTION associated with the
|
|
connection ID if successful, NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlFreeOpaqueId(
|
|
IN UL_OPAQUE_ID OpaqueId,
|
|
IN UL_OPAQUE_ID_TYPE OpaqueIdType
|
|
)
|
|
{
|
|
POPAQUE_ID_TABLE_ENTRY tableEntry;
|
|
PVOID pContext;
|
|
|
|
#if HACK_OPAQUE_ID
|
|
KIRQL oldIrql;
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
#endif
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Try to map the opaque ID to a table entry.
|
|
//
|
|
|
|
tableEntry = UlpMapOpaqueIdToTableEntry( OpaqueId );
|
|
|
|
if (tableEntry != NULL)
|
|
{
|
|
OPAQUE_ID_INTERNAL internal;
|
|
|
|
//
|
|
// Got a match. Snag the context, free the table entry, then
|
|
// unlock the table.
|
|
//
|
|
|
|
pContext = tableEntry->pContext;
|
|
|
|
internal.OpaqueId = OpaqueId;
|
|
tableEntry->FirstLevelIndex = internal.FirstIndex;
|
|
tableEntry->EntryType = ENTRY_TYPE_FREE;
|
|
|
|
ExInterlockedPushEntrySList(
|
|
&g_FreeOpaqueIdSListHead,
|
|
&tableEntry->FreeListEntry,
|
|
&g_FreeOpaqueIdSListLock
|
|
);
|
|
|
|
RELEASE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
#if HACK_OPAQUE_ID
|
|
if (oldIrql != DISPATCH_LEVEL)
|
|
{
|
|
KeLowerIrql( oldIrql );
|
|
}
|
|
#endif
|
|
return pContext;
|
|
}
|
|
|
|
#if HACK_OPAQUE_ID
|
|
if (oldIrql != DISPATCH_LEVEL)
|
|
{
|
|
KeLowerIrql( oldIrql );
|
|
}
|
|
#endif
|
|
return NULL;
|
|
|
|
} // UlFreeOpaqueId
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Maps the specified opaque ID to the corresponding context value.
|
|
|
|
Arguments:
|
|
|
|
OpaqueId - Supplies the opaque ID to map.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Returns the context value associated with the opaque ID
|
|
if successful, NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlGetObjectFromOpaqueId(
|
|
IN UL_OPAQUE_ID OpaqueId,
|
|
IN UL_OPAQUE_ID_TYPE OpaqueIdType
|
|
)
|
|
{
|
|
POPAQUE_ID_TABLE_ENTRY tableEntry;
|
|
PVOID pContext;
|
|
|
|
#if HACK_OPAQUE_ID
|
|
KIRQL oldIrql;
|
|
|
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|
#endif
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Try to map the opaque ID to a table entry.
|
|
//
|
|
|
|
tableEntry = UlpMapOpaqueIdToTableEntry( OpaqueId );
|
|
|
|
if (tableEntry != NULL)
|
|
{
|
|
//
|
|
// Got a match. Retrieve the context value, then unlock the
|
|
// table. Note that we cannot touch the table entry once we
|
|
// unlock the table.
|
|
//
|
|
|
|
pContext = tableEntry->pContext;
|
|
|
|
RELEASE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
#if HACK_OPAQUE_ID
|
|
if (oldIrql != DISPATCH_LEVEL)
|
|
{
|
|
KeLowerIrql( oldIrql );
|
|
}
|
|
#endif
|
|
return pContext;
|
|
}
|
|
|
|
#if HACK_OPAQUE_ID
|
|
if (oldIrql != DISPATCH_LEVEL)
|
|
{
|
|
KeLowerIrql( oldIrql );
|
|
}
|
|
#endif
|
|
return NULL;
|
|
|
|
} // UlGetObjectFromOpaqueId
|
|
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Maps the specified UL_OPAQUE_ID to the corresponding
|
|
PVOID pointer.
|
|
|
|
Arguments:
|
|
|
|
OpaqueId - Supplies the opaque ID to map.
|
|
|
|
Return Value:
|
|
|
|
POPAQUE_ID_TABLE_ENTRY - Pointer to the table entry corresponding to the
|
|
opaque ID if successful, NULL otherwise.
|
|
|
|
N.B. If this function is successful, it returns with the table lock
|
|
held for read access!
|
|
|
|
--***************************************************************************/
|
|
POPAQUE_ID_TABLE_ENTRY
|
|
UlpMapOpaqueIdToTableEntry(
|
|
IN UL_OPAQUE_ID OpaqueId
|
|
)
|
|
{
|
|
POPAQUE_ID_TABLE_ENTRY secondTable;
|
|
POPAQUE_ID_TABLE_ENTRY tableEntry;
|
|
OPAQUE_ID_INTERNAL internal;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Unpack the ID.
|
|
//
|
|
|
|
internal.OpaqueId = OpaqueId;
|
|
|
|
//
|
|
// Lock the table.
|
|
//
|
|
|
|
ACQUIRE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
|
|
//
|
|
// Validate the index.
|
|
//
|
|
|
|
if (internal.FirstIndex >= 0 &&
|
|
internal.FirstIndex < g_FirstLevelTableInUse)
|
|
{
|
|
secondTable = g_FirstLevelTable[internal.FirstIndex];
|
|
ASSERT( secondTable != NULL );
|
|
ASSERT( internal.SecondIndex >= 0 );
|
|
ASSERT( internal.SecondIndex <= SECOND_LEVEL_TABLE_SIZE );
|
|
|
|
tableEntry = secondTable + internal.SecondIndex;
|
|
|
|
//
|
|
// The index is within legal range. Ensure it's in use and
|
|
// the cyclic matches.
|
|
//
|
|
|
|
if (tableEntry->Cyclic == internal.Cyclic &&
|
|
tableEntry->EntryType == ENTRY_TYPE_IN_USE)
|
|
{
|
|
return tableEntry;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Invalid opaque ID. Fail it.
|
|
//
|
|
|
|
RELEASE_READ_LOCK( &g_OpaqueIdTableLock );
|
|
return NULL;
|
|
|
|
} // UlpMapOpaqueIdToTableEntry
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a new second-level table, and (optionally) reallocates the
|
|
first-level table if necessary.
|
|
|
|
Arguments:
|
|
|
|
CapturedFirstTableSize - The size of the first-level table as captured
|
|
with the table lock held.
|
|
|
|
CapturedFirstTableInUse - The number of entries currently used in the
|
|
first-level table as captured with the table lock held.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpReallocOpaqueIdTables(
|
|
IN ULONG CapturedFirstTableSize,
|
|
IN ULONG CapturedFirstTableInUse
|
|
)
|
|
{
|
|
ULONG firstIndex;
|
|
ULONG secondIndex;
|
|
POPAQUE_ID_TABLE_ENTRY tableEntry;
|
|
POPAQUE_ID_TABLE_ENTRY newSecondTable;
|
|
POPAQUE_ID_TABLE_ENTRY *newFirstTable;
|
|
ULONG newFirstTableSize;
|
|
ULONG i;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
ASSERT( ExQueryDepthSList( &g_FreeOpaqueIdSListHead ) == 0 );
|
|
|
|
//
|
|
// Assume we won't actually allocate anything.
|
|
//
|
|
|
|
newFirstTable = NULL;
|
|
newSecondTable = NULL;
|
|
|
|
//
|
|
// Allocate a new second-level table.
|
|
//
|
|
|
|
newSecondTable = UL_ALLOCATE_ARRAY(
|
|
NonPagedPool,
|
|
OPAQUE_ID_TABLE_ENTRY,
|
|
SECOND_LEVEL_TABLE_SIZE,
|
|
UL_OPAQUE_ID_TABLE_POOL_TAG
|
|
);
|
|
|
|
if (newSecondTable == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (CapturedFirstTableInUse == CapturedFirstTableSize)
|
|
{
|
|
//
|
|
// We need a new first-level table.
|
|
//
|
|
|
|
newFirstTableSize =
|
|
CapturedFirstTableSize + FIRST_LEVEL_TABLE_GROWTH;
|
|
|
|
newFirstTable = UL_ALLOCATE_ARRAY(
|
|
NonPagedPool,
|
|
POPAQUE_ID_TABLE_ENTRY,
|
|
newFirstTableSize,
|
|
UL_OPAQUE_ID_TABLE_POOL_TAG
|
|
);
|
|
|
|
if (newFirstTable == NULL)
|
|
{
|
|
UL_FREE_POOL( newSecondTable, UL_OPAQUE_ID_TABLE_POOL_TAG );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
newFirstTable + CapturedFirstTableSize,
|
|
FIRST_LEVEL_TABLE_GROWTH * sizeof(POPAQUE_ID_TABLE_ENTRY)
|
|
);
|
|
}
|
|
|
|
//
|
|
// OK, we've got the new table(s). Acquire the lock for write access
|
|
// and see if another thread has already done the work for us.
|
|
//
|
|
|
|
ACQUIRE_WRITE_LOCK( &g_OpaqueIdTableLock );
|
|
|
|
if (ExQueryDepthSList( &g_FreeOpaqueIdSListHead ) == 0)
|
|
{
|
|
//
|
|
// The free list is still empty. This could potentially
|
|
// mean that another thread has already performed the
|
|
// realloc and all of *those* new entries are now in use.
|
|
// We can detect this by comparing the current table size
|
|
// with the size we captured previously with the lock held.
|
|
//
|
|
|
|
if (CapturedFirstTableSize != g_FirstLevelTableSize)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// OK, we're the one performing the realloc.
|
|
//
|
|
|
|
if (newFirstTable != NULL)
|
|
{
|
|
POPAQUE_ID_TABLE_ENTRY *tmp;
|
|
|
|
//
|
|
// Copy the old table into the new table.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
newFirstTable,
|
|
g_FirstLevelTable,
|
|
g_FirstLevelTableSize * sizeof(POPAQUE_ID_TABLE_ENTRY)
|
|
);
|
|
|
|
//
|
|
// Swap the new & old table pointers so we can keep the
|
|
// original pointer & free it later (after we've released
|
|
// the table lock).
|
|
//
|
|
|
|
tmp = g_FirstLevelTable;
|
|
g_FirstLevelTable = newFirstTable;
|
|
newFirstTable = tmp;
|
|
|
|
g_FirstLevelTableSize = newFirstTableSize;
|
|
}
|
|
|
|
//
|
|
// Attach the second-level table to the first-level table,
|
|
//
|
|
|
|
ASSERT( g_FirstLevelTable[g_FirstLevelTableInUse] == NULL );
|
|
g_FirstLevelTable[g_FirstLevelTableInUse++] = newSecondTable;
|
|
ASSERT( g_FirstLevelTableInUse <= g_FirstLevelTableSize );
|
|
|
|
//
|
|
// Link it onto the global free list.
|
|
//
|
|
|
|
for (i = 0 ; i < SECOND_LEVEL_TABLE_SIZE ; i++)
|
|
{
|
|
newSecondTable[i].FirstLevelIndex = CapturedFirstTableInUse;
|
|
newSecondTable[i].EntryType = ENTRY_TYPE_FREE;
|
|
|
|
ExInterlockedPushEntrySList(
|
|
&g_FreeOpaqueIdSListHead,
|
|
&newSecondTable[i].FreeListEntry,
|
|
&g_FreeOpaqueIdSListLock
|
|
);
|
|
}
|
|
|
|
//
|
|
// Remember that we don't need to free the second-level table.
|
|
//
|
|
|
|
newSecondTable = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The free list was not empty after we reacquired the lock,
|
|
// indicating that another thread has already performed the
|
|
// realloc. This is cool; we'll just free the pool we allocated
|
|
// and return.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Cleanup.
|
|
//
|
|
|
|
cleanup:
|
|
|
|
RELEASE_WRITE_LOCK( &g_OpaqueIdTableLock );
|
|
|
|
if (newSecondTable != NULL)
|
|
{
|
|
UL_FREE_POOL( newSecondTable, UL_OPAQUE_ID_TABLE_POOL_TAG );
|
|
}
|
|
|
|
if (newFirstTable != NULL)
|
|
{
|
|
UL_FREE_POOL( newFirstTable, UL_OPAQUE_ID_TABLE_POOL_TAG );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlpReallocOpaqueIdTables
|
|
|