[+] AuMemory::Heap adapters for third party heap allocators

[+] AuMemory::HeapAdapterInterface to describe aforementioned heap allocators of a very limited API
[+] AuMemory::HeapAdapter[Unique,Shared,]
[+] HeapWin32Adapter to convert HANDLE hHeaps of win32s CreateHeap (RtlCreateHeap?) into AuMemory::Heaps
This commit is contained in:
Reece Wilson 2024-07-19 09:03:13 +01:00
parent a995c37e81
commit 8be1afe570
7 changed files with 590 additions and 6 deletions

View File

@ -17,7 +17,7 @@ namespace Aurora::Memory
struct CppHeapWrapper;
/**
* Note: The following public aliases for heap or global process heap based allocations exist:
* Note: The following public global aliases exists for heap and/or global process heap based allocations:
*
* AuHUPOf_t<T> AuNewClassArrayUnique([pHeap, ]uElements, ...)
* AuSPtr<T> AuNewClassArray([pHeap, ]uElements, ...)
@ -168,6 +168,35 @@ namespace Aurora::Memory
}
};
struct HeapAdapterHandle
{
void *pReserved[2];
};
struct HeapAdapterInterface
{
HeapAdapterHandle handle;
// Required:
void *(* fAllocate)(HeapAdapterHandle *pHandle,
AuUInt uLength,
AuUInt uAlignment) = nullptr;
// Required:
void (* fFree)(HeapAdapterHandle *pHandle,
void *pPointer) = nullptr;
// Optional:
AuUInt (* fGetBlockSize)(HeapAdapterHandle *pHandle,
void *pPointer) = nullptr;
// Optional:
void (* fHeapDestroy)(HeapAdapterHandle *pHandle) = nullptr;
//
bool bHasAlignmentAwareness {};
};
/**
* Returns a heap interface backed by the default allocator
*/
@ -189,29 +218,46 @@ namespace Aurora::Memory
* @return a heap backed by uLength bytes of virtual memory
* @warning the SOO variant cannot guarantee release-on-last-free and will panic if uLength cannot be allocated. Use AllocHeap[Shared/Unique/New](uLength) instead.
*/
AUKN_SHARED_SOO2_NCM(AllocHeap, Heap, kHeapSize, ((AuUInt, uLength)), AuUInt uLength);
AUKN_SHARED_SOO2_NCM(AllocHeap, Heap, kHeapSize, ((AuUInt, uLength)),
AuUInt uLength);
/**
* @warning the SOO variant cannot guarantee release-on-last-free and will panic if an invalid memory handle is provided.
*/
AUKN_SHARED_SOO2_NCM(RequestHeapOfRegion, Heap, kHeapSize, ((const MemoryViewWrite &, memory)), const MemoryViewWrite &memory);
AUKN_SHARED_SOO2_NCM(RequestHeapOfRegion, Heap, kHeapSize, ((const MemoryViewWrite &, memory)),
const MemoryViewWrite &memory);
/**
* @warning the SOO variant cannot guarantee release-on-last-free and will panic if an invalid memory handle is provided.
*/
AUKN_SHARED_SOO2_NCM(RequestHeapOfSharedRegion, Heap, kHeapSize, ((const AuSPtr<MemoryViewWrite> &, memory)), const AuSPtr<MemoryViewWrite> &pMemory);
AUKN_SHARED_SOO2_NCM(RequestHeapOfSharedRegion, Heap, kHeapSize, ((const AuSPtr<MemoryViewWrite> &, memory)),
const AuSPtr<MemoryViewWrite> &pMemory);
/**
* Proxies an existing heap with encapsulated statistics.
* This is intended for debugging purposes when accurate heap stats of a heap-subset are desired.
* @warning this heap cannot guarantee release-on-last-free
*/
AUKN_SHARED_SOO2_NCM(HeapProxy, Heap, kHeap2Size, ((const AuSPtr<Heap> &, pHead)), const AuSPtr<Heap> &pHead);
AUKN_SHARED_SOO2_NCM(HeapProxy, Heap, kHeap2Size, ((const AuSPtr<Heap> &, pHead)),
const AuSPtr<Heap> &pHead);
/**
* Proxies an existing heap with encapsulated statistics and leak detector
* This is intended for debugging purposes when accurate heap stats of a heap-subset are desired.
* @warning this heap cannot guarantee release-on-last-free
*/
AUKN_SHARED_SOO2_NCM(HeapProxyEx, Heap, kHeap2Size, ((const AuSPtr<Heap> &,pHead), (LeakFinderAlloc_f, pfAlloc), (LeakFinderFree_f, pfFree)), const AuSPtr<Heap> &pHead, LeakFinderAlloc_f pfAlloc, LeakFinderFree_f pfFree);
AUKN_SHARED_SOO2_NCM(HeapProxyEx, Heap, kHeap2Size, ((const AuSPtr<Heap> &, pHead), (LeakFinderAlloc_f, pfAlloc), (LeakFinderFree_f, pfFree)),
const AuSPtr<Heap> &pHead,
LeakFinderAlloc_f pfAlloc,
LeakFinderFree_f pfFree);
/**
* Proxies an existing heap allocator library of a malloc and free; bonus points for aligned malloc, get allocation size, and destroy
*/
AUKN_SHARED_SOO2_NCM(HeapAdapter, Heap, kHeap2Size, ((const HeapAdapterInterface &, adapterInterface)),
const HeapAdapterInterface &adapterInterface);
#if defined(AURORA_IS_MODERNNT_DERIVED)
AUKN_SHARED_SOO2_NCM(HeapWin32Adapter, Heap, kHeap2Size, ((void *, hHeap)), void *hHeap);
#endif
}

View File

@ -0,0 +1,83 @@
/***
Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHeapAdapter.NT.cpp
File: Heap.hpp
Date: 2024-7-17
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuHeap.hpp"
#include "AuHeapAdapter.hpp"
namespace Aurora::Memory
{
static void *Win32HeapAllocate(HeapAdapterHandle *pHandle,
AuUInt uLength,
AuUInt uAlignment)
{
auto hHandle = (HANDLE)pHandle->pReserved[0];
return HeapAlloc(hHandle, 0, uLength);
}
static void Win32HeapFree(HeapAdapterHandle *pHandle,
void *pPointer)
{
auto hHandle = (HANDLE)pHandle->pReserved[0];
HeapFree(hHandle, 0, pPointer);
}
struct Win32HeapAllocator : ImplHeapAdapter
{
Win32HeapAllocator()
{
}
Win32HeapAllocator(void *hHeap)
{
if (!this->Init(hHeap))
{
AU_THROW_CONST_STRING("Invalid heap adapter configuration");
}
}
bool Init(void *hHeap)
{
if (!hHeap)
{
return false;
}
HeapAdapterInterface heap;
heap.handle.pReserved[0] = hHeap;
heap.fFree = Win32HeapFree;
heap.fAllocate = Win32HeapAllocate;
return ImplHeapAdapter::Init(heap);
}
};
AUKN_SYM Heap *HeapWin32AdapterNew(void *hHeap)
{
auto pHeap = _new Win32HeapAllocator();
if (!pHeap)
{
return nullptr;
}
if (!pHeap->Init(hHeap))
{
delete pHeap;
return nullptr;
}
return pHeap;
}
AUKN_SYM void HeapWin32AdapterRelease(Heap *pHeap)
{
static_cast<Win32HeapAllocator *>(pHeap)->RequestTermination();
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, HeapWin32Adapter, Win32HeapAllocator, (void *, hHeap))
}

View File

@ -0,0 +1,9 @@
/***
Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHeapAdapter.NT.hpp
File: Heap.hpp
Date: 2024-7-17
Author: Reece
***/
#pragma once

View File

@ -0,0 +1,280 @@
/***
Copyright (C) 2021-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHeapAdapter.cpp
File: Heap.hpp
Date: 2024-7-17
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuHeap.hpp"
#include "AuHeapAdapter.hpp"
#include "AuHeapInternal.hpp"
#include "AuHeapDeletable.hpp"
#include "AuHeapAlignmentInterface.hpp"
namespace Aurora::Memory
{
ImplHeapAdapter::ImplHeapAdapter(const HeapAdapterInterface &heapInterface)
{
if (!this->Init(heapInterface))
{
AU_THROW_CONST_STRING("Invalid heap adapter configuration");
}
}
ImplHeapAdapter::ImplHeapAdapter()
{
}
ImplHeapAdapter::~ImplHeapAdapter()
{
if (this->adapter.fHeapDestroy)
{
this->adapter.fHeapDestroy(&this->adapter.handle);
}
}
bool ImplHeapAdapter::Init(const HeapAdapterInterface &heapInterface)
{
this->adapter = heapInterface;
if (!this->adapter.fFree ||
!this->adapter.fAllocate)
{
return false;
}
if (!this->adapter.fGetBlockSize ||
!this->adapter.bHasAlignmentAwareness)
{
if (!MakeHeapInterfaceOfStricterAlignment(&this->adapter))
{
return false;
}
}
return true;
}
HeapStats &ImplHeapAdapter::GetStats()
{
return this->stats;
}
Types::size_t ImplHeapAdapter::GetChunkSize(const void *head)
{
if (!head)
{
return 0;
}
return this->adapter.fGetBlockSize(&this->adapter.handle, (void *)head);
}
AuSPtr<Heap> ImplHeapAdapter::AllocateDivision(AuUInt32 heap, AuUInt32 alignment)
{
return AllocateDivisionGlobal(this, heap, alignment);
}
void *ImplHeapAdapter::_FAlloc(Types::size_t uLength)
{
return _FAlloc(uLength, alignof(void *));
}
void *ImplHeapAdapter::_FAlloc(Types::size_t uLength, Types::size_t uAlign)
{
if (!uLength)
{
return nullptr;
}
auto pRet = this->adapter.fAllocate(&this->adapter.handle, uLength, uAlign);
if (!pRet)
{
return nullptr;
}
auto uLengthCurrent = this->GetChunkSize(pRet);
AuAtomicAdd(&this->stats.uBytesLiveCounter, uLengthCurrent);
AuAtomicAdd(&this->stats.uBytesAllocatedLifetime, uLengthCurrent);
return pRet;
}
void *ImplHeapAdapter::_ZAlloc(Types::size_t uLength)
{
auto ptr = this->_FAlloc(uLength);
if (!ptr)
{
return nullptr;
}
AuMemset(ptr, 0, uLength);
return ptr;
}
void *ImplHeapAdapter::_ZAlloc(Types::size_t uLength, Types::size_t uAlign)
{
auto ptr = this->_FAlloc(uLength, uAlign);
if (!ptr)
{
return nullptr;
}
AuMemset(ptr, 0, uLength);
return ptr;
}
void *ImplHeapAdapter::_ZRealloc(void *pBuffer, Types::size_t uLength)
{
auto prevLength = this->GetChunkSize(pBuffer);
auto alloc = this->_ZAlloc(uLength);
if (!alloc)
{
return nullptr;
}
AuMemcpy(alloc, pBuffer, AuMin(prevLength, uLength));
this->_Free(pBuffer);
return alloc;
}
void *ImplHeapAdapter::_ZRealloc(void *pBuffer, Types::size_t uLength, Types::size_t uAlign)
{
auto prevLength = this->GetChunkSize(pBuffer);
auto alloc = this->_ZAlloc(uLength, uAlign);
if (!alloc)
{
return nullptr;
}
AuMemcpy(alloc, pBuffer, AuMin(prevLength, uLength));
this->_Free(pBuffer);
return alloc;
}
void *ImplHeapAdapter::_FRealloc(void *pBuffer, Types::size_t uLength)
{
auto prevLength = this->GetChunkSize(pBuffer);
auto alloc = this->_FAlloc(uLength);
if (!alloc)
{
return nullptr;
}
AuMemcpy(alloc, pBuffer, AuMin(prevLength, uLength));
this->_Free(pBuffer);
return alloc;
}
void *ImplHeapAdapter::_FRealloc(void *pBuffer, Types::size_t uLength, Types::size_t uAlign)
{
auto prevLength = this->GetChunkSize(pBuffer);
auto alloc = this->_FAlloc(uLength, uAlign);
if (!alloc)
{
return nullptr;
}
AuMemcpy(alloc, pBuffer, AuMin(prevLength, uLength));
this->_Free(pBuffer);
return alloc;
}
void ImplHeapAdapter::_Free(void *pBuffer)
{
if (pBuffer == nullptr)
{
return;
}
auto uLengthCurrent = this->GetChunkSize(pBuffer);
this->adapter.fFree(&this->adapter.handle, pBuffer);
AuAtomicAdd(&this->stats.uBytesFreeLifetime, uLengthCurrent);
AuAtomicSub(&this->stats.uBytesLiveCounter, uLengthCurrent);
TryRelease();
}
void ImplHeapAdapter::TryRelease()
{
if (!this->bIsDangling_)
{
return;
}
if (AuAtomicLoad(&this->stats.uBytesLiveCounter) == 0)
{
delete this;
}
}
void ImplHeapAdapter::RequestTermination()
{
if (AuAtomicLoad(&this->stats.uBytesLiveCounter))
{
SysPushErrorMemory("Heap life was less than its allocations, waiting for final free");
SysPushErrorMemory("Reporting using mayday!");
// Write a crash dump for later review, and do not panic.
// We just have a leak with no sign of corruption
Telemetry::Mayday();
this->bIsDangling_ = true;
}
else
{
delete this;
}
}
void ImplHeapAdapter::WalkHeap(bool(*fCallback)(void *, void *), void *pSecondArg)
{
}
AuSPtr<Heap> ImplHeapAdapter::GetSelfReference()
{
try
{
return AuSharedFromThis();
}
catch (...)
{
return {};
}
}
Heap *ImplHeapAdapter::GetSelfReferenceRaw()
{
return this;
}
AUKN_SYM Heap *HeapAdapterNew(const HeapAdapterInterface &adapterInterface)
{
auto pHeap = _new ImplHeapAdapter();
if (!pHeap)
{
return nullptr;
}
if (!pHeap->Init(adapterInterface))
{
delete pHeap;
return nullptr;
}
return pHeap;
}
AUKN_SYM void HeapAdapterRelease(Heap *pHeap)
{
static_cast<ImplHeapAdapter *>(pHeap)->RequestTermination();
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, HeapAdapter, ImplHeapAdapter, (const HeapAdapterInterface &, adapterInterface))
}

View File

@ -0,0 +1,60 @@
/***
Copyright (C) 2021-2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHeapAdapter.hpp
File: Heap.hpp
Date: 2024-7-17
Author: Reece
***/
#pragma once
#include "AuMemory.hpp"
#include "AuBaseHeap.hpp"
#include "o1heap.hpp"
namespace Aurora::Memory
{
struct ImplHeapAdapter :
Heap,
AuEnableSharedFromThis<ImplHeapAdapter>
{
virtual AuSPtr<Heap> AllocateDivision(AuUInt32 heap, AuUInt32 alignment) override;
ImplHeapAdapter();
ImplHeapAdapter(const HeapAdapterInterface &heapInterface);
virtual ~ImplHeapAdapter();
bool Init(const HeapAdapterInterface &heapInterface);
Types::size_t GetChunkSize(const void *head) override;
void *_FAlloc(Types::size_t uLength) override;
void *_FAlloc(Types::size_t uLength, Types::size_t uAlign) override;
void *_ZAlloc(Types::size_t uLength) override;
void *_ZAlloc(Types::size_t uLength, Types::size_t uAlign) override;
void *_ZRealloc(void *pBuffer, Types::size_t uLength) override;
void *_ZRealloc(void *pBuffer, Types::size_t uLength, Types::size_t uAlign) override;
void *_FRealloc(void *pBuffer, Types::size_t uLength) override;
void *_FRealloc(void *pBuffer, Types::size_t uLength, Types::size_t uAlign) override;
void _Free(void *pBuffer) override;
AuSPtr<Heap> GetSelfReference() override;
Heap *GetSelfReferenceRaw() override;
void TryRelease();
void RequestTermination();
void WalkHeap(bool(*fCallback)(void *, void *), void *pSecondArg) override;
HeapStats &GetStats() override;
private:
HeapStats stats;
HeapAdapterInterface adapter;
protected:
bool bOwnsMemory_ {};
bool bIsDangling_ {};
};
}

View File

@ -0,0 +1,92 @@
/***
Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHeapAlignmentInterface.cpp
File: Heap.hpp
Date: 2024-7-17
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuHeap.hpp"
#include "AuHeapAlignmentInterface.hpp"
namespace Aurora::Memory
{
struct OldHeap
{
HeapAdapterInterface interface;
};
static void *AlignedHeapAllocate(HeapAdapterHandle *pHandle,
AuUInt uLength,
AuUInt uAlignment)
{
auto pInterface = (OldHeap *)pHandle->pReserved[0];
uAlignment = AuNextPow2(uAlignment);
const auto kAlignment = AuMax(uAlignment, sizeof(void *) * 2);
auto pPtr = (AuUInt8 *)pInterface->interface.fAllocate(&pInterface->interface.handle, uLength + (kAlignment * 2), kAlignment);
if (!pPtr)
{
return {};
}
auto pRet = (AuUInt8 *)AuPageRoundUp(AuUInt(pPtr + kAlignment), uAlignment);
auto pSize = pRet - (sizeof(void *) * 2);
auto pDelta = pRet - (sizeof(void *) * 1);
*(AuUInt *)pSize = uLength;
*(AuUInt *)pDelta = AuUInt(pPtr);
return pRet;
}
static void AlignedHeapFree(HeapAdapterHandle *pHandle,
void *pPointer)
{
auto pInterface = (OldHeap *)pHandle->pReserved[0];
auto pRet = (AuUInt8 *)pPointer;
auto pDelta = pRet - (sizeof(void *) * 1);
pInterface->interface.fFree(&pInterface->interface.handle, *(void **)pDelta);
}
static AuUInt AlignedHeapGetBlockSize(HeapAdapterHandle *pHandle,
void *pPointer)
{
auto pRet = (AuUInt8 *)pPointer;
auto pSize = pRet - (sizeof(void *) * 2);
return *(AuUInt *)pSize;
}
static void AlignedHeapDestroy(HeapAdapterHandle *pHandle)
{
auto pInterface = (OldHeap *)pHandle->pReserved[0];
if (pInterface->interface.fHeapDestroy)
{
pInterface->interface.fHeapDestroy(&pInterface->interface.handle);
}
delete pInterface;
}
bool MakeHeapInterfaceOfStricterAlignment(HeapAdapterInterface *pInterface)
{
auto pNew = _new OldHeap();
if (!pNew)
{
return false;
}
pNew->interface = *pInterface;
pInterface->handle.pReserved[0] = pNew;
pInterface->fHeapDestroy = AlignedHeapDestroy;
pInterface->fGetBlockSize = AlignedHeapGetBlockSize;
pInterface->fFree = AlignedHeapFree;
pInterface->fAllocate = AlignedHeapAllocate;
return true;
}
}

View File

@ -0,0 +1,14 @@
/***
Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuHeapAlignmentInterface.hpp
File: Heap.hpp
Date: 2024-7-17
Author: Reece
***/
#pragma once
namespace Aurora::Memory
{
bool MakeHeapInterfaceOfStricterAlignment(HeapAdapterInterface *pInterface);
}