754 lines
20 KiB
C
754 lines
20 KiB
C
/*
|
|
* Process Hacker -
|
|
* object manager
|
|
*
|
|
* Copyright (C) 2009-2016 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <phbase.h>
|
|
#include <phintrnl.h>
|
|
#include <workqueue.h>
|
|
#include <refp.h>
|
|
|
|
PPH_OBJECT_TYPE PhObjectTypeObject = NULL;
|
|
SLIST_HEADER PhObjectDeferDeleteListHead;
|
|
PH_FREE_LIST PhObjectSmallFreeList;
|
|
PPH_OBJECT_TYPE PhAllocType = NULL;
|
|
|
|
ULONG PhObjectTypeCount = 0;
|
|
PPH_OBJECT_TYPE PhObjectTypeTable[PH_OBJECT_TYPE_TABLE_SIZE];
|
|
|
|
static ULONG PhpAutoPoolTlsIndex;
|
|
|
|
#ifdef DEBUG
|
|
LIST_ENTRY PhDbgObjectListHead;
|
|
PH_QUEUED_LOCK PhDbgObjectListLock = PH_QUEUED_LOCK_INIT;
|
|
PPH_CREATE_OBJECT_HOOK PhDbgCreateObjectHook = NULL;
|
|
#endif
|
|
|
|
#define REF_STAT_UP(Name) PHLIB_INC_STATISTIC(Name)
|
|
|
|
/**
|
|
* Initializes the object manager module.
|
|
*/
|
|
NTSTATUS PhRefInitialization(
|
|
VOID
|
|
)
|
|
{
|
|
PH_OBJECT_TYPE dummyObjectType;
|
|
|
|
#ifdef DEBUG
|
|
InitializeListHead(&PhDbgObjectListHead);
|
|
#endif
|
|
|
|
RtlInitializeSListHead(&PhObjectDeferDeleteListHead);
|
|
PhInitializeFreeList(
|
|
&PhObjectSmallFreeList,
|
|
PhAddObjectHeaderSize(PH_OBJECT_SMALL_OBJECT_SIZE),
|
|
PH_OBJECT_SMALL_OBJECT_COUNT
|
|
);
|
|
|
|
// Create the fundamental object type.
|
|
|
|
memset(&dummyObjectType, 0, sizeof(PH_OBJECT_TYPE));
|
|
PhObjectTypeObject = &dummyObjectType; // PhCreateObject expects an object type.
|
|
PhObjectTypeTable[0] = &dummyObjectType; // PhCreateObject also expects PhObjectTypeTable[0] to be filled in.
|
|
PhObjectTypeObject = PhCreateObjectType(L"Type", 0, NULL);
|
|
|
|
// Now that the fundamental object type exists, fix it up.
|
|
PhObjectToObjectHeader(PhObjectTypeObject)->TypeIndex = PhObjectTypeObject->TypeIndex;
|
|
PhObjectTypeObject->NumberOfObjects = 1;
|
|
|
|
// Create the allocated memory object type.
|
|
PhAllocType = PhCreateObjectType(L"Alloc", 0, NULL);
|
|
|
|
// Reserve a slot for the auto pool.
|
|
PhpAutoPoolTlsIndex = TlsAlloc();
|
|
|
|
if (PhpAutoPoolTlsIndex == TLS_OUT_OF_INDEXES)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Allocates a object.
|
|
*
|
|
* \param ObjectSize The size of the object.
|
|
* \param ObjectType The type of the object.
|
|
*
|
|
* \return A pointer to the newly allocated object.
|
|
*/
|
|
_May_raise_ PVOID PhCreateObject(
|
|
_In_ SIZE_T ObjectSize,
|
|
_In_ PPH_OBJECT_TYPE ObjectType
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
|
|
// Allocate storage for the object. Note that this includes the object header followed by the
|
|
// object body.
|
|
objectHeader = PhpAllocateObject(ObjectType, ObjectSize);
|
|
|
|
// Object type statistics.
|
|
_InterlockedIncrement((PLONG)&ObjectType->NumberOfObjects);
|
|
|
|
// Initialize the object header.
|
|
objectHeader->RefCount = 1;
|
|
objectHeader->TypeIndex = ObjectType->TypeIndex;
|
|
// objectHeader->Flags is set by PhpAllocateObject.
|
|
|
|
REF_STAT_UP(RefObjectsCreated);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
USHORT capturedFrames;
|
|
|
|
capturedFrames = RtlCaptureStackBackTrace(1, 16, objectHeader->StackBackTrace, NULL);
|
|
memset(
|
|
&objectHeader->StackBackTrace[capturedFrames],
|
|
0,
|
|
sizeof(objectHeader->StackBackTrace) - capturedFrames * sizeof(PVOID)
|
|
);
|
|
}
|
|
|
|
PhAcquireQueuedLockExclusive(&PhDbgObjectListLock);
|
|
InsertTailList(&PhDbgObjectListHead, &objectHeader->ObjectListEntry);
|
|
PhReleaseQueuedLockExclusive(&PhDbgObjectListLock);
|
|
|
|
{
|
|
PPH_CREATE_OBJECT_HOOK dbgCreateObjectHook;
|
|
|
|
dbgCreateObjectHook = PhDbgCreateObjectHook;
|
|
|
|
if (dbgCreateObjectHook)
|
|
{
|
|
dbgCreateObjectHook(
|
|
PhObjectHeaderToObject(objectHeader),
|
|
ObjectSize,
|
|
0,
|
|
ObjectType
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return PhObjectHeaderToObject(objectHeader);
|
|
}
|
|
|
|
/**
|
|
* References the specified object.
|
|
*
|
|
* \param Object A pointer to the object to reference.
|
|
*
|
|
* \return The object.
|
|
*/
|
|
PVOID PhReferenceObject(
|
|
_In_ PVOID Object
|
|
)
|
|
{
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
|
|
objectHeader = PhObjectToObjectHeader(Object);
|
|
// Increment the reference count.
|
|
_InterlockedIncrement(&objectHeader->RefCount);
|
|
|
|
return Object;
|
|
}
|
|
|
|
/**
|
|
* References the specified object.
|
|
*
|
|
* \param Object A pointer to the object to reference.
|
|
* \param RefCount The number of references to add.
|
|
*
|
|
* \return The object.
|
|
*/
|
|
_May_raise_ PVOID PhReferenceObjectEx(
|
|
_In_ PVOID Object,
|
|
_In_ LONG RefCount
|
|
)
|
|
{
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
LONG oldRefCount;
|
|
|
|
assert(!(RefCount < 0));
|
|
|
|
objectHeader = PhObjectToObjectHeader(Object);
|
|
// Increase the reference count.
|
|
oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, RefCount);
|
|
|
|
return Object;
|
|
}
|
|
|
|
/**
|
|
* Attempts to reference an object and fails if it is being destroyed.
|
|
*
|
|
* \param Object The object to reference if it is not being deleted.
|
|
*
|
|
* \return The object itself if the object was referenced, NULL if it was being deleted and was not
|
|
* referenced.
|
|
*
|
|
* \remarks This function is useful if a reference to an object is held, protected by a mutex, and
|
|
* the delete procedure of the object's type attempts to acquire the mutex. If this function is
|
|
* called while the mutex is owned, you can avoid referencing an object that is being destroyed.
|
|
*/
|
|
PVOID PhReferenceObjectSafe(
|
|
_In_ PVOID Object
|
|
)
|
|
{
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
|
|
objectHeader = PhObjectToObjectHeader(Object);
|
|
|
|
// Increase the reference count only if it positive already (atomically).
|
|
if (PhpInterlockedIncrementSafe(&objectHeader->RefCount))
|
|
return Object;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Dereferences the specified object.
|
|
* The object will be freed if its reference count reaches 0.
|
|
*
|
|
* \param Object A pointer to the object to dereference.
|
|
*/
|
|
VOID PhDereferenceObject(
|
|
_In_ PVOID Object
|
|
)
|
|
{
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
LONG newRefCount;
|
|
|
|
objectHeader = PhObjectToObjectHeader(Object);
|
|
// Decrement the reference count.
|
|
newRefCount = _InterlockedDecrement(&objectHeader->RefCount);
|
|
ASSUME_ASSERT(newRefCount >= 0);
|
|
|
|
// Free the object if it has 0 references.
|
|
if (newRefCount == 0)
|
|
{
|
|
PhpFreeObject(objectHeader);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dereferences the specified object.
|
|
* The object will be freed in a worker thread if its reference count reaches 0.
|
|
*
|
|
* \param Object A pointer to the object to dereference.
|
|
*/
|
|
VOID PhDereferenceObjectDeferDelete(
|
|
_In_ PVOID Object
|
|
)
|
|
{
|
|
PhDereferenceObjectEx(Object, 1, TRUE);
|
|
}
|
|
|
|
/**
|
|
* Dereferences the specified object.
|
|
* The object will be freed if its reference count reaches 0.
|
|
*
|
|
* \param Object A pointer to the object to dereference.
|
|
* \param RefCount The number of references to remove.
|
|
* \param DeferDelete Whether to defer deletion of the object.
|
|
*/
|
|
_May_raise_ VOID PhDereferenceObjectEx(
|
|
_In_ PVOID Object,
|
|
_In_ LONG RefCount,
|
|
_In_ BOOLEAN DeferDelete
|
|
)
|
|
{
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
LONG oldRefCount;
|
|
LONG newRefCount;
|
|
|
|
assert(!(RefCount < 0));
|
|
|
|
objectHeader = PhObjectToObjectHeader(Object);
|
|
|
|
// Decrease the reference count.
|
|
oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount);
|
|
newRefCount = oldRefCount - RefCount;
|
|
|
|
// Free the object if it has 0 references.
|
|
if (newRefCount == 0)
|
|
{
|
|
if (DeferDelete)
|
|
{
|
|
PhpDeferDeleteObject(objectHeader);
|
|
}
|
|
else
|
|
{
|
|
// Free the object.
|
|
PhpFreeObject(objectHeader);
|
|
}
|
|
}
|
|
else if (newRefCount < 0)
|
|
{
|
|
PhRaiseStatus(STATUS_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets an object's type.
|
|
*
|
|
* \param Object A pointer to an object.
|
|
*
|
|
* \return A pointer to a type object.
|
|
*/
|
|
PPH_OBJECT_TYPE PhGetObjectType(
|
|
_In_ PVOID Object
|
|
)
|
|
{
|
|
return PhObjectTypeTable[PhObjectToObjectHeader(Object)->TypeIndex];
|
|
}
|
|
|
|
/**
|
|
* Creates an object type.
|
|
*
|
|
* \param Name The name of the type.
|
|
* \param Flags A combination of flags affecting the behaviour of the object type.
|
|
* \param DeleteProcedure A callback function that is executed when an object of this type is about
|
|
* to be freed (i.e. when its reference count is 0).
|
|
*
|
|
* \return A pointer to the newly created object type.
|
|
*
|
|
* \remarks Do not reference or dereference the object type once it is created.
|
|
*/
|
|
PPH_OBJECT_TYPE PhCreateObjectType(
|
|
_In_ PWSTR Name,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure
|
|
)
|
|
{
|
|
return PhCreateObjectTypeEx(
|
|
Name,
|
|
Flags,
|
|
DeleteProcedure,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates an object type.
|
|
*
|
|
* \param Name The name of the type.
|
|
* \param Flags A combination of flags affecting the behaviour of the object type.
|
|
* \param DeleteProcedure A callback function that is executed when an object of this type is about
|
|
* to be freed (i.e. when its reference count is 0).
|
|
* \param Parameters A structure containing additional parameters for the object type.
|
|
*
|
|
* \return A pointer to the newly created object type.
|
|
*
|
|
* \remarks Do not reference or dereference the object type once it is created.
|
|
*/
|
|
PPH_OBJECT_TYPE PhCreateObjectTypeEx(
|
|
_In_ PWSTR Name,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure,
|
|
_In_opt_ PPH_OBJECT_TYPE_PARAMETERS Parameters
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PPH_OBJECT_TYPE objectType;
|
|
|
|
// Check the flags.
|
|
if ((Flags & PH_OBJECT_TYPE_VALID_FLAGS) != Flags) /* Valid flag mask */
|
|
PhRaiseStatus(STATUS_INVALID_PARAMETER_3);
|
|
if ((Flags & PH_OBJECT_TYPE_USE_FREE_LIST) && !Parameters)
|
|
PhRaiseStatus(STATUS_INVALID_PARAMETER_MIX);
|
|
|
|
// Create the type object.
|
|
objectType = PhCreateObject(sizeof(PH_OBJECT_TYPE), PhObjectTypeObject);
|
|
|
|
// Initialize the type object.
|
|
objectType->Flags = (USHORT)Flags;
|
|
objectType->TypeIndex = (USHORT)_InterlockedIncrement(&PhObjectTypeCount) - 1;
|
|
objectType->NumberOfObjects = 0;
|
|
objectType->DeleteProcedure = DeleteProcedure;
|
|
objectType->Name = Name;
|
|
|
|
if (objectType->TypeIndex < PH_OBJECT_TYPE_TABLE_SIZE)
|
|
PhObjectTypeTable[objectType->TypeIndex] = objectType;
|
|
else
|
|
PhRaiseStatus(STATUS_UNSUCCESSFUL);
|
|
|
|
if (Parameters)
|
|
{
|
|
if (Flags & PH_OBJECT_TYPE_USE_FREE_LIST)
|
|
{
|
|
PhInitializeFreeList(
|
|
&objectType->FreeList,
|
|
PhAddObjectHeaderSize(Parameters->FreeListSize),
|
|
Parameters->FreeListCount
|
|
);
|
|
}
|
|
}
|
|
|
|
return objectType;
|
|
}
|
|
|
|
/**
|
|
* Gets information about an object type.
|
|
*
|
|
* \param ObjectType A pointer to an object type.
|
|
* \param Information A variable which receives information about the object type.
|
|
*/
|
|
VOID PhGetObjectTypeInformation(
|
|
_In_ PPH_OBJECT_TYPE ObjectType,
|
|
_Out_ PPH_OBJECT_TYPE_INFORMATION Information
|
|
)
|
|
{
|
|
Information->Name = ObjectType->Name;
|
|
Information->NumberOfObjects = ObjectType->NumberOfObjects;
|
|
Information->Flags = ObjectType->Flags;
|
|
Information->TypeIndex = ObjectType->TypeIndex;
|
|
}
|
|
|
|
/**
|
|
* Allocates storage for an object.
|
|
*
|
|
* \param ObjectType The type of the object.
|
|
* \param ObjectSize The size of the object, excluding the header.
|
|
*/
|
|
PPH_OBJECT_HEADER PhpAllocateObject(
|
|
_In_ PPH_OBJECT_TYPE ObjectType,
|
|
_In_ SIZE_T ObjectSize
|
|
)
|
|
{
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
|
|
if (ObjectType->Flags & PH_OBJECT_TYPE_USE_FREE_LIST)
|
|
{
|
|
assert(ObjectType->FreeList.Size == PhAddObjectHeaderSize(ObjectSize));
|
|
|
|
objectHeader = PhAllocateFromFreeList(&ObjectType->FreeList);
|
|
objectHeader->Flags = PH_OBJECT_FROM_TYPE_FREE_LIST;
|
|
REF_STAT_UP(RefObjectsAllocatedFromTypeFreeList);
|
|
}
|
|
else if (ObjectSize <= PH_OBJECT_SMALL_OBJECT_SIZE)
|
|
{
|
|
objectHeader = PhAllocateFromFreeList(&PhObjectSmallFreeList);
|
|
objectHeader->Flags = PH_OBJECT_FROM_SMALL_FREE_LIST;
|
|
REF_STAT_UP(RefObjectsAllocatedFromSmallFreeList);
|
|
}
|
|
else
|
|
{
|
|
objectHeader = PhAllocate(PhAddObjectHeaderSize(ObjectSize));
|
|
objectHeader->Flags = 0;
|
|
REF_STAT_UP(RefObjectsAllocated);
|
|
}
|
|
|
|
return objectHeader;
|
|
}
|
|
|
|
/**
|
|
* Calls the delete procedure for an object and frees its allocated storage.
|
|
*
|
|
* \param ObjectHeader A pointer to the object header of an allocated object.
|
|
*/
|
|
VOID PhpFreeObject(
|
|
_In_ PPH_OBJECT_HEADER ObjectHeader
|
|
)
|
|
{
|
|
PPH_OBJECT_TYPE objectType;
|
|
|
|
objectType = PhObjectTypeTable[ObjectHeader->TypeIndex];
|
|
|
|
// Object type statistics.
|
|
_InterlockedDecrement(&objectType->NumberOfObjects);
|
|
|
|
#ifdef DEBUG
|
|
PhAcquireQueuedLockExclusive(&PhDbgObjectListLock);
|
|
RemoveEntryList(&ObjectHeader->ObjectListEntry);
|
|
PhReleaseQueuedLockExclusive(&PhDbgObjectListLock);
|
|
#endif
|
|
|
|
REF_STAT_UP(RefObjectsDestroyed);
|
|
|
|
// Call the delete procedure if we have one.
|
|
if (objectType->DeleteProcedure)
|
|
{
|
|
objectType->DeleteProcedure(PhObjectHeaderToObject(ObjectHeader), 0);
|
|
}
|
|
|
|
if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST)
|
|
{
|
|
PhFreeToFreeList(&objectType->FreeList, ObjectHeader);
|
|
REF_STAT_UP(RefObjectsFreedToTypeFreeList);
|
|
}
|
|
else if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST)
|
|
{
|
|
PhFreeToFreeList(&PhObjectSmallFreeList, ObjectHeader);
|
|
REF_STAT_UP(RefObjectsFreedToSmallFreeList);
|
|
}
|
|
else
|
|
{
|
|
PhFree(ObjectHeader);
|
|
REF_STAT_UP(RefObjectsFreed);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Queues an object for deletion.
|
|
*
|
|
* \param ObjectHeader A pointer to the object header of the object to delete.
|
|
*/
|
|
VOID PhpDeferDeleteObject(
|
|
_In_ PPH_OBJECT_HEADER ObjectHeader
|
|
)
|
|
{
|
|
PSLIST_ENTRY oldFirstEntry;
|
|
|
|
// Save TypeIndex and Flags since they get overwritten when we push the object onto the defer
|
|
// delete list.
|
|
ObjectHeader->DeferDelete = 1;
|
|
MemoryBarrier();
|
|
ObjectHeader->SavedTypeIndex = ObjectHeader->TypeIndex;
|
|
ObjectHeader->SavedFlags = ObjectHeader->Flags;
|
|
|
|
oldFirstEntry = RtlFirstEntrySList(&PhObjectDeferDeleteListHead);
|
|
RtlInterlockedPushEntrySList(&PhObjectDeferDeleteListHead, &ObjectHeader->DeferDeleteListEntry);
|
|
REF_STAT_UP(RefObjectsDeleteDeferred);
|
|
|
|
// Was the to-free list empty before? If so, we need to queue a work item.
|
|
if (!oldFirstEntry)
|
|
{
|
|
PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), PhpDeferDeleteObjectRoutine, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes and frees objects from the to-free list.
|
|
*/
|
|
NTSTATUS PhpDeferDeleteObjectRoutine(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
PSLIST_ENTRY listEntry;
|
|
PPH_OBJECT_HEADER objectHeader;
|
|
|
|
// Clear the list and obtain the first object to free.
|
|
listEntry = RtlInterlockedFlushSList(&PhObjectDeferDeleteListHead);
|
|
|
|
while (listEntry)
|
|
{
|
|
objectHeader = CONTAINING_RECORD(listEntry, PH_OBJECT_HEADER, DeferDeleteListEntry);
|
|
listEntry = listEntry->Next;
|
|
|
|
// Restore TypeIndex and Flags.
|
|
objectHeader->TypeIndex = (USHORT)objectHeader->SavedTypeIndex;
|
|
objectHeader->Flags = (UCHAR)objectHeader->SavedFlags;
|
|
|
|
PhpFreeObject(objectHeader);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Creates a reference-counted memory block.
|
|
*
|
|
* \param Size The number of bytes to allocate.
|
|
*
|
|
* \return A pointer to the memory block.
|
|
*/
|
|
PVOID PhCreateAlloc(
|
|
_In_ SIZE_T Size
|
|
)
|
|
{
|
|
return PhCreateObject(Size, PhAllocType);
|
|
}
|
|
|
|
/**
|
|
* Gets the current auto-dereference pool for the current thread.
|
|
*/
|
|
FORCEINLINE PPH_AUTO_POOL PhpGetCurrentAutoPool(
|
|
VOID
|
|
)
|
|
{
|
|
return (PPH_AUTO_POOL)TlsGetValue(PhpAutoPoolTlsIndex);
|
|
}
|
|
|
|
/**
|
|
* Sets the current auto-dereference pool for the current thread.
|
|
*/
|
|
_May_raise_ FORCEINLINE VOID PhpSetCurrentAutoPool(
|
|
_In_ PPH_AUTO_POOL AutoPool
|
|
)
|
|
{
|
|
if (!TlsSetValue(PhpAutoPoolTlsIndex, AutoPool))
|
|
PhRaiseStatus(STATUS_UNSUCCESSFUL);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
PPHP_BASE_THREAD_DBG dbg;
|
|
|
|
dbg = (PPHP_BASE_THREAD_DBG)TlsGetValue(PhDbgThreadDbgTlsIndex);
|
|
|
|
if (dbg)
|
|
{
|
|
dbg->CurrentAutoPool = AutoPool;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Initializes an auto-dereference pool and sets it as the current pool for the current thread. You
|
|
* must call PhDeleteAutoPool() before storage for the auto-dereference pool is freed.
|
|
*
|
|
* \remarks Always store auto-dereference pools in local variables, and do not share the pool with
|
|
* any other functions.
|
|
*/
|
|
VOID PhInitializeAutoPool(
|
|
_Out_ PPH_AUTO_POOL AutoPool
|
|
)
|
|
{
|
|
AutoPool->StaticCount = 0;
|
|
AutoPool->DynamicCount = 0;
|
|
AutoPool->DynamicAllocated = 0;
|
|
AutoPool->DynamicObjects = NULL;
|
|
|
|
// Add the pool to the stack.
|
|
AutoPool->NextPool = PhpGetCurrentAutoPool();
|
|
PhpSetCurrentAutoPool(AutoPool);
|
|
|
|
REF_STAT_UP(RefAutoPoolsCreated);
|
|
}
|
|
|
|
/**
|
|
* Deletes an auto-dereference pool. The function will dereference any objects currently in the
|
|
* pool. If a pool other than the current pool is passed to the function, an exception is raised.
|
|
*
|
|
* \param AutoPool The auto-dereference pool to delete.
|
|
*/
|
|
_May_raise_ VOID PhDeleteAutoPool(
|
|
_Inout_ PPH_AUTO_POOL AutoPool
|
|
)
|
|
{
|
|
PhDrainAutoPool(AutoPool);
|
|
|
|
if (PhpGetCurrentAutoPool() != AutoPool)
|
|
PhRaiseStatus(STATUS_UNSUCCESSFUL);
|
|
|
|
// Remove the pool from the stack.
|
|
PhpSetCurrentAutoPool(AutoPool->NextPool);
|
|
|
|
// Free the dynamic array if it hasn't been freed yet.
|
|
if (AutoPool->DynamicObjects)
|
|
PhFree(AutoPool->DynamicObjects);
|
|
|
|
REF_STAT_UP(RefAutoPoolsDestroyed);
|
|
}
|
|
|
|
/**
|
|
* Dereferences and removes all objects in an auto-release pool.
|
|
*
|
|
* \param AutoPool The auto-release pool to drain.
|
|
*/
|
|
VOID PhDrainAutoPool(
|
|
_In_ PPH_AUTO_POOL AutoPool
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < AutoPool->StaticCount; i++)
|
|
PhDereferenceObject(AutoPool->StaticObjects[i]);
|
|
|
|
AutoPool->StaticCount = 0;
|
|
|
|
if (AutoPool->DynamicObjects)
|
|
{
|
|
for (i = 0; i < AutoPool->DynamicCount; i++)
|
|
{
|
|
PhDereferenceObject(AutoPool->DynamicObjects[i]);
|
|
}
|
|
|
|
AutoPool->DynamicCount = 0;
|
|
|
|
if (AutoPool->DynamicAllocated > PH_AUTO_POOL_DYNAMIC_BIG_SIZE)
|
|
{
|
|
AutoPool->DynamicAllocated = 0;
|
|
PhFree(AutoPool->DynamicObjects);
|
|
AutoPool->DynamicObjects = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an object to the current auto-dereference pool for the current thread. If the current thread
|
|
* does not have an auto-dereference pool, the function raises an exception.
|
|
*
|
|
* \param Object A pointer to an object. The object will be dereferenced when the current
|
|
* auto-dereference pool is drained or freed.
|
|
*/
|
|
_May_raise_ PVOID PhAutoDereferenceObject(
|
|
_In_opt_ PVOID Object
|
|
)
|
|
{
|
|
PPH_AUTO_POOL autoPool = PhpGetCurrentAutoPool();
|
|
|
|
#ifdef DEBUG
|
|
// If we don't have an auto-dereference pool, we don't want to leak the object (unlike what
|
|
// Apple does with NSAutoreleasePool).
|
|
if (!autoPool)
|
|
PhRaiseStatus(STATUS_UNSUCCESSFUL);
|
|
#endif
|
|
|
|
if (!Object)
|
|
return NULL;
|
|
|
|
// See if we can use the static array.
|
|
if (autoPool->StaticCount < PH_AUTO_POOL_STATIC_SIZE)
|
|
{
|
|
autoPool->StaticObjects[autoPool->StaticCount++] = Object;
|
|
return Object;
|
|
}
|
|
|
|
// Use the dynamic array.
|
|
|
|
// Allocate the array if we haven't already.
|
|
if (!autoPool->DynamicObjects)
|
|
{
|
|
autoPool->DynamicAllocated = 64;
|
|
autoPool->DynamicObjects = PhAllocate(
|
|
sizeof(PVOID) * autoPool->DynamicAllocated
|
|
);
|
|
REF_STAT_UP(RefAutoPoolsDynamicAllocated);
|
|
}
|
|
|
|
// See if we need to resize the array.
|
|
if (autoPool->DynamicCount == autoPool->DynamicAllocated)
|
|
{
|
|
autoPool->DynamicAllocated *= 2;
|
|
autoPool->DynamicObjects = PhReAllocate(
|
|
autoPool->DynamicObjects,
|
|
sizeof(PVOID) * autoPool->DynamicAllocated
|
|
);
|
|
REF_STAT_UP(RefAutoPoolsDynamicResized);
|
|
}
|
|
|
|
autoPool->DynamicObjects[autoPool->DynamicCount++] = Object;
|
|
|
|
return Object;
|
|
}
|