AuroraRuntime/Source/Memory/Heap.cpp
2021-06-27 22:25:29 +01:00

229 lines
6.2 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Heap.cpp
Date: 2021-6-12
Author: Reece
***/
#include <RuntimeInternal.hpp>
#include "Memory.hpp"
#include "Heap.hpp"
#include "mimalloc.h"
#include "o1heap.hpp"
namespace Aurora::Memory
{
class InternalHeap : public Heap
{
public:
InternalHeap() : base_(nullptr), mutex_(nullptr), heap_(nullptr), _count(0)
{
}
~InternalHeap();
bool Init(AuUInt length);
typedef struct FragmentHeader
{
void *next;
void *prev;
size_t size;
bool used;
} FragmentHeader;
static AuUInt GetHeapSize(const void *ptr)
{
return reinterpret_cast<const FragmentHeader *>(ptr)[-1].size;
}
Types::size_t GetChunkSize(const void *head) override;
void *_FAlloc(Types::size_t length) override;
void *_FAlloc(Types::size_t length, Types::size_t align) override;
void *_ZAlloc(Types::size_t length) override;
void *_ZAlloc(Types::size_t length, Types::size_t align) override;
void *_ZRealloc(void *buffer, Types::size_t length) override;
void *_ZRealloc(void *buffer, Types::size_t length, Types::size_t align) override;
void *_FRealloc(void *buffer, Types::size_t length) override;
void *_FRealloc(void *buffer, Types::size_t length, Types::size_t align) override;
void _Free(void *buffer) override;
void TryRelease();
void RequestTermination();
private:
Threading::Primitives::MutexUnique_t mutex_;
void *base_{};
O1HeapInstance *heap_{};
int _count{};
bool _isDangling{};
};
InternalHeap::~InternalHeap()
{
SysAssertDbgExp(_count == 0);
mutex_.reset();
if (base_)
{
o1HeapReleaseCpp(heap_);// ->~O1HeapInstance(); // TODO: make free func
Memory::Free(base_);
base_ = nullptr;
mi_collect(false);
}
}
bool InternalHeap::Init(AuUInt length)
{
SysAssert(!base_, "heap already initialized");
SysAssert(!mutex_, "heap already initialized");
SysAssert(length, "invalid heap allocation");
mutex_ = Threading::Primitives::MutexUnique();
if (!mutex_) return false;
base_ = Memory::FAlloc<void *>(length);
if (!base_) return false;
heap_ = o1heapInit(base_, length,
[this](const O1HeapInstance *const handle) -> void
{
SysAssertDbg(this->mutex_ ? true : false, "missing mutex");
this->mutex_->Lock();
},
[this](const O1HeapInstance *const handle) -> void
{
SysAssertDbg(this->mutex_ ? true : false, "missing mutex");
this->mutex_->Unlock();
});
return true;
}
Types::size_t InternalHeap::GetChunkSize(const void *head)
{
return InternalHeap::GetHeapSize(head);
}
void *InternalHeap::_FAlloc(Types::size_t length)
{
if (!heap_) return nullptr;
auto ret = o1heapAllocate(heap_, length);
if (ret) _count++;
return ret;
}
void *InternalHeap::_FAlloc(Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return _FAlloc(length);
}
void *InternalHeap::_ZAlloc(Types::size_t length)
{
if (!heap_) return nullptr;
auto ptr = _FAlloc(length);
if (!ptr) return nullptr;
std::memset(ptr, 0, length);
return ptr;
}
void *InternalHeap::_ZAlloc(Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return _ZAlloc(length);
}
void *InternalHeap::_ZRealloc(void *buffer, Types::size_t length)
{
auto prevLength = GetHeapSize(buffer);
auto alloc = _ZAlloc(length);
if (!alloc) return nullptr;
std::memcpy(alloc, buffer, std::min(prevLength, length));
_Free(buffer);
return alloc;
}
void *InternalHeap::_ZRealloc(void *buffer, Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return _ZRealloc(buffer, length);
}
void *InternalHeap::_FRealloc(void *buffer, Types::size_t length)
{
auto prevLength = GetHeapSize(buffer);
auto alloc = _FAlloc(length);
if (!alloc) return nullptr;
std::memcpy(alloc, buffer, std::min(prevLength, length));
_Free(buffer);
return alloc;
}
void *InternalHeap::_FRealloc(void *buffer, Types::size_t length, Types::size_t align)
{
SysAssert(align < O1HEAP_ALIGNMENT, "heap wrapping is unsupported, alignment past the supported 2^x alignment is not possible");
return _FRealloc(buffer, length);
}
void InternalHeap::_Free(void *buffer)
{
if (buffer == nullptr) return;
o1heapFree(heap_, buffer);
_count--;
}
void InternalHeap::TryRelease()
{
if (!_isDangling) return;
if (_count == 0)
{
delete this;
return;
}
}
void InternalHeap::RequestTermination()
{
if (_count)
{
LogWarn("Heap life was less than its allocations, waiting for final free");
LogWarn("Reporting using mayday!");
Telemetry::Mayday();
_isDangling = true;
TryRelease();
}
else
{
delete this;
}
}
AUKN_SYM Heap *AllocHeapNew(AuUInt size)
{
auto heap = _new InternalHeap();
if (!heap)
{
return nullptr;
}
if (!heap->Init(size))
{
delete heap;
return nullptr;
}
return heap;
}
AUKN_SYM void AllocHeapRelease(Heap *heap)
{
dynamic_cast<InternalHeap *>(heap)->RequestTermination();
}
}