/* $Id$
- *
+ *
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ex/handle.c
* PURPOSE: Generic Executive Handle Tables
- *
+ *
* PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
*
* TODO:
*
* - the last entry of a subhandle list should be reserved for auditing
*
- * ExSweepHandleTable (???)
* ExReferenceHandleDebugInfo
* ExSnapShotHandleTables
* ExpMoveFreeHandles (???)
#define IS_VALID_EX_HANDLE(index) \
(((index) & ~VALID_HANDLE_MASK) == 0)
+#define HANDLE_TO_EX_HANDLE(handle) \
+ (LONG)(((LONG)(handle) >> 2) - 1)
+#define EX_HANDLE_TO_HANDLE(exhandle) \
+ (HANDLE)(((exhandle) + 1) << 2)
+
static BOOLEAN ExpInitialized = FALSE;
/******************************************************************************/
ExCreateHandleTable(IN PEPROCESS QuotaProcess OPTIONAL)
{
PHANDLE_TABLE HandleTable;
-
+
PAGED_CODE();
-
+
if(!ExpInitialized)
{
KEBUGCHECK(0);
}
-
+
if(QuotaProcess != NULL)
{
/* FIXME - Charge process quota before allocating the handle table! */
{
/* FIXME - return the quota to the process */
}
-
+
return HandleTable;
}
-static BOOLEAN
-ExLockHandleTableEntryNoDestructionCheck(IN PHANDLE_TABLE HandleTable,
- IN PHANDLE_TABLE_ENTRY Entry)
+VOID
+ExSweepHandleTable(IN PHANDLE_TABLE HandleTable,
+ IN PEX_SWEEP_HANDLE_CALLBACK SweepHandleCallback OPTIONAL,
+ IN PVOID Context OPTIONAL)
{
- ULONG_PTR Current, New;
+ PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp;
PAGED_CODE();
- DPRINT("Entering handle table entry 0x%x lock...\n", Entry);
-
ASSERT(HandleTable);
- ASSERT(Entry);
- for(;;)
- {
- Current = (volatile ULONG_PTR)Entry->u1.Object;
-
- if(!Current)
- {
- DPRINT("Attempted to lock empty handle table entry 0x%x or handle table shut down\n", Entry);
- break;
- }
-
- if(!(Current & EX_HANDLE_ENTRY_LOCKED))
- {
- New = Current | EX_HANDLE_ENTRY_LOCKED;
- if(InterlockedCompareExchangePointer(&Entry->u1.Object,
- (PVOID)New,
- (PVOID)Current) == (PVOID)Current)
- {
- DPRINT("SUCCESS handle table 0x%x entry 0x%x lock\n", HandleTable, Entry);
- /* we acquired the lock */
- return TRUE;
- }
- }
-
- /* wait about 5ms at maximum so we don't wait forever in unfortunate
- co-incidences where releasing the lock in another thread happens right
- before we're waiting on the contention event to get pulsed, which might
- never happen again... */
- KeWaitForSingleObject(&HandleTable->HandleContentionEvent,
- Executive,
- KernelMode,
- FALSE,
- &ExpHandleShortWait);
- }
-
- return FALSE;
-}
-
-VOID
-ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable,
- IN PEX_DESTROY_HANDLE_CALLBACK DestroyHandleCallback OPTIONAL,
- IN PVOID Context OPTIONAL)
-{
- PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp;
- PEPROCESS QuotaProcess;
-
- PAGED_CODE();
-
- ASSERT(HandleTable);
-
KeEnterCriticalRegion();
-
+
/* ensure there's no other operations going by acquiring an exclusive lock */
ExAcquireHandleTableLockExclusive(HandleTable);
-
+
ASSERT(!(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING));
-
+
HandleTable->Flags |= EX_HANDLE_TABLE_CLOSING;
-
+
KePulseEvent(&HandleTable->HandleContentionEvent,
EVENT_INCREMENT,
FALSE);
-
- /* remove the handle table from the global handle table list */
- ExAcquireHandleTableListLock();
- RemoveEntryList(&HandleTable->HandleTableList);
- ExReleaseHandleTableListLock();
-
+
/* call the callback function to cleanup the objects associated with the
handle table */
- if(DestroyHandleCallback != NULL)
+ for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS;
+ tlp != lasttlp;
+ tlp++)
{
- for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS;
- tlp != lasttlp;
- tlp++)
+ if((*tlp) != NULL)
{
- if((*tlp) != NULL)
+ for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS;
+ mlp != lastmlp;
+ mlp++)
{
- for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS;
- mlp != lastmlp;
- mlp++)
+ if((*mlp) != NULL)
{
- if((*mlp) != NULL)
+ PHANDLE_TABLE_ENTRY curee, laste;
+
+ for(curee = *mlp, laste = *mlp + N_SUBHANDLE_ENTRIES;
+ curee != laste;
+ curee++)
{
- PHANDLE_TABLE_ENTRY curee, laste;
-
- for(curee = *mlp, laste = *mlp + N_SUBHANDLE_ENTRIES;
- curee != laste;
- curee++)
+ if(curee->u1.Object != NULL && SweepHandleCallback != NULL)
{
- if(curee->u1.Object != NULL && ExLockHandleTableEntryNoDestructionCheck(HandleTable, curee))
- {
- DestroyHandleCallback(HandleTable, curee->u1.Object, curee->u2.GrantedAccess, Context);
- ExUnlockHandleTableEntry(HandleTable, curee);
- }
+ curee->u1.ObAttributes |= EX_HANDLE_ENTRY_LOCKED;
+ SweepHandleCallback(HandleTable, curee->u1.Object, curee->u2.GrantedAccess, Context);
}
}
}
}
}
}
-
+
+ ExReleaseHandleTableLock(HandleTable);
+
+ KeLeaveCriticalRegion();
+}
+
+VOID
+ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable)
+{
+ PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp;
+ PEPROCESS QuotaProcess;
+
+ PAGED_CODE();
+
+ ASSERT(HandleTable);
+ ASSERT(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING);
+
+ KeEnterCriticalRegion();
+
+ /* at this point the table should not be queried or altered anymore,
+ no locks should be necessary */
+
+ ASSERT(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING);
+
+ /* remove the handle table from the global handle table list */
+ ExAcquireHandleTableListLock();
+ RemoveEntryList(&HandleTable->HandleTableList);
+ ExReleaseHandleTableListLock();
+
QuotaProcess = HandleTable->QuotaProcess;
-
+
/* free the tables */
for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS;
tlp != lasttlp;
if((*mlp) != NULL)
{
ExFreePool(*mlp);
-
+
if(QuotaProcess != NULL)
{
/* FIXME - return the quota to the process */
}
ExFreePool(*tlp);
-
+
if(QuotaProcess != NULL)
{
/* FIXME - return the quota to the process */
}
}
}
-
- ExReleaseHandleTableLock(HandleTable);
-
+
KeLeaveCriticalRegion();
-
+
/* free the handle table */
ExDeleteResource(&HandleTable->HandleTableLock);
ExFreePool(HandleTable);
-
+
if(QuotaProcess != NULL)
{
/* FIXME - return the quota to the process */
IN PHANDLE_TABLE SourceHandleTable)
{
PHANDLE_TABLE HandleTable;
-
+
PAGED_CODE();
-
+
ASSERT(SourceHandleTable);
HandleTable = ExCreateHandleTable(QuotaProcess);
{
PHANDLE_TABLE_ENTRY **tlp, **srctlp, **etlp, *mlp, *srcmlp, *emlp, stbl, srcstbl, estbl;
LONG tli, mli, eli;
-
+
tli = mli = eli = 0;
-
+
/* make sure the other handle table isn't being changed during the duplication */
ExAcquireHandleTableLockShared(SourceHandleTable);
-
+
/* allocate enough tables */
etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS;
for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table;
{
/* FIXME - Charge process quota before allocating the handle table! */
}
-
+
*tlp = ExAllocatePoolWithTag(PagedPool,
N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY),
TAG('E', 'x', 'H', 't'));
if(*tlp != NULL)
{
RtlZeroMemory(*tlp, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY));
-
+
KeMemoryBarrier();
-
+
emlp = *srctlp + N_MIDDLELEVEL_POINTERS;
for(srcmlp = *srctlp, mlp = *tlp;
srcmlp != emlp;
else
{
freehandletable:
- DPRINT1("Failed to duplicate handle table 0x%x\n", SourceHandleTable);
+ DPRINT1("Failed to duplicate handle table 0x%p\n", SourceHandleTable);
ExReleaseHandleTableLock(SourceHandleTable);
-
- ExDestroyHandleTable(HandleTable,
- NULL,
- NULL);
+
+ ExDestroyHandleTable(HandleTable);
/* allocate an empty handle table */
return ExCreateHandleTable(QuotaProcess);
}
}
}
}
-
+
/* release the source handle table */
ExReleaseHandleTableLock(SourceHandleTable);
}
-
+
return HandleTable;
}
static PHANDLE_TABLE_ENTRY
ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable,
- OUT PLONG Handle)
+ OUT PHANDLE Handle)
{
PHANDLE_TABLE_ENTRY Entry = NULL;
-
+
PAGED_CODE();
-
+
ASSERT(HandleTable);
ASSERT(Handle);
ASSERT(KeGetCurrentThread() != NULL);
-
- DPRINT("HT[0x%x]: HandleCount: %d\n", HandleTable, HandleTable->HandleCount);
-
+
+ DPRINT("HT[0x%p]: HandleCount: %d\n", HandleTable, HandleTable->HandleCount);
+
if(HandleTable->HandleCount < EX_MAX_HANDLES)
{
ULONG tli, mli, eli;
-
+
if(HandleTable->FirstFreeTableEntry != -1)
{
/* there's a free handle entry we can just grab and use */
tli = TLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry);
mli = MLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry);
eli = ELI_FROM_HANDLE(HandleTable->FirstFreeTableEntry);
-
+
/* the pointer should be valid in any way!!! */
ASSERT(HandleTable->Table[tli]);
ASSERT(HandleTable->Table[tli][mli]);
-
+
Entry = &HandleTable->Table[tli][mli][eli];
-
- *Handle = HandleTable->FirstFreeTableEntry;
-
+
+ *Handle = EX_HANDLE_TO_HANDLE(HandleTable->FirstFreeTableEntry);
+
/* save the index to the next free handle (if available) */
HandleTable->FirstFreeTableEntry = Entry->u2.NextFreeTableEntry;
Entry->u2.NextFreeTableEntry = 0;
Entry->u1.Object = NULL;
-
+
HandleTable->HandleCount++;
}
else
BOOLEAN AllocatedMtbl;
ASSERT(HandleTable->NextIndexNeedingPool <= N_MAX_HANDLE);
-
+
/* the index of the next table to be allocated was saved in
NextIndexNeedingPool the last time a handle entry was allocated and
the subhandle entry list was full. the subhandle entry index of
ASSERT(ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool) == 0);
- DPRINT("HandleTable->Table[%d] == 0x%x\n", tli, HandleTable->Table[tli]);
-
+ DPRINT("HandleTable->Table[%d] == 0x%p\n", tli, HandleTable->Table[tli]);
+
/* allocate a middle level entry list if required */
nmtbl = HandleTable->Table[tli];
if(nmtbl == NULL)
{
/* FIXME - Charge process quota before allocating the handle table! */
}
-
+
nmtbl = ExAllocatePoolWithTag(PagedPool,
N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY),
TAG('E', 'x', 'H', 't'));
{
/* FIXME - return the quota to the process */
}
-
+
return NULL;
}
-
+
/* clear the middle level entry list */
RtlZeroMemory(nmtbl, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY));
-
+
/* make sure the table was zeroed before we set one item */
KeMemoryBarrier();
-
+
/* note, don't set the the pointer in the top level list yet because we
might screw up lookups if allocating a subhandle entry table failed
and this newly allocated table might get freed again */
else
{
AllocatedMtbl = FALSE;
-
+
/* allocate a subhandle entry table in any case! */
ASSERT(nmtbl[mli] == NULL);
}
- DPRINT("HandleTable->Table[%d][%d] == 0x%x\n", tli, mli, nmtbl[mli]);
+ DPRINT("HandleTable->Table[%d][%d] == 0x%p\n", tli, mli, nmtbl[mli]);
if(HandleTable->QuotaProcess != NULL)
{
{
/* FIXME - Return process quota charged */
}
-
+
/* free the middle level entry list, if allocated, because it's empty and
unused */
if(AllocatedMtbl)
{
ExFreePool(nmtbl);
-
+
if(HandleTable->QuotaProcess != NULL)
{
/* FIXME - Return process quota charged */
}
}
-
+
return NULL;
}
-
+
/* let's just use the very first entry */
Entry = ntbl;
Entry->u1.ObAttributes = EX_HANDLE_ENTRY_LOCKED;
Entry->u2.NextFreeTableEntry = 0;
-
- *Handle = HandleTable->NextIndexNeedingPool;
-
+
+ *Handle = EX_HANDLE_TO_HANDLE(HandleTable->NextIndexNeedingPool);
+
HandleTable->HandleCount++;
-
+
/* set the FirstFreeTableEntry member to the second entry and chain the
free entries */
HandleTable->FirstFreeTableEntry = HandleTable->NextIndexNeedingPool + 1;
{
InterlockedExchangePointer(&HandleTable->Table[tli], nmtbl);
}
-
+
/* increment the NextIndexNeedingPool to the next index where we need to
allocate new memory */
HandleTable->NextIndexNeedingPool += N_SUBHANDLE_ENTRIES;
}
else
{
- DPRINT1("Can't allocate any more handles in handle table 0x%x!\n", HandleTable);
+ DPRINT1("Can't allocate any more handles in handle table 0x%p!\n", HandleTable);
}
-
+
return Entry;
}
IN LONG Handle)
{
PAGED_CODE();
-
+
ASSERT(HandleTable);
ASSERT(Entry);
ASSERT(IS_VALID_EX_HANDLE(Handle));
-
- DPRINT("ExpFreeHandleTableEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry);
-
+
+ DPRINT("ExpFreeHandleTableEntry HT:0x%p Entry:0x%p\n", HandleTable, Entry);
+
/* automatically unlock the entry if currently locked. We however don't notify
anyone who waited on the handle because we're holding an exclusive lock after
all and these locks will fail then */
InterlockedExchangePointer(&Entry->u1.Object, NULL);
Entry->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry;
HandleTable->FirstFreeTableEntry = Handle;
-
+
HandleTable->HandleCount--;
}
IN LONG Handle)
{
PHANDLE_TABLE_ENTRY Entry = NULL;
-
+
PAGED_CODE();
-
+
ASSERT(HandleTable);
-
+
if(IS_VALID_EX_HANDLE(Handle))
{
ULONG tli, mli, eli;
PHANDLE_TABLE_ENTRY *mlp;
-
+
tli = TLI_FROM_HANDLE(Handle);
mli = MLI_FROM_HANDLE(Handle);
eli = ELI_FROM_HANDLE(Handle);
-
+
mlp = HandleTable->Table[tli];
if(Handle < HandleTable->NextIndexNeedingPool &&
mlp != NULL && mlp[mli] != NULL && mlp[mli][eli].u1.Object != NULL)
{
Entry = &mlp[mli][eli];
- DPRINT("handle lookup 0x%x -> entry 0x%x [HT:0x%x] ptr: 0x%x\n", Handle, Entry, HandleTable, mlp[mli][eli].u1.Object);
+ DPRINT("handle lookup 0x%x -> entry 0x%p [HT:0x%p] ptr: 0x%p\n", Handle, Entry, HandleTable, mlp[mli][eli].u1.Object);
}
}
else
{
DPRINT("Looking up invalid handle 0x%x\n", Handle);
}
-
+
return Entry;
}
ULONG_PTR Current, New;
PAGED_CODE();
-
- DPRINT("Entering handle table entry 0x%x lock...\n", Entry);
-
+
+ DPRINT("Entering handle table entry 0x%p lock...\n", Entry);
+
ASSERT(HandleTable);
ASSERT(Entry);
-
+
for(;;)
{
Current = (volatile ULONG_PTR)Entry->u1.Object;
-
+
if(!Current || (HandleTable->Flags & EX_HANDLE_TABLE_CLOSING))
{
- DPRINT("Attempted to lock empty handle table entry 0x%x or handle table shut down\n", Entry);
+ DPRINT("Attempted to lock empty handle table entry 0x%p or handle table shut down\n", Entry);
break;
}
-
+
if(!(Current & EX_HANDLE_ENTRY_LOCKED))
{
New = Current | EX_HANDLE_ENTRY_LOCKED;
(PVOID)New,
(PVOID)Current) == (PVOID)Current)
{
- DPRINT("SUCCESS handle table 0x%x entry 0x%x lock\n", HandleTable, Entry);
+ DPRINT("SUCCESS handle table 0x%p entry 0x%p lock\n", HandleTable, Entry);
/* we acquired the lock */
return TRUE;
}
IN PHANDLE_TABLE_ENTRY Entry)
{
ULONG_PTR Current, New;
-
+
PAGED_CODE();
-
+
ASSERT(HandleTable);
ASSERT(Entry);
-
- DPRINT("ExUnlockHandleTableEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry);
-
+
+ DPRINT("ExUnlockHandleTableEntry HT:0x%p Entry:0x%p\n", HandleTable, Entry);
+
Current = (volatile ULONG_PTR)Entry->u1.Object;
ASSERT(Current & EX_HANDLE_ENTRY_LOCKED);
-
+
New = Current & ~EX_HANDLE_ENTRY_LOCKED;
-
+
InterlockedExchangePointer(&Entry->u1.Object,
(PVOID)New);
FALSE);
}
-LONG
+HANDLE
ExCreateHandle(IN PHANDLE_TABLE HandleTable,
IN PHANDLE_TABLE_ENTRY Entry)
{
PHANDLE_TABLE_ENTRY NewHandleTableEntry;
- LONG Handle = EX_INVALID_HANDLE;
-
+ HANDLE Handle = NULL;
+
PAGED_CODE();
-
+
ASSERT(HandleTable);
ASSERT(Entry);
-
+
/* The highest bit in Entry->u1.Object has to be 1 so we make sure it's a
pointer to kmode memory. It will cleared though because it also indicates
the lock */
ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED);
-
+
KeEnterCriticalRegion();
ExAcquireHandleTableLockExclusive(HandleTable);
-
+
NewHandleTableEntry = ExpAllocateHandleTableEntry(HandleTable,
&Handle);
if(NewHandleTableEntry != NULL)
{
*NewHandleTableEntry = *Entry;
-
+
ExUnlockHandleTableEntry(HandleTable,
NewHandleTableEntry);
}
BOOLEAN
ExDestroyHandle(IN PHANDLE_TABLE HandleTable,
- IN LONG Handle)
+ IN HANDLE Handle)
{
PHANDLE_TABLE_ENTRY HandleTableEntry;
+ LONG ExHandle;
BOOLEAN Ret = FALSE;
-
+
PAGED_CODE();
-
+
ASSERT(HandleTable);
+ ExHandle = HANDLE_TO_EX_HANDLE(Handle);
+
KeEnterCriticalRegion();
ExAcquireHandleTableLockExclusive(HandleTable);
-
+
HandleTableEntry = ExpLookupHandleTableEntry(HandleTable,
- Handle);
-
+ ExHandle);
+
if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry))
{
/* free and automatically unlock the handle. However we don't need to pulse
the contention event since other locks on this entry will fail */
ExpFreeHandleTableEntry(HandleTable,
HandleTableEntry,
- Handle);
+ ExHandle);
Ret = TRUE;
}
-
+
ExReleaseHandleTableLock(HandleTable);
KeLeaveCriticalRegion();
-
+
return Ret;
}
VOID
ExDestroyHandleByEntry(IN PHANDLE_TABLE HandleTable,
IN PHANDLE_TABLE_ENTRY Entry,
- IN LONG Handle)
+ IN HANDLE Handle)
{
PAGED_CODE();
ASSERT(HandleTable);
ASSERT(Entry);
-
+
/* This routine requires the entry to be locked */
ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED);
-
- DPRINT("DestroyHandleByEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry);
+
+ DPRINT("DestroyHandleByEntry HT:0x%p Entry:0x%p\n", HandleTable, Entry);
KeEnterCriticalRegion();
ExAcquireHandleTableLockExclusive(HandleTable);
the contention event since other locks on this entry will fail */
ExpFreeHandleTableEntry(HandleTable,
Entry,
- Handle);
+ HANDLE_TO_EX_HANDLE(Handle));
ExReleaseHandleTableLock(HandleTable);
KeLeaveCriticalRegion();
PHANDLE_TABLE_ENTRY
ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,
- IN LONG Handle)
+ IN HANDLE Handle)
{
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
ASSERT(HandleTable);
-
+
HandleTableEntry = ExpLookupHandleTableEntry(HandleTable,
- Handle);
+ HANDLE_TO_EX_HANDLE(Handle));
if (HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry))
{
- DPRINT("ExMapHandleToPointer HT:0x%x Entry:0x%x locked\n", HandleTable, HandleTableEntry);
+ DPRINT("ExMapHandleToPointer HT:0x%p Entry:0x%p locked\n", HandleTable, HandleTableEntry);
return HandleTableEntry;
}
-
+
return NULL;
}
BOOLEAN
ExChangeHandle(IN PHANDLE_TABLE HandleTable,
- IN LONG Handle,
+ IN HANDLE Handle,
IN PEX_CHANGE_HANDLE_CALLBACK ChangeHandleCallback,
IN PVOID Context)
{
ASSERT(ChangeHandleCallback);
KeEnterCriticalRegion();
-
+
HandleTableEntry = ExpLookupHandleTableEntry(HandleTable,
- Handle);
+ HANDLE_TO_EX_HANDLE(Handle));
if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry))
{
ExUnlockHandleTableEntry(HandleTable,
HandleTableEntry);
}
-
+
KeLeaveCriticalRegion();
return Ret;