Implemented Region. Arena allocator now uses Region for OS specific allocation and page management.
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, macos-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled

This commit is contained in:
2026-08-15 15:50:32 -07:00
parent 3a6711a942
commit e358b02bea
6 changed files with 225 additions and 78 deletions

View File

@@ -10,6 +10,7 @@ set(PUBLIC_HEADERS
include/arena.h include/arena.h
include/block.h include/block.h
include/heap.h include/heap.h
include/region.h
include/util.h include/util.h
) )
@@ -18,6 +19,7 @@ set(SRCS
src/block.cpp src/block.cpp
src/heap.cpp src/heap.cpp
src/mimir.cpp src/mimir.cpp
src/region.cpp
src/util.cpp src/util.cpp
) )

View File

@@ -33,6 +33,8 @@
#include <cstddef> #include <cstddef>
#include "region.h"
namespace mimir { namespace mimir {
class Arena class Arena
{ {
@@ -53,14 +55,13 @@ public:
// Reset the arena, potentially also releasing committed pages // Reset the arena, potentially also releasing committed pages
void reset(); void reset();
private: private:
std::byte* m_data; Region m_region;
size_t m_minSize; size_t m_minSize;
size_t m_maxSize;
size_t m_committedSize;
size_t m_usedSize; size_t m_usedSize;
static const size_t kWatermarkLen = 8; static const size_t kWatermarkLen = 8;
size_t m_watermark[kWatermarkLen]; size_t m_watermark[kWatermarkLen];
}; // class Arena }; // class Arena
} // namespace mimir } // namespace mimir

View File

@@ -34,6 +34,7 @@
#include "arena.h" #include "arena.h"
#include "block.h" #include "block.h"
#include "heap.h" #include "heap.h"
#include "region.h"
#include "util.h" #include "util.h"
namespace mimir { namespace mimir {

55
include/region.h Normal file
View File

@@ -0,0 +1,55 @@
//
// region.h
// Kraken Engine / Mimir
//
// Copyright 2026 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#pragma once
#include <cstddef>
namespace mimir {
class Region
{
public:
Region();
~Region();
bool init(size_t maxSize = 1ULL << 32);
bool resize(size_t size);
size_t getSize() const;
size_t getMaxSize() const;
std::byte* getAddress() const;
private:
std::byte* m_data;
size_t m_committedSize;
size_t m_maxSize;
};
} // namespace mimir

View File

@@ -40,10 +40,7 @@ namespace mimir {
Arena::Arena() Arena::Arena()
: m_minSize(0) : m_minSize(0)
, m_maxSize(0)
, m_data(nullptr)
, m_usedSize(0) , m_usedSize(0)
, m_committedSize(0)
{ {
for (size_t i = 0; i < kWatermarkLen; i++) { for (size_t i = 0; i < kWatermarkLen; i++) {
m_watermark[i] = 0; m_watermark[i] = 0;
@@ -52,73 +49,31 @@ Arena::Arena()
Arena::~Arena() Arena::~Arena()
{ {
if (m_data) {
#if defined(_WIN32) || defined(_WIN64)
if (!VirtualFree((void*)m_data, 0, MEM_RELEASE)) {
ReportWindowsLastError("VirtualAlloc");
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
munmap(m_data, KRAKEN_MEM_ROUND_UP_PAGE(m_maxSize));
#else
static_assert(false, "Not Implemented");
#endif
}
} }
bool Arena::init(size_t minSize, size_t maxSize) bool Arena::init(size_t minSize, size_t maxSize)
{ {
assert(maxSize >= minSize); assert(maxSize >= minSize);
assert(m_data == nullptr);
assert(m_usedSize == 0); assert(m_usedSize == 0);
assert(m_committedSize == 0);
m_minSize = KRAKEN_MEM_ROUND_UP_PAGE(minSize); m_minSize = KRAKEN_MEM_ROUND_UP_PAGE(minSize);
m_maxSize = KRAKEN_MEM_ROUND_UP_PAGE(maxSize); return m_region.init(maxSize);
#if defined(_WIN32) || defined(_WIN64)
m_data = (std::byte*)VirtualAlloc(NULL, KRAKEN_MEM_ROUND_UP_PAGE(m_maxSize), MEM_RESERVE, PAGE_NOACCESS);
if (m_data == nullptr) {
ReportWindowsLastError("VirtualAlloc");
return false;
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
m_data = (std::byte*)mmap(NULL, KRAKEN_MEM_ROUND_UP_PAGE(m_maxSize), PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (m_data == MAP_FAILED) {
return false;
}
#else
static_assert(false, "Not Implemented");
#endif
return true;
} }
std::byte* Arena::alloc(size_t size) std::byte* Arena::alloc(size_t size)
{ {
size_t neededSize = m_usedSize + size; size_t neededSize = m_usedSize + size;
if (neededSize > m_committedSize) if (neededSize > m_region.getSize())
{ {
// Increase the size exponentially when we run out
size_t newSize = std::bit_ceil(neededSize) << 1; size_t newSize = std::bit_ceil(neededSize) << 1;
if (newSize < m_minSize) { if (newSize < m_minSize) {
newSize = m_minSize; newSize = m_minSize;
} }
newSize = KRAKEN_MEM_ROUND_UP_PAGE(newSize); m_region.resize(newSize);
}
#if defined(_WIN32) || defined(_WIN64) std::byte* ret = m_region.getAddress() + m_usedSize;
if(VirtualAlloc(m_data + m_committedSize, newSize - m_committedSize, MEM_COMMIT, PAGE_READWRITE) == nullptr) {
ReportWindowsLastError("VirtualAlloc");
return nullptr;
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
if (mprotect(m_data + m_committedSize, newSize - m_committedSize, PROT_READ | PROT_WRITE) != 0) {
return nullptr;
}
#else
static_assert(false, "Not Implemented");
#endif
m_committedSize = newSize;
} // if (neededSize > m_committedSize)
std::byte* ret = m_data + m_usedSize;
m_usedSize = neededSize; m_usedSize = neededSize;
return ret; return ret;
} }
@@ -131,7 +86,7 @@ std::byte* Arena::allocA16(size_t size)
return nullptr; return nullptr;
} }
return m_data + nextByte; return m_region.getAddress() + nextByte;
} }
std::byte* Arena::allocA64(size_t size) std::byte* Arena::allocA64(size_t size)
@@ -142,7 +97,7 @@ std::byte* Arena::allocA64(size_t size)
return nullptr; return nullptr;
} }
return m_data + nextByte; return m_region.getAddress() + nextByte;
} }
void Arena::reset() void Arena::reset()
@@ -161,28 +116,8 @@ void Arena::reset()
targetSize = m_minSize; targetSize = m_minSize;
} }
size_t thresholdSize = targetSize << 1; // The threshold for shrinking is greater than the target to implement hysteresis size_t thresholdSize = targetSize << 1; // The threshold for shrinking is greater than the target to implement hysteresis
if (m_committedSize > thresholdSize) { if (m_region.getSize() > thresholdSize) {
size_t newSize = KRAKEN_MEM_ROUND_UP_PAGE(targetSize); m_region.resize(targetSize);
std::byte* rangeStart = m_data + newSize;
size_t rangeLen = m_committedSize - newSize;
#if defined(_WIN32) || defined(_WIN64)
if (!VirtualAlloc(rangeStart, rangeLen, MEM_RESET, PAGE_NOACCESS)) {
ReportWindowsLastError("VirtualAlloc");
} else {
m_committedSize = newSize;
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
if (madvise(rangeStart, rangeLen, MADV_DONTNEED) != 0) {
// TODO - Log Error or debug build assert
}
if (mprotect(rangeStart, rangeLen, PROT_NONE) != 0) {
// TODO - Log Error or debug build assert
}
#else
static_assert(false, "Not Implemented");
#endif
m_committedSize = newSize;
} }
m_usedSize = 0; m_usedSize = 0;

153
src/region.cpp Normal file
View File

@@ -0,0 +1,153 @@
//
// region.cpp
// Kraken Engine
//
// Copyright 2026 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
//
#include "../include/mimir.h"
#include "mimir_impl.h"
#include <cassert>
#include <cstring>
#include <bit>
namespace mimir {
Region::Region()
: m_data(nullptr)
, m_committedSize(0)
, m_maxSize(0)
{
}
Region::~Region()
{
if (m_data) {
#if defined(_WIN32) || defined(_WIN64)
if (!VirtualFree((void*)m_data, 0, MEM_RELEASE)) {
ReportWindowsLastError("VirtualAlloc");
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
munmap(m_data, KRAKEN_MEM_ROUND_UP_PAGE(m_maxSize));
#else
static_assert(false, "Not Implemented");
#endif
}
}
bool Region::init(size_t maxSize)
{
assert(m_data == nullptr);
assert(m_committedSize == 0);
m_maxSize = KRAKEN_MEM_ROUND_UP_PAGE(maxSize);
#if defined(_WIN32) || defined(_WIN64)
m_data = (std::byte*)VirtualAlloc(NULL, KRAKEN_MEM_ROUND_UP_PAGE(m_maxSize), MEM_RESERVE, PAGE_NOACCESS);
if (m_data == nullptr) {
ReportWindowsLastError("VirtualAlloc");
return false;
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
m_data = (std::byte*)mmap(NULL, KRAKEN_MEM_ROUND_UP_PAGE(m_maxSize), PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (m_data == MAP_FAILED) {
return false;
}
#else
static_assert(false, "Not Implemented");
#endif
return true;
}
bool Region::resize(size_t size)
{
size_t newSize = KRAKEN_MEM_ROUND_UP_PAGE(size);
if (newSize == m_committedSize) {
return true;
}
if (newSize > m_committedSize) {
// ---- Growing ----
#if defined(_WIN32) || defined(_WIN64)
if (VirtualAlloc(m_data + m_committedSize, newSize - m_committedSize, MEM_COMMIT, PAGE_READWRITE) == nullptr) {
ReportWindowsLastError("VirtualAlloc");
return false;
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
if (mprotect(m_data + m_committedSize, newSize - m_committedSize, PROT_READ | PROT_WRITE) != 0) {
return false;
}
#else
static_assert(false, "Not Implemented");
#endif
m_committedSize = newSize;
return true;
} // if (newSize > m_committedSize)
// ---- Shrinking ----
std::byte* rangeStart = m_data + newSize;
size_t rangeLen = m_committedSize - newSize;
#if defined(_WIN32) || defined(_WIN64)
if (!VirtualAlloc(rangeStart, rangeLen, MEM_RESET, PAGE_NOACCESS)) {
ReportWindowsLastError("VirtualAlloc");
} else {
m_committedSize = newSize;
}
#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID)
if (madvise(rangeStart, rangeLen, MADV_DONTNEED) != 0) {
// TODO - Log Error or debug build assert
}
if (mprotect(rangeStart, rangeLen, PROT_NONE) != 0) {
// TODO - Log Error or debug build assert
}
#else
static_assert(false, "Not Implemented");
#endif
m_committedSize = newSize;
return true;
}
size_t Region::getSize() const
{
return m_committedSize;
}
size_t Region::getMaxSize() const
{
return m_maxSize;
}
std::byte* Region::getAddress() const
{
return m_data;
}
};