2#include "gaia/config/config.h"
9#include "gaia/cnt/fwd_llist.h"
10#include "gaia/cnt/sarray.h"
11#include "gaia/core/bit_utils.h"
12#include "gaia/core/dyn_singleton.h"
13#include "gaia/core/utility.h"
14#include "gaia/mem/mem_alloc.h"
15#include "gaia/meta/type_info.h"
16#include "gaia/util/logging.h"
20 static constexpr uint32_t MemoryBlockAlignment = 16;
23 static constexpr uint32_t MemoryBlockBytesDefault = 32768;
25 static constexpr uint32_t MemoryBlockUsableOffset =
sizeof(uintptr_t);
34 template <
typename T, u
int32_t RequestedBlockSize>
36 static constexpr uint32_t next_multiple_of_alignment(uint32_t num) {
37 return (num + (MemoryBlockAlignment - 1)) & uint32_t(-(int32_t)MemoryBlockAlignment);
39 static constexpr uint32_t calculate_block_size() {
40 if constexpr (RequestedBlockSize == 0)
41 return next_multiple_of_alignment(MemoryBlockBytesDefault);
43 return next_multiple_of_alignment(RequestedBlockSize);
46 static constexpr uint32_t MemoryBlockBytes = calculate_block_size();
47 static constexpr uint16_t NBlocks = 48;
48 static constexpr uint16_t NBlocks_Bits = (uint16_t)core::count_bits(NBlocks);
49 static constexpr uint32_t InvalidBlockId = NBlocks + 1;
51 static constexpr uint8_t FreedBlockPattern = 0xDD;
52 static constexpr uintptr_t FreedPageMarker = ~(uintptr_t)0;
54 static constexpr uint32_t BlockArrayBytes = ((uint32_t)NBlocks_Bits * (uint32_t)NBlocks + 7) / 8;
80 void write_block_idx(uint32_t blockIdx, uint32_t value) {
81 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
83 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
84 GAIA_ASSERT(value <= InvalidBlockId);
86 BitView{{(uint8_t*)
m_blocks.data(), BlockArrayBytes}}.set(bitPosition, (uint8_t)value);
89 uint8_t read_block_idx(uint32_t blockIdx)
const {
90 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
92 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
94 return BitView{{(uint8_t*)
m_blocks.data(), BlockArrayBytes}}.get(bitPosition);
99 auto StoreBlockAddress = [&](uint32_t index) {
102 uint8_t* pMemoryBlock = (uint8_t*)
m_data + (index * MemoryBlockBytes);
103 GAIA_ASSERT((uintptr_t)pMemoryBlock % MemoryBlockAlignment == 0);
105 return (
void*)(pMemoryBlock + MemoryBlockUsableOffset);
109 GAIA_ASSERT(!full() &&
"Trying to allocate too many blocks!");
115 write_block_idx(index, index);
117 return StoreBlockAddress(index);
128 return StoreBlockAddress(index);
136 auto ReadBlockAddress = [&](
void* pMemory) {
138 const auto* pMemoryBlock = (uint8_t*)pMemory - MemoryBlockUsableOffset;
139#if GAIA_ASSERT_ENABLED
141 GAIA_ASSERT(pageAddr == (uintptr_t)
this);
143 const auto blckAddr = (uintptr_t)pMemoryBlock;
144 GAIA_ASSERT(blckAddr % 16 == 0);
145 const auto dataAddr = (uintptr_t)
m_data;
146 const auto blockIdx = (uint32_t)((blckAddr - dataAddr) / MemoryBlockBytes);
149 const auto blockIdx = ReadBlockAddress(pBlock);
153 std::memset(pBlock, FreedBlockPattern, MemoryBlockBytes - MemoryBlockUsableOffset);
158 write_block_idx(blockIdx, InvalidBlockId);
167 GAIA_NODISCARD uint32_t used_blocks_cnt()
const {
171 GAIA_NODISCARD
bool full()
const {
172 return used_blocks_cnt() >= NBlocks;
175 GAIA_NODISCARD
bool empty()
const {
176 return used_blocks_cnt() == 0;
179 void verify()
const {
180#if GAIA_ASSERT_ENABLED
185 GAIA_ASSERT(((uintptr_t)
m_data % MemoryBlockAlignment) == 0);
187 uint64_t freeMask = 0;
192 const auto bit = uint64_t(1) << next;
193 GAIA_ASSERT((freeMask & bit) == 0 &&
"Free list contains a cycle");
195 next = read_block_idx(next);
198 GAIA_ASSERT(next == InvalidBlockId);
202 const auto* pMemoryBlock = (
const uint8_t*)
m_data + (i * MemoryBlockBytes);
203 GAIA_ASSERT(((uintptr_t)pMemoryBlock % MemoryBlockAlignment) == 0);
206 const bool isFree = (freeMask & (uint64_t(1) << i)) != 0;
207 const auto pageAddr = (uintptr_t)mem::unaligned_ref<uintptr_t>{(
void*)pMemoryBlock};
208 GAIA_ASSERT(pageAddr == (isFree ? FreedPageMarker : (uintptr_t)this));
215 template <
typename T, u
int32_t RequestedBlockSize>
222 GAIA_NODISCARD
bool empty()
const {
239 template <
typename T, u
int32_t RequestedBlockSize>
240 class PagedAllocatorImpl;
243 template <
typename T, u
int32_t RequestedBlockSize = 0>
248 template <
typename T, u
int32_t RequestedBlockSize>
250 friend ::gaia::mem::PagedAllocator<T, RequestedBlockSize>;
252 inline static char s_strPageData[256]{};
253 inline static char s_strMemPage[256]{};
261 bool m_isDone =
false;
267 auto ct_name = meta::type_info::name<T>();
268 const auto ct_name_len = (uint32_t)ct_name.size();
269 GAIA_STRCPY(s_strPageData, 256,
"PageData_");
270 memcpy((
void*)&s_strPageData[9], (
const void*)ct_name.data(), ct_name_len);
271 s_strPageData[9 + ct_name_len] = 0;
272 GAIA_STRCPY(s_strMemPage, 256,
"MemPage_");
273 memcpy((
void*)&s_strMemPage[8], (
const void*)ct_name.data(), ct_name_len);
274 s_strMemPage[8 + ct_name_len] = 0;
281 auto memStats =
stats();
282 if (memStats.mem_total != 0) {
283 GAIA_ASSERT2(
false,
"Paged allocator leaking memory");
284 GAIA_LOG_W(
"Paged allocator leaking memory!");
300 void*
alloc([[maybe_unused]] uint32_t dummy) {
301 void* pBlock =
nullptr;
305 GAIA_ASSERT(pPage ==
nullptr || !pPage->full());
306 if (pPage ==
nullptr) {
308 pPage = alloc_page();
313 pBlock = pPage->alloc_block();
327 GAIA_CLANG_WARNING_PUSH()
329 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
334 const auto pageAddr = *(uintptr_t*)((uint8_t*)pBlock - MemoryBlockUsableOffset);
335 GAIA_ASSERT(pageAddr % MemoryBlockAlignment == 0);
336 auto* pPage = (
Page*)pageAddr;
337 const bool wasFull = pPage->full();
339#if GAIA_ASSERT_ENABLED
341 const auto res = m_pages.
pagesFull.has(pPage);
342 GAIA_ASSERT(res &&
"Memory page couldn't be found among full pages");
344 const auto res = m_pages.
pagesFree.has(pPage);
345 GAIA_ASSERT(res &&
"Memory page couldn't be found among free pages");
350 pPage->free_block(pBlock);
365 if (pPage->empty()) {
374 GAIA_CLANG_WARNING_POP()
384 for (
const auto& page: m_pages.
pagesFree)
385 stats.
mem_used += page.used_blocks_cnt() * (size_t)Page::MemoryBlockBytes;
393 auto* pPage = &(*it);
409 auto memStats =
stats();
410 GAIA_LOG_N(
"PagedAllocator %p stats", (
void*)
this);
411 GAIA_LOG_N(
" Allocated: %" PRIu64
" B", memStats.mem_total);
412 GAIA_LOG_N(
" Used: %" PRIu64
" B", memStats.mem_total - memStats.mem_used);
413 GAIA_LOG_N(
" Overhead: %" PRIu64
" B", memStats.mem_used);
415 " Utilization: %.1f%%",
416 memStats.mem_total != 0 ? 100.0 * ((
double)memStats.mem_used / (
double)memStats.mem_total) : 0.0);
417 GAIA_LOG_N(
" Pages: %u", memStats.num_pages);
418 GAIA_LOG_N(
" Free pages: %u", memStats.num_pages_free);
421 void verify()
const {
422#if GAIA_ASSERT_ENABLED
423 for (
const auto& page: m_pages.pagesFree) {
424 GAIA_ASSERT(page.get_fwd_llist_link().linked());
425 GAIA_ASSERT(!page.full());
429 for (
const auto& page: m_pages.pagesFull) {
430 GAIA_ASSERT(page.get_fwd_llist_link().linked());
431 GAIA_ASSERT(page.full());
438 static Page* alloc_page() {
439 const uint32_t size = Page::NBlocks * Page::MemoryBlockBytes;
440 auto* pPageData = mem::AllocHelper::alloc_alig<uint8_t>(&s_strPageData[0], MemoryBlockAlignment, size);
441 auto* pMemoryPage = mem::AllocHelper::alloc<Page>(&s_strMemPage[0]);
442 return new (pMemoryPage) Page(pPageData);
445 static void free_page(Page* pMemoryPage) {
446 GAIA_ASSERT(pMemoryPage !=
nullptr);
448 mem::AllocHelper::free_alig(&s_strPageData[0], pMemoryPage->m_data);
449 pMemoryPage->~MemoryPage();
450 mem::AllocHelper::free(&s_strMemPage[0], pMemoryPage);
457 void try_delete_this() {
Array with variable size of elements of type.
Definition darray_impl.h:25
Gaia-ECS is a header-only library which means we want to avoid using global static variables because ...
Definition dyn_singleton.h:21
Definition paged_allocator.h:249
void diag() const
Performs diagnostics of the memory used.
Definition paged_allocator.h:408
void free(void *pBlock)
Releases memory allocated for pointer.
Definition paged_allocator.h:332
MemoryPageStats stats() const
Returns allocator statistics.
Definition paged_allocator.h:377
void * alloc(uint32_t dummy)
Allocates memory.
Definition paged_allocator.h:300
void flush()
Flushes unused memory.
Definition paged_allocator.h:391
Pointer wrapper for writing memory in defined way (not causing undefined behavior)
Definition mem_alloc.h:260
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Each fwd_llist node either has to inherit from fwd_llist_base or it has to provide get_fwd_llist_link...
Definition fwd_llist.h:26
Definition bit_utils.h:11
Definition paged_allocator.h:216
cnt::fwd_llist< MemoryPage< T, RequestedBlockSize > > pagesFull
List of full pages.
Definition paged_allocator.h:220
cnt::fwd_llist< MemoryPage< T, RequestedBlockSize > > pagesFree
List of available pages.
Definition paged_allocator.h:218
Definition paged_allocator.h:227
uint32_t num_pages_free
Number of free pages.
Definition paged_allocator.h:235
uint64_t mem_used
Memory actively used.
Definition paged_allocator.h:231
uint64_t mem_total
Total allocated memory.
Definition paged_allocator.h:229
uint32_t num_pages
Number of allocated pages.
Definition paged_allocator.h:233
Definition paged_allocator.h:35
void free_block(void *pBlock)
Release the block allocated by this page.
Definition paged_allocator.h:132
BlockArray m_blocks
Implicit list of blocks.
Definition paged_allocator.h:61
uint32_t m_nextFreeBlock
Index of the next block to recycle.
Definition paged_allocator.h:68
uint32_t m_freeBlocks
Number of blocks to recycle.
Definition paged_allocator.h:70
uint32_t m_usedBlocks
Number of used blocks out of NBlocks.
Definition paged_allocator.h:66
MemoryPage(void *ptr)
Free bits to use in the future.
Definition paged_allocator.h:74
uint32_t m_blockCnt
Number of blocks in the block array.
Definition paged_allocator.h:64
GAIA_NODISCARD void * alloc_block()
Allocate a new block for this page.
Definition paged_allocator.h:98