Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
smallblock_allocator.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cinttypes>
5#include <cstddef>
6#include <cstdint>
7#include <cstring>
8
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/util/logging.h"
16
17namespace gaia {
18 namespace mem {
20 static constexpr uint32_t SmallBlockAlignment = (uint32_t)alignof(std::max_align_t);
22 static constexpr uint32_t SmallBlockGranularity = SmallBlockAlignment;
24 static constexpr uint32_t SmallBlockMaxSize = 512;
26 static constexpr uint32_t SmallBlockSizeTypeCount = SmallBlockMaxSize / SmallBlockGranularity;
27
31 constexpr uint32_t small_block_size(uint32_t sizeType) {
32 GAIA_ASSERT(sizeType < SmallBlockSizeTypeCount);
33 return (sizeType + 1) * SmallBlockGranularity;
34 }
35
39 constexpr uint8_t small_block_size_type(uint32_t sizeBytes) {
40 GAIA_ASSERT(sizeBytes > 0);
41 GAIA_ASSERT(sizeBytes <= SmallBlockMaxSize);
42 return (uint8_t)((align(sizeBytes, SmallBlockGranularity) / SmallBlockGranularity) - 1);
43 }
44
46 namespace detail {
47 struct SmallBlockHeader final {
48 uintptr_t m_pageAddr = 0;
49#if GAIA_DEBUG
50 uint32_t m_requestedBytes = 0;
51 uint32_t m_reserved = 0;
52#else
53 uint64_t m_reserved = 0;
54#endif
55 };
56 } // namespace detail
58
60 static constexpr uint32_t SmallBlockUsableOffset =
61 align((uint32_t)sizeof(detail::SmallBlockHeader), SmallBlockAlignment);
62
64 struct GAIA_API SmallBlockAllocatorPageStats final {
66 uint64_t mem_total;
68 uint64_t mem_used;
70 uint32_t num_pages;
73#if GAIA_DEBUG
75 uint64_t mem_requested;
77 uint32_t num_pages_empty;
78#endif
79 };
80
82 struct GAIA_API SmallBlockAllocatorStats final {
84 SmallBlockAllocatorPageStats stats[SmallBlockSizeTypeCount];
85 };
86
88 namespace detail {
89 static_assert(sizeof(SmallBlockHeader) <= SmallBlockUsableOffset);
90
91 constexpr uint32_t small_block_stride(uint32_t sizeType) {
92 return SmallBlockUsableOffset + small_block_size(sizeType);
93 }
94
95 class SmallBlockAllocatorImpl;
96
97 struct SmallBlockPage final: cnt::fwd_llist_base<SmallBlockPage> {
98 static constexpr uint16_t NBlocks = 64;
99 static constexpr uint16_t NBlocks_Bits = (uint16_t)core::count_bits(NBlocks);
100 static constexpr uint16_t SizeTypeBits = (uint16_t)core::count_bits(SmallBlockSizeTypeCount - 1);
101 static constexpr uint32_t InvalidBlockId = NBlocks + 1;
102#if GAIA_DEBUG
103 static constexpr uint8_t FreedBlockPattern = 0xDD;
104#endif
105 static constexpr uint32_t BlockArrayBytes = ((uint32_t)NBlocks_Bits * (uint32_t)NBlocks + 7) / 8;
106
107 using BlockArray = cnt::sarray<uint8_t, BlockArrayBytes>;
108 using BitView = core::bit_view<NBlocks_Bits>;
109
110 void* m_data;
111 BlockArray m_blocks;
112
113 uint32_t m_sizeType : SizeTypeBits;
114 uint32_t m_blockCnt : NBlocks_Bits;
115 uint32_t m_usedBlocks : NBlocks_Bits;
116 uint32_t m_nextFreeBlock : NBlocks_Bits;
117 uint32_t m_freeBlocks : NBlocks_Bits;
118
119#if GAIA_ASSERT_ENABLED
120 uint64_t m_usedMask = 0;
121#endif
122
126 SmallBlockPage(void* ptr, uint8_t sizeType):
127 m_data(ptr), m_sizeType(sizeType), m_blockCnt(0), m_usedBlocks(0), m_nextFreeBlock(0), m_freeBlocks(0) {}
128
130 GAIA_NODISCARD uint32_t block_stride() const {
131 return small_block_stride(m_sizeType);
132 }
133
134 void write_block_idx(uint32_t blockIdx, uint32_t value) {
135 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
136
137 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
138 GAIA_ASSERT(value <= InvalidBlockId);
139
140 BitView{{(uint8_t*)m_blocks.data(), BlockArrayBytes}}.set(bitPosition, (uint8_t)value);
141 }
142
143 GAIA_NODISCARD uint8_t read_block_idx(uint32_t blockIdx) const {
144 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
145
146 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
147
148 return BitView{{(uint8_t*)m_blocks.data(), BlockArrayBytes}}.get(bitPosition);
149 }
150
151#if GAIA_DEBUG
155 GAIA_NODISCARD void* alloc_block(uint32_t bytesWanted){
156#else
159 GAIA_NODISCARD void* alloc_block() {
160#endif
161 auto store_block_address = [&](uint32_t index) {
162 auto* pMemoryBlock = (uint8_t*)m_data + (index * block_stride());
163 GAIA_ASSERT((uintptr_t)pMemoryBlock % SmallBlockAlignment == 0);
164 auto& header = block_header(pMemoryBlock);
165 header.m_pageAddr = (uintptr_t)this;
166#if GAIA_DEBUG
167 header.m_requestedBytes = bytesWanted;
168 header.m_reserved = 0;
169#else
170 header.m_reserved = 0;
171#endif
172 auto* pData = pMemoryBlock + SmallBlockUsableOffset;
173 GAIA_ASSERT((uintptr_t)pData % SmallBlockAlignment == 0);
174 return (void*)pData;
175 };
176
177 GAIA_ASSERT(!full() && "Trying to allocate too many blocks!");
178
179 uint32_t index = 0;
180 if (m_freeBlocks == 0U) {
181 index = m_blockCnt;
182 ++m_usedBlocks;
183 ++m_blockCnt;
184 write_block_idx(index, index);
185 } else {
186 GAIA_ASSERT(m_nextFreeBlock < m_blockCnt && "Block allocator recycle list broken!");
187
188 ++m_usedBlocks;
189 --m_freeBlocks;
190
191 index = m_nextFreeBlock;
192 m_nextFreeBlock = read_block_idx(m_nextFreeBlock);
193 }
194
195#if GAIA_ASSERT_ENABLED
196 GAIA_ASSERT((m_usedMask & (uint64_t(1) << index)) == 0 && "Block already marked as live");
197 m_usedMask |= uint64_t(1) << index;
198#endif
199
200 return store_block_address(index);
201 }
202
205 void free_block(void* pBlock) {
206 GAIA_ASSERT(pBlock != nullptr);
207 GAIA_ASSERT(m_usedBlocks > 0);
208 GAIA_ASSERT(m_freeBlocks <= NBlocks);
209
210 const auto* pMemoryBlock = (uint8_t*)pBlock - SmallBlockUsableOffset;
211 const auto blckAddr = (uintptr_t)pMemoryBlock;
212 GAIA_ASSERT(blckAddr % SmallBlockAlignment == 0);
213 const auto dataAddr = (uintptr_t)m_data;
214 GAIA_ASSERT(blckAddr >= dataAddr);
215 const auto blockStride = (uintptr_t)block_stride();
216#if GAIA_ASSERT_ENABLED
217 const auto pageSize = blockStride * NBlocks;
218 GAIA_ASSERT(blckAddr < dataAddr + pageSize);
219#endif
220 GAIA_ASSERT((blckAddr - dataAddr) % blockStride == 0);
221 const auto blockIdx = (uint32_t)((blckAddr - dataAddr) / blockStride);
222 GAIA_ASSERT(blockIdx < m_blockCnt);
223
224#if GAIA_DEBUG
225 auto& header = block_header((void*)pMemoryBlock);
226 GAIA_ASSERT(header.m_requestedBytes > 0);
227#endif
228#if GAIA_ASSERT_ENABLED
229 GAIA_ASSERT((m_usedMask & (uint64_t(1) << blockIdx)) != 0 && "Double free or corrupted block state");
230 m_usedMask &= ~(uint64_t(1) << blockIdx);
231#endif
232
233#if GAIA_DEBUG
234 header.m_requestedBytes = 0;
235 std::memset(pBlock, FreedBlockPattern, small_block_size(m_sizeType));
236#endif
237
238 if (m_freeBlocks == 0U)
239 write_block_idx(blockIdx, InvalidBlockId);
240 else
241 write_block_idx(blockIdx, m_nextFreeBlock);
242 m_nextFreeBlock = blockIdx;
243
244 ++m_freeBlocks;
245 --m_usedBlocks;
246 }
247
249 GAIA_NODISCARD uint32_t used_blocks_cnt() const {
250 return m_usedBlocks;
251 }
252
254 GAIA_NODISCARD bool full() const {
255 return used_blocks_cnt() >= NBlocks;
256 }
257
259 GAIA_NODISCARD bool empty() const {
260 return used_blocks_cnt() == 0;
261 }
262
264 void verify() const {
265#if GAIA_ASSERT_ENABLED
266 GAIA_ASSERT(m_sizeType < SmallBlockSizeTypeCount);
267 GAIA_ASSERT(m_blockCnt <= NBlocks);
268 GAIA_ASSERT(m_usedBlocks <= m_blockCnt);
269 GAIA_ASSERT(m_freeBlocks <= m_blockCnt);
270 GAIA_ASSERT(m_usedBlocks + m_freeBlocks == m_blockCnt);
271 GAIA_ASSERT(((uintptr_t)m_data % SmallBlockAlignment) == 0);
272
273 [[maybe_unused]] uint64_t freeMask = 0;
274
275 if (m_freeBlocks != 0) {
276 uint32_t next = m_nextFreeBlock;
277 GAIA_FOR(m_freeBlocks) {
278 GAIA_ASSERT(next < m_blockCnt);
279 #if GAIA_DEBUG
280 const auto bit = uint64_t(1) << next;
281 GAIA_ASSERT((freeMask & bit) == 0 && "Free list contains a cycle");
282 freeMask |= bit;
283 #endif
284 next = read_block_idx(next);
285 }
286
287 GAIA_ASSERT(next == InvalidBlockId);
288 }
289
290 GAIA_FOR(m_blockCnt) {
291 const auto* pMemoryBlock = (const uint8_t*)m_data + (i * block_stride());
292 const auto& header = block_header(pMemoryBlock);
293 GAIA_ASSERT(header.m_pageAddr == (uintptr_t)this);
294 GAIA_ASSERT(((uintptr_t)pMemoryBlock % SmallBlockAlignment) == 0);
295
296 #if GAIA_DEBUG
297 const bool isFree = (freeMask & (uint64_t(1) << i)) != 0;
298 GAIA_ASSERT((header.m_requestedBytes == 0) == isFree);
299 #endif
300 }
301
302 #if GAIA_DEBUG
303 GAIA_ASSERT((m_usedMask & freeMask) == 0);
304 const auto liveMask = m_blockCnt == 64 ? ~uint64_t(0) : ((uint64_t(1) << m_blockCnt) - 1);
305 GAIA_ASSERT((m_usedMask | freeMask) == liveMask);
306 #endif
307#endif
308 }
309
310#if GAIA_DEBUG
312 GAIA_NODISCARD uint64_t requested_bytes() const {
313 if (m_usedBlocks == 0)
314 return 0;
315
316 uint64_t freeMask = 0;
317 uint32_t next = m_nextFreeBlock;
318 GAIA_FOR(m_freeBlocks) {
319 GAIA_ASSERT(next < m_blockCnt);
320 const auto bit = uint64_t(1) << next;
321 GAIA_ASSERT((freeMask & bit) == 0 && "Free list contains a cycle");
322 freeMask |= bit;
323 next = read_block_idx(next);
324 }
325
326 uint64_t requested = 0;
327 GAIA_FOR(m_blockCnt) {
328 if ((freeMask & (uint64_t(1) << i)) != 0)
329 continue;
330
331 const auto* pMemoryBlock = (const uint8_t*)m_data + (i * block_stride());
332 requested += block_header(pMemoryBlock).m_requestedBytes;
333 }
334
335 return requested;
336 }
337#endif
338
339 private:
340 static SmallBlockHeader& block_header(void* pMemoryBlock) {
341 return *(SmallBlockHeader*)pMemoryBlock;
342 }
343
344 static const SmallBlockHeader& block_header(const void* pMemoryBlock) {
345 return *(const SmallBlockHeader*)pMemoryBlock;
346 }
347 }; // namespace detail
348
349 enum class SmallBlockPageState : uint8_t { Detached, Empty, Partial, Full };
350
351 struct SmallBlockPageContainer final {
352 cnt::fwd_llist<SmallBlockPage> pagesEmpty;
353 cnt::fwd_llist<SmallBlockPage> pagesPartial;
354 cnt::fwd_llist<SmallBlockPage> pagesFull;
355 };
356 } // namespace mem
358
360 using SmallBlockAllocator = core::dyn_singleton<detail::SmallBlockAllocatorImpl>;
361
363 namespace detail {
365 class SmallBlockAllocatorImpl final {
366 friend ::gaia::mem::SmallBlockAllocator;
367
368 SmallBlockPageContainer m_pages[SmallBlockSizeTypeCount];
369 bool m_isDone = false;
370
371 SmallBlockAllocatorImpl() = default;
372
373 public:
374 static constexpr uint32_t MAX_SIZE = SmallBlockMaxSize;
375
376 ~SmallBlockAllocatorImpl() {
377 flush(true);
378
379#if GAIA_ASSERT_ENABLED
380 for (const auto& container: m_pages) {
381 const bool hasPages = container.pagesEmpty.first != nullptr || container.pagesPartial.first != nullptr ||
382 container.pagesFull.first != nullptr;
383 GAIA_ASSERT(!hasPages && "SmallBlockAllocator leaking memory");
384 }
385#endif
386 }
387
388 SmallBlockAllocatorImpl(SmallBlockAllocatorImpl&&) = delete;
389 SmallBlockAllocatorImpl(const SmallBlockAllocatorImpl&) = delete;
390 SmallBlockAllocatorImpl& operator=(SmallBlockAllocatorImpl&&) = delete;
391 SmallBlockAllocatorImpl& operator=(const SmallBlockAllocatorImpl&) = delete;
392
396 GAIA_NODISCARD void* alloc(uint32_t bytesWanted) {
397 GAIA_ASSERT(bytesWanted > 0);
398 GAIA_ASSERT(bytesWanted <= MAX_SIZE);
399 if (bytesWanted == 0 || bytesWanted > MAX_SIZE)
400 return nullptr;
401
402 const detail::ArenaLock arenaLock;
403 const auto sizeType = small_block_size_type(bytesWanted);
404 auto& container = m_pages[sizeType];
405
406 SmallBlockPageState prevState = SmallBlockPageState::Partial;
407 auto* pPage = container.pagesPartial.first;
408 if (pPage == nullptr) {
409 prevState = SmallBlockPageState::Empty;
410 pPage = container.pagesEmpty.first;
411 if (pPage == nullptr) {
412 prevState = SmallBlockPageState::Detached;
413 pPage = alloc_page(sizeType);
414 }
415 }
416
417#if GAIA_DEBUG
418 void* pBlock = pPage->alloc_block(bytesWanted);
419#else
420 void* pBlock = pPage->alloc_block();
421#endif
422 GAIA_PROF_ALLOC(pBlock, bytesWanted);
423 move_page(container, pPage, prevState, state_for(*pPage));
424 verify();
425 return pBlock;
426 }
427
430 void free(void* pBlock) {
431 GAIA_ASSERT(pBlock != nullptr);
432 if (pBlock == nullptr)
433 return;
434
435 const detail::ArenaLock arenaLock;
436 const auto& header = *(const SmallBlockHeader*)((uint8_t*)pBlock - SmallBlockUsableOffset);
437 const auto pageAddr = header.m_pageAddr;
438 GAIA_ASSERT(pageAddr % sizeof(uintptr_t) == 0);
439#if GAIA_DEBUG
440 GAIA_ASSERT(header.m_requestedBytes > 0);
441#endif
442 auto* pPage = (SmallBlockPage*)pageAddr;
443 const auto prevState = state_for(*pPage);
444 auto& container = m_pages[pPage->m_sizeType];
445
446 GAIA_PROF_FREE(pBlock);
447 pPage->free_block(pBlock);
448 move_page(container, pPage, prevState, state_for(*pPage));
449 verify();
450
451 if (m_isDone) {
452 if (pPage->empty()) {
453 container.pagesEmpty.unlink(pPage);
454 free_page(pPage);
455 }
456
457 try_delete_this();
458 }
459 }
460
463 void flush(bool releaseAll = false) {
464 const detail::ArenaLock arenaLock;
465 for (uint32_t i = 0; i < SmallBlockSizeTypeCount; ++i)
466 flush_pages(m_pages[i], releaseAll);
467 verify();
468 }
469
472 GAIA_NODISCARD SmallBlockAllocatorStats stats() const {
473 SmallBlockAllocatorStats stats{};
474 for (uint32_t sizeType = 0; sizeType < SmallBlockSizeTypeCount; ++sizeType)
475 stats.stats[sizeType] = page_stats(sizeType);
476 return stats;
477 }
478
480 void diag() const {
481 const auto allStats = stats();
482 for (uint32_t sizeType = 0; sizeType < SmallBlockSizeTypeCount; ++sizeType) {
483 const auto& stats = allStats.stats[sizeType];
484 if (stats.num_pages == 0)
485 continue;
486
487 GAIA_LOG_N("SmallBlockAllocator %u B stats", small_block_size(sizeType));
488 GAIA_LOG_N(" Allocated: %" PRIu64 " B", stats.mem_total);
489 GAIA_LOG_N(" Reserved by live blocks: %" PRIu64 " B", stats.mem_used);
490 GAIA_LOG_N(" Pages: %u", stats.num_pages);
491 GAIA_LOG_N(" Reusable pages: %u", stats.num_pages_free);
492#if !GAIA_DEBUG
493 GAIA_LOG_N(
494 " Utilization: %.1f%%",
495 stats.mem_total ? 100.0 * ((double)stats.mem_used / (double)stats.mem_total) : 0.0);
496#else
497 GAIA_LOG_N(" Requested: %" PRIu64 " B", stats.mem_requested);
498 GAIA_LOG_N(" Free capacity: %" PRIu64 " B", stats.mem_total - stats.mem_used);
499 GAIA_LOG_N(" Internal slack: %" PRIu64 " B", stats.mem_used - stats.mem_requested);
500 GAIA_LOG_N(
501 " Utilization: %.1f%%",
502 stats.mem_total ? 100.0 * ((double)stats.mem_requested / (double)stats.mem_total) : 0.0);
503 GAIA_LOG_N(" Empty pages: %u", stats.num_pages_empty);
504#endif
505 }
506 }
507
509 void verify() const {
510#if GAIA_ASSERT_ENABLED
511 for (uint32_t sizeType = 0; sizeType < SmallBlockSizeTypeCount; ++sizeType)
512 verify_container(m_pages[sizeType], sizeType);
513#endif
514 }
515
516 private:
517 static constexpr const char* s_strSmallBlockData = "SmallBlockData";
518 static constexpr const char* s_strSmallBlockPage = "SmallBlockPage";
519
520 static SmallBlockPage* alloc_page(uint8_t sizeType) {
521 const uint32_t size = small_block_stride(sizeType) * SmallBlockPage::NBlocks;
522 auto* pPageData = AllocHelper::alloc_alig<uint8_t>(s_strSmallBlockData, SmallBlockAlignment, size);
523 auto* pMemoryPage = AllocHelper::alloc<SmallBlockPage>(s_strSmallBlockPage);
524 return new (pMemoryPage) SmallBlockPage(pPageData, sizeType);
525 }
526
527 static void free_page(SmallBlockPage* pPage) {
528 GAIA_ASSERT(pPage != nullptr);
529
530 AllocHelper::free_alig(s_strSmallBlockData, pPage->m_data);
531 pPage->~SmallBlockPage();
532 AllocHelper::free(s_strSmallBlockPage, pPage);
533 }
534
535 void done() {
536 m_isDone = true;
537 }
538
539 void try_delete_this() {
540 bool allEmpty = true;
541 for (const auto& container: m_pages) {
542 const bool hasPages = container.pagesEmpty.first != nullptr || container.pagesPartial.first != nullptr ||
543 container.pagesFull.first != nullptr;
544 allEmpty = allEmpty && !hasPages;
545 }
546
547 if (allEmpty)
548 delete this;
549 }
550
551 static constexpr uint32_t warm_pages_to_keep() {
552 return 0;
553 }
554
555 static SmallBlockPageState state_for(const SmallBlockPage& page) {
556 if (page.empty())
557 return SmallBlockPageState::Empty;
558 if (page.full())
559 return SmallBlockPageState::Full;
560 return SmallBlockPageState::Partial;
561 }
562
563 static cnt::fwd_llist<SmallBlockPage>& page_list(SmallBlockPageContainer& container, SmallBlockPageState state) {
564 switch (state) {
565 case SmallBlockPageState::Empty:
566 return container.pagesEmpty;
567 case SmallBlockPageState::Partial:
568 return container.pagesPartial;
569 default:
570 GAIA_ASSERT(state == SmallBlockPageState::Full);
571 return container.pagesFull;
572 }
573 }
574
575 static void move_page(
576 SmallBlockPageContainer& container, SmallBlockPage* pPage, SmallBlockPageState fromState,
577 SmallBlockPageState toState) {
578 if (fromState == toState)
579 return;
580
581 if (fromState != SmallBlockPageState::Detached)
582 page_list(container, fromState).unlink(pPage);
583 page_list(container, toState).link(pPage);
584 }
585
586 [[maybe_unused]] static void verify_page_membership(
587 [[maybe_unused]] const SmallBlockPage& page, //
588 [[maybe_unused]] uint32_t sizeType, //
589 [[maybe_unused]] SmallBlockPageState expectedState //
590 ) {
591 GAIA_ASSERT(page.m_sizeType == sizeType);
592 GAIA_ASSERT(state_for(page) == expectedState);
593 GAIA_ASSERT(page.get_fwd_llist_link().linked());
594 }
595
596 static void verify_container(const SmallBlockPageContainer& container, uint32_t sizeType) {
597 for (const auto& page: container.pagesEmpty) {
598 verify_page_membership(page, sizeType, SmallBlockPageState::Empty);
599 page.verify();
600 }
601
602 for (const auto& page: container.pagesPartial) {
603 verify_page_membership(page, sizeType, SmallBlockPageState::Partial);
604 page.verify();
605 }
606
607 for (const auto& page: container.pagesFull) {
608 verify_page_membership(page, sizeType, SmallBlockPageState::Full);
609 page.verify();
610 }
611 }
612
613 GAIA_NODISCARD SmallBlockAllocatorPageStats page_stats(uint32_t sizeType) const {
614 SmallBlockAllocatorPageStats stats{};
615 const auto& container = m_pages[sizeType];
616 const auto blockStride = (uint64_t)small_block_stride(sizeType);
617 const auto pageSize = blockStride * SmallBlockPage::NBlocks;
618
619 stats.num_pages = (uint32_t)container.pagesEmpty.size() + (uint32_t)container.pagesPartial.size() +
620 (uint32_t)container.pagesFull.size();
621 stats.num_pages_free = (uint32_t)container.pagesEmpty.size() + (uint32_t)container.pagesPartial.size();
622 stats.mem_total = stats.num_pages * pageSize;
623 stats.mem_used = container.pagesFull.size() * pageSize;
624
625#if GAIA_DEBUG
626 stats.num_pages_empty = (uint32_t)container.pagesEmpty.size();
627
628 for (const auto& page: container.pagesFull)
629 stats.mem_requested += page.requested_bytes();
630
631 for (const auto& page: container.pagesPartial) {
632 stats.mem_used += page.used_blocks_cnt() * blockStride;
633 stats.mem_requested += page.requested_bytes();
634 }
635#else
636 for (const auto& page: container.pagesPartial)
637 stats.mem_used += page.used_blocks_cnt() * blockStride;
638#endif
639
640 return stats;
641 }
642
643 static void flush_pages(SmallBlockPageContainer& container, bool releaseAll) {
644 const bool keepWarmPage = !releaseAll && warm_pages_to_keep() != 0;
645 bool keptWarmPage = false;
646
647 for (auto it = container.pagesEmpty.begin(); it != container.pagesEmpty.end();) {
648 auto* pPage = &(*it);
649 ++it;
650
651 if (!pPage->empty())
652 continue;
653
654 if (keepWarmPage && !keptWarmPage) {
655 keptWarmPage = true;
656 continue;
657 }
658
659 container.pagesEmpty.unlink(pPage);
660 free_page(pPage);
661 }
662 }
663 };
664
665 } // namespace detail
667} // namespace gaia
668} // namespace gaia
669
671#define GAIA_USE_SMALLBLOCK(name) \
672 void _smallblockallocator_verify_size() { \
673 static_assert( \
674 sizeof(*this) <= ::gaia::mem::SmallBlockMaxSize, "Object is too large to be used with SmallBlockAllocator"); \
675 } \
676 static void* operator new(size_t size) { \
677 return ::gaia::mem::SmallBlockAllocator::get().alloc((uint32_t)size); \
678 } \
679 static void* operator new[](size_t size) { \
680 return ::gaia::mem::mem_alloc(#name, (uint32_t)size); \
681 } \
682 static void operator delete(void* p) noexcept { \
683 ::gaia::mem::SmallBlockAllocator::get().free(p); \
684 } \
685 static void operator delete[](void* p) { \
686 return ::gaia::mem::mem_free(#name, p); \
687 }
Array with variable size of elements of type.
Definition darray_impl.h:27
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:29
Provides packed access to fixed-width unsigned values stored in a byte span.
Definition bit_utils.h:13
Allocation statistics for one small-block size class.
Definition smallblock_allocator.h:64
uint32_t num_pages_free
Number of reusable pages (partial + empty).
Definition smallblock_allocator.h:72
uint64_t mem_used
Memory reserved by live blocks for this size class.
Definition smallblock_allocator.h:68
uint32_t num_pages
Number of allocated pages.
Definition smallblock_allocator.h:70
uint64_t mem_total
Total allocated memory for this size class.
Definition smallblock_allocator.h:66
Allocation statistics for every small-block size class.
Definition smallblock_allocator.h:82