Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
fixed_pool_allocator.cpp
Go to the documentation of this file.
1#include <pch.hpp>
2
4
5#include <helios/assert.hpp>
8#include <helios/memory/details/profile.hpp>
9
10#include <atomic>
11#include <cstddef>
12#include <utility>
13
14namespace {
15
16void PushBlock(std::atomic<void*>& head, void* block) noexcept {
17 void* observed = head.load(std::memory_order_acquire);
18 for (;;) {
19 *static_cast<void**>(block) = observed;
20 if (head.compare_exchange_weak(observed, block, std::memory_order_release,
21 std::memory_order_acquire)) {
22 return;
23 }
24 }
25}
26
27[[nodiscard]] void* PopBlock(std::atomic<void*>& head) noexcept {
28 void* observed = head.load(std::memory_order_acquire);
29 for (;;) {
30 if (observed == nullptr) {
31 return nullptr;
32 }
33 void* const next = *static_cast<void**>(observed);
34 if (head.compare_exchange_weak(observed, next, std::memory_order_acq_rel,
35 std::memory_order_acquire)) {
36 return observed;
37 }
38 }
39}
40
41} // namespace
42
43namespace helios::mem {
44
45void FixedPoolAllocator::MoveFrom(FixedPoolAllocator& other) noexcept {
46 block_size_ = std::exchange(other.block_size_, 0);
47 block_count_ = std::exchange(other.block_count_, 0);
48 alignment_ = std::exchange(other.alignment_, 0);
49 chunk_capacity_ = std::exchange(other.chunk_capacity_, 0);
50 buffer_ = std::exchange(other.buffer_, nullptr);
51 free_head_.store(
52 other.free_head_.exchange(nullptr, std::memory_order_acq_rel),
53 std::memory_order_release);
54 free_blocks_.store(other.free_blocks_.exchange(0, std::memory_order_acq_rel),
55 std::memory_order_release);
56 peak_used_blocks_.store(
57 other.peak_used_blocks_.exchange(0, std::memory_order_acq_rel),
58 std::memory_order_release);
59 total_allocations_.store(
60 other.total_allocations_.exchange(0, std::memory_order_acq_rel),
61 std::memory_order_release);
62 total_deallocations_.store(
63 other.total_deallocations_.exchange(0, std::memory_order_acq_rel),
64 std::memory_order_release);
65}
66
67void FixedPoolAllocator::Rebuild() noexcept {
68 free_head_.store(nullptr, std::memory_order_release);
69 for (size_t index = 0; index < block_count_; ++index) {
70 PushBlock(free_head_, buffer_ + index * block_size_);
71 }
72 free_blocks_.store(block_count_, std::memory_order_release);
73}
74
75void FixedPoolAllocator::Release() noexcept {
76 if (buffer_ != nullptr) {
77 HELIOS_MEMORY_PROFILE_FREE(buffer_, "FixedPoolAllocator");
78 AlignedFree(buffer_, false);
79 buffer_ = nullptr;
80 }
81}
82
83void* FixedPoolAllocator::do_allocate(size_t bytes, size_t alignment) {
84 HELIOS_MEMORY_PROFILE_SCOPE_N("helios::mem::FixedPoolAllocator::do_allocate");
85 HELIOS_MEMORY_PROFILE_ZONE_VALUE(bytes);
86
87 if (bytes == 0) [[unlikely]] {
88 return nullptr;
89 }
90
91 HELIOS_VERIFY(bytes <= block_size_,
92 "Requested size exceeds fixed pool block size!");
93 HELIOS_VERIFY(alignment <= alignment_ && IsPowerOfTwo(alignment),
94 "Requested alignment exceeds fixed pool alignment!");
95
96 void* const block = PopBlock(free_head_);
97 HELIOS_VERIFY(block != nullptr, "Fixed pool allocator exhausted!");
98
99 const size_t free = free_blocks_.fetch_sub(1) - 1;
100 const size_t used = block_count_ - free;
101 size_t peak = peak_used_blocks_.load(std::memory_order_relaxed);
102 while (used > peak && !peak_used_blocks_.compare_exchange_weak(
103 peak, used, std::memory_order_relaxed)) {
104 }
105
106 total_allocations_.fetch_add(1, std::memory_order_relaxed);
107 return block;
108}
109
110void FixedPoolAllocator::do_deallocate(void* ptr, size_t /*bytes*/,
111 size_t /*alignment*/) {
112 HELIOS_MEMORY_PROFILE_SCOPE_N(
113 "helios::mem::FixedPoolAllocator::do_deallocate");
114
115 if (ptr == nullptr) [[unlikely]] {
116 return;
117 }
118
119 HELIOS_ASSERT(Owns(ptr), "ptr does not belong to fixed pool!");
120 PushBlock(free_head_, ptr);
121 free_blocks_.fetch_add(1, std::memory_order_relaxed);
122 total_deallocations_.fetch_add(1, std::memory_order_relaxed);
123}
124
125} // namespace helios::mem
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:259
#define HELIOS_VERIFY(condition,...)
Verify macro that always checks the condition.
Definition assert.hpp:323
Fixed-capacity intrusive block pool.
bool Owns(const void *ptr) const noexcept
Checks pointer ownership.
void PushBlock(std::atomic< void * > &head, void *block) noexcept
void * PopBlock(std::atomic< void * > &head) noexcept
constexpr bool IsPowerOfTwo(size_t value) noexcept
Checks if a value is a power of two.
Definition common.hpp:215
void AlignedFree(void *ptr, bool enable_profile=true) noexcept
Frees memory allocated with AlignedAlloc.