1 // license:GPL-2.0+ 2 // copyright-holders:Couriersud 3 4 #ifndef PMEMPOOL_H_ 5 #define PMEMPOOL_H_ 6 7 /// 8 /// \file pmempool.h 9 /// 10 11 #include "palloc.h" 12 #include "pconfig.h" 13 #include "pstream.h" // perrlogger 14 //#include "pstring.h" 15 #include "ptypes.h" 16 //#include "putil.h" 17 18 #include <algorithm> 19 #include <memory> 20 #include <unordered_map> 21 #include <utility> 22 #include <vector> 23 24 namespace plib { 25 26 //============================================================ 27 // Memory pool 28 //============================================================ 29 30 template <typename BASEARENA, std::size_t MINALIGN = PALIGN_MIN_SIZE> 31 class mempool_arena : public arena_base<mempool_arena<BASEARENA, MINALIGN>, false, false> 32 { 33 public: 34 35 using size_type = typename BASEARENA::size_type; 36 using base_type = arena_base<mempool_arena<BASEARENA, MINALIGN>, false, false>; 37 template <class T> 38 using base_allocator_type = typename BASEARENA::template allocator_type<T>; 39 40 mempool_arena(size_t min_align = MINALIGN, size_t min_alloc = (1<<21)) m_min_alloc(min_alloc)41 : m_min_alloc(min_alloc) 42 , m_min_align(min_align) 43 , m_block_align(1024) 44 , m_blocks(base_allocator_type<block *>(m_arena)) 45 { 46 } 47 PCOPYASSIGNMOVE(mempool_arena,delete)48 PCOPYASSIGNMOVE(mempool_arena, delete) 49 50 ~mempool_arena() 51 { 52 for (auto & b : m_blocks) 53 { 54 if (b->m_num_alloc != 0) 55 { 56 plib::perrlogger("Found {} info blocks\n", m_info.size()); 57 plib::perrlogger("Found block with {} dangling allocations\n", b->m_num_alloc); 58 } 59 detail::free(m_arena, b); 60 } 61 if (!m_info.empty()) 62 plib::perrlogger("Still found {} info blocks after mempool deleted\n", m_info.size()); 63 } 64 allocate(size_t align,size_t size)65 void *allocate(size_t align, size_t size) 66 { 67 block *b = nullptr; 68 69 if (align < m_min_align) 70 align = m_min_align; 71 72 size_t rs = size + align; 73 74 for (auto &bs : m_blocks) 75 { 76 if (bs->m_free > rs) 77 { 78 b = bs; 79 break; 80 } 81 } 82 if (b == nullptr) 83 { 84 b = new_block(rs); 85 } 86 b->m_free -= rs; 87 b->m_num_alloc++; 88 void *ret = reinterpret_cast<void *>(b->m_data + b->m_cur); // NOLINT(cppcoreguidelines-pro-type-reinterpret 89 auto capacity(rs); 90 ret = std::align(align, size, ret, capacity); 91 m_info.insert({ ret, info(b, b->m_cur)}); 92 rs -= (capacity - size); 93 b->m_cur += rs; 94 this->inc_alloc_stat(size); 95 96 return ret; 97 } 98 deallocate(void * ptr,size_t size)99 void deallocate(void *ptr, size_t size) noexcept 100 { 101 auto it = m_info.find(ptr); 102 if (it == m_info.end()) 103 plib::terminate("mempool::free - pointer not found"); 104 block *b = it->second.m_block; 105 if (b->m_num_alloc == 0) 106 plib::terminate("mempool::free - double free was called"); 107 else 108 { 109 mempool_arena &mp = b->m_mempool; 110 b->m_num_alloc--; 111 mp.dec_alloc_stat(size); 112 if (b->m_num_alloc == 0) 113 { 114 auto itb = std::find(mp.m_blocks.begin(), mp.m_blocks.end(), b); 115 if (itb == mp.m_blocks.end()) 116 plib::terminate("mempool::free - block not found"); 117 118 mp.m_blocks.erase(itb); 119 detail::free(mp.base_arena(), b); 120 } 121 m_info.erase(it); 122 } 123 } 124 125 bool operator ==(const mempool_arena &rhs) const noexcept { return this == &rhs; } 126 base_arena()127 BASEARENA &base_arena() noexcept { return m_arena; } 128 private: 129 struct block 130 { blockblock131 block(mempool_arena &mp, size_type min_bytes) 132 : m_num_alloc(0) 133 , m_cur(0) 134 , m_data(nullptr) 135 , m_mempool(mp) 136 { 137 min_bytes = std::max(mp.m_min_alloc, min_bytes); 138 m_free = min_bytes; 139 m_bytes_allocated = (min_bytes + mp.m_block_align); // - 1); // & ~(mp.m_min_align - 1); 140 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) 141 //m_data_allocated = new std::uint8_t[alloc_bytes]; 142 m_data_allocated = static_cast<std::uint8_t *>(mp.base_arena().allocate(mp.m_block_align, m_bytes_allocated)); 143 void *r = m_data_allocated; 144 std::align(mp.m_block_align, min_bytes, r, m_bytes_allocated); 145 m_data = reinterpret_cast<std::uint8_t *>(r); // NOLINT(cppcoreguidelines-pro-type-reinterpret 146 } ~blockblock147 ~block() 148 { 149 //::operator delete(m_data_allocated); 150 //delete [] m_data_allocated; 151 m_mempool.base_arena().deallocate(m_data_allocated, m_bytes_allocated); 152 } 153 154 block(const block &) = delete; 155 block(block &&) = delete; 156 block &operator =(const block &) = delete; 157 block &operator =(block &&) = delete; 158 159 size_type m_num_alloc; 160 size_type m_free; 161 size_type m_cur; 162 size_type m_bytes_allocated; 163 std::uint8_t *m_data; 164 std::uint8_t *m_data_allocated; 165 mempool_arena &m_mempool; 166 }; 167 168 struct info 169 { infoinfo170 info(block *b, size_type p) : m_block(b), m_pos(p) { } 171 info(const info &) = default; 172 info &operator=(const info &) = default; 173 info(info &&) noexcept = default; 174 info &operator=(info &&) noexcept = default; 175 ~info() = default; 176 177 block * m_block; 178 size_type m_pos; 179 }; 180 new_block(size_type min_bytes)181 block * new_block(size_type min_bytes) 182 { 183 auto *b = detail::alloc<block>(m_arena, *this, min_bytes); 184 m_blocks.push_back(b); 185 return b; 186 } 187 188 size_t m_min_alloc; 189 size_t m_min_align; 190 size_t m_block_align; 191 BASEARENA m_arena; 192 193 using base_allocator_typex = typename BASEARENA::template allocator_type<std::pair<void * const, info>>; 194 std::unordered_map<void *, info, std::hash<void *>, std::equal_to<void *>, 195 base_allocator_typex> m_info; 196 // std::unordered_map<void *, info> m_info; 197 std::vector<block *, typename BASEARENA::template allocator_type<block *>> m_blocks; 198 199 }; 200 201 } // namespace plib 202 203 #endif // PMEMPOOL_H_ 204