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