CppCommon  1.0.4.1
C++ Common Library
allocator_pool.inl
Go to the documentation of this file.
1 
9 namespace CppCommon {
10 
11 template <class TAuxMemoryManager>
12 inline PoolMemoryManager<TAuxMemoryManager>::PoolMemoryManager(TAuxMemoryManager& auxiliary, size_t page, size_t pages)
13  : _allocated(0),
14  _allocations(0),
15  _auxiliary(auxiliary),
16  _external(false),
17  _max_pages(0),
18  _pages(0),
19  _page(0),
20  _current(nullptr),
21  _free_block(nullptr)
22 {
23  reset(page, pages);
24 }
25 
26 template <class TAuxMemoryManager>
27 inline PoolMemoryManager<TAuxMemoryManager>::PoolMemoryManager(TAuxMemoryManager& auxiliary, void* buffer, size_t capacity)
28  : _allocated(0),
29  _allocations(0),
30  _auxiliary(auxiliary),
31  _external(false),
32  _max_pages(0),
33  _pages(0),
34  _page(0),
35  _current(nullptr),
36  _free_block(nullptr)
37 {
38  reset(buffer, capacity);
39 }
40 
41 template <class TAuxMemoryManager>
42 inline void* PoolMemoryManager<TAuxMemoryManager>::malloc(size_t size, size_t alignment)
43 {
44  assert((size > 0) && "Allocated block size must be greater than zero!");
45  assert(Memory::IsValidAlignment(alignment) && "Alignment must be valid!");
46 
47  // Allocate huge blocks using the auxiliary memory manager
48  if (size > _page)
49  {
50  void* result = _auxiliary.malloc(size, alignment);
51  if (result != nullptr)
52  {
53  // Update allocation statistics
54  _allocated += size;
55  ++_allocations;
56  }
57  return result;
58  }
59 
60  bool allocate = true;
61  FreeBlock* prev_free_block = nullptr;
62  FreeBlock* current_free_block = _free_block;
63 
64  while ((current_free_block != nullptr) || allocate)
65  {
66  // Try to allocate a new memory pool page
67  if ((current_free_block == nullptr) && allocate)
68  {
69  if ((_current != nullptr) && (_current->next != nullptr))
70  {
71  // Use the next memory pool page
72  _current = _current->next;
73 
74  // Update the current free block
75  current_free_block = (FreeBlock*)_current->buffer;
76  current_free_block->size = _page;
77  current_free_block->next = nullptr;
78 
79  // Update the free blocks list
80  if (prev_free_block != nullptr)
81  prev_free_block->next = current_free_block;
82  else
83  _free_block = current_free_block;
84  }
85  else
86  {
87  // Check if there is enough pages to create
88  if (_pages > 0)
89  {
90  // Allocate a new memory pool page
91  Page* current = AllocateMemoryPool(_page, _current);
92  if (current != nullptr)
93  {
94  // Use the new memory pool page
95  _current = current;
96 
97  // Update the current free block
98  current_free_block = (FreeBlock*)_current->buffer;
99  current_free_block->size = _page;
100  current_free_block->next = nullptr;
101 
102  // Update the free blocks list
103  if (prev_free_block != nullptr)
104  prev_free_block->next = current_free_block;
105  else
106  _free_block = current_free_block;
107 
108  // Update created memory pool pages count
109  --_pages;
110  }
111  }
112  }
113 
114  // Stop the allocation attempts
115  allocate = false;
116  continue;
117  }
118 
119  // Reset the allocation attempts
120  allocate = true;
121 
122  // Calculate memory adjustment including the allocation header
123  size_t adjustment = AlignAdjustment(current_free_block, alignment, sizeof(AllocBlock));
124 
125  // Calculate aligned block size
126  size_t aligned_size = size + adjustment;
127 
128  // Concatenate joint free blocks
129  while (current_free_block->next != nullptr)
130  {
131  FreeBlock* base_free_block = current_free_block;
132  FreeBlock* next_free_block = current_free_block->next;
133 
134  // Right joint
135  if (((uint8_t*)base_free_block + base_free_block->size) == (uint8_t*)next_free_block)
136  {
137  current_free_block->size += next_free_block->size;
138  current_free_block->next = next_free_block->next;
139  }
140  else
141  break;
142  }
143 
144  // If there is no enough free space in the current free block use the next one
145  if (current_free_block->size < aligned_size)
146  {
147  // Optimization: Skip the last too small peace of memory in page
148  if (current_free_block->next != nullptr)
149  prev_free_block = current_free_block;
150  current_free_block = current_free_block->next;
151  continue;
152  }
153 
154  static_assert(sizeof(AllocBlock) >= sizeof(FreeBlock), "Allocated block structure size must not be less than free block structure size!");
155 
156  // Allocate from the remaining memory in the current free block
157  if ((current_free_block->size - aligned_size) <= sizeof(AllocBlock))
158  {
159  //Increase allocation size instead of creating a new free block
160  aligned_size = current_free_block->size;
161 
162  // Update the free blocks list
163  if (prev_free_block != nullptr)
164  prev_free_block->next = current_free_block->next;
165  else
166  _free_block = current_free_block->next;
167  }
168  else
169  {
170  // Create a new free block containing remaining memory
171  FreeBlock* next_free_block = (FreeBlock*)((uint8_t*)current_free_block + aligned_size);
172  next_free_block->size = current_free_block->size - aligned_size;
173  next_free_block->next = current_free_block->next;
174 
175  // Update the free blocks list
176  if (prev_free_block != nullptr)
177  prev_free_block->next = next_free_block;
178  else
179  _free_block = next_free_block;
180  }
181 
182  // Calculate the aligned address
183  uint8_t* aligned = (uint8_t*)current_free_block + adjustment;
184 
185  // Create a new allocated block
186  AllocBlock* alloc_block = (AllocBlock*)(aligned - sizeof(AllocBlock));
187  alloc_block->size = aligned_size;
188  alloc_block->adjustment = adjustment;
189 
190  // Update allocation statistics
191  _allocated += size;
192  ++_allocations;
193 
194  return aligned;
195  }
196 
197  // Out of memory...
198  return nullptr;
199 }
200 
201 template <class TAuxMemoryManager>
202 inline void PoolMemoryManager<TAuxMemoryManager>::free(void* ptr, size_t size)
203 {
204  assert((ptr != nullptr) && "Deallocated block must be valid!");
205 
206  // Deallocate huge blocks using the auxiliary memory manager
207  if (size > _page)
208  {
209  _auxiliary.free(ptr, size);
210 
211  // Update allocation statistics
212  _allocated -= size;
213  --_allocations;
214 
215  return;
216  }
217 
218  AllocBlock* alloc_block = (AllocBlock*)((uint8_t*)ptr - sizeof(AllocBlock));
219 
220  uint8_t* block_start = (uint8_t*)ptr - alloc_block->adjustment;
221  size_t block_size = alloc_block->size;
222  uint8_t* block_end = block_start + block_size;
223 
224  // Insert a new free block in the begin of the list
225  FreeBlock* old_free_block = _free_block;
226  FreeBlock* new_free_block = (FreeBlock*)block_start;
227  new_free_block->size = block_size;
228  new_free_block->next = old_free_block;
229  _free_block = new_free_block;
230 
231  // Concatenate two joint free blocks
232  if (old_free_block != nullptr)
233  {
234  // Left joint
235  if (((uint8_t*)old_free_block + old_free_block->size) == block_start)
236  {
237  old_free_block->size += new_free_block->size;
238  _free_block = old_free_block;
239  }
240  // Right joint
241  else if ((uint8_t*)old_free_block == block_end)
242  {
243  new_free_block->size += old_free_block->size;
244  new_free_block->next = old_free_block->next;
245  _free_block = new_free_block;
246  }
247  }
248 
249  // Update allocation statistics
250  _allocated -= size;
251  --_allocations;
252 }
253 
254 template <class TAuxMemoryManager>
256 {
257  assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
258  assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
259 
260  if (_current != nullptr)
261  {
262  // Reset the current memory pool page
263  while (_current->prev != nullptr)
264  _current = _current->prev;
265 
266  // Reset the free block pointer
267  _free_block = (FreeBlock*)_current->buffer;
268  _free_block->size = _page;
269  _free_block->next = nullptr;
270  }
271 }
272 
273 template <class TAuxMemoryManager>
274 inline void PoolMemoryManager<TAuxMemoryManager>::reset(size_t page, size_t pages)
275 {
276  assert((page >= (sizeof(Page) + sizeof(AllocBlock))) && "Memory pool page must be big enough to fit at least one allocation block!");
277 
278  assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
279  assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
280 
281  // Clear previous allocations
282  clear();
283 
284  // Initialize the memory pool
285  _external = false;
286  _max_pages = (pages > 0) ? pages : std::numeric_limits<size_t>::max();
287  _pages = _max_pages;
288  _page = page;
289 
290  // Allocate a new memory pool page
291  Page* current = AllocateMemoryPool(page, _current);
292  if (current != nullptr)
293  {
294  // Use the new memory pool page
295  _current = current;
296 
297  // Initialize the free block pointer
298  _free_block = (FreeBlock*)_current->buffer;
299  _free_block->size = page;
300  _free_block->next = nullptr;
301 
302  // Update created memory pool pages count
303  --_pages;
304  }
305 }
306 
307 template <class TAuxMemoryManager>
308 inline void PoolMemoryManager<TAuxMemoryManager>::reset(void* buffer, size_t capacity)
309 {
310  assert((buffer != nullptr) && "Memory pool buffer must be valid!");
311  assert((capacity >= (sizeof(Page) + sizeof(AllocBlock))) && "Memory pool buffer capacity must be big enough to fit at least one allocation block!");
312 
313  assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
314  assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
315 
316  // Clear previous allocations
317  clear();
318 
319  // Initialize the external memory pool
320  _external = true;
321  _max_pages = 1;
322  _pages = 0;
323  _page = capacity;
324  _current = (Page*)buffer;
325  _current->buffer = (uint8_t*)buffer + sizeof(Page);
326  _current->prev = nullptr;
327  _current->next = nullptr;
328 
329  // Initialize the free block pointer
330  _free_block = (FreeBlock*)_current->buffer;
331  _free_block->size = capacity - sizeof(Page);
332  _free_block->next = nullptr;
333 }
334 
335 template <class TAuxMemoryManager>
337 {
338  assert((_allocated == 0) && "Memory leak detected! Allocated memory size must be zero!");
339  assert((_allocations == 0) && "Memory leak detected! Count of active memory allocations must be zero!");
340 
341  // Clear memory pool
342  ClearMemoryPool();
343 
344  // Clear memory pool buffer
345  _external = false;
346  _max_pages = 0;
347  _pages = 0;
348  _page = 0;
349  _current = nullptr;
350 
351  // Reset the free block pointer
352  _free_block = nullptr;
353 }
354 
355 template <class TAuxMemoryManager>
356 inline size_t PoolMemoryManager<TAuxMemoryManager>::AlignAdjustment(const void* address, size_t alignment)
357 {
358  return (uint8_t*)Memory::Align(address, alignment) - (uint8_t*)address;
359 }
360 
361 template <class TAuxMemoryManager>
362 inline size_t PoolMemoryManager<TAuxMemoryManager>::AlignAdjustment(const void* address, size_t alignment, size_t header)
363 {
364  size_t adjustment = AlignAdjustment(address, alignment);
365 
366  size_t required = header;
367  if (adjustment < required)
368  {
369  required -= adjustment;
370  adjustment += alignment * (required / alignment);
371  if ((required % alignment) > 0)
372  adjustment += alignment;
373  }
374 
375  return adjustment;
376 }
377 
378 template <class TAuxMemoryManager>
379 inline typename PoolMemoryManager<TAuxMemoryManager>::Page* PoolMemoryManager<TAuxMemoryManager>::AllocateMemoryPool(size_t capacity, Page* prev)
380 {
381  // Allocate a new memory pool page
382  uint8_t* buffer = (uint8_t*)_auxiliary.malloc(sizeof(Page) + capacity + alignof(std::max_align_t));
383  Page* page = (Page*)buffer;
384  if (page != nullptr)
385  {
386  // Prepare and return a new memory pool page
387  page->buffer = buffer + sizeof(Page);
388  page->prev = prev;
389  page->next = nullptr;
390  if (prev != nullptr)
391  prev->next = page;
392  return page;
393  }
394 
395  // Out of memory...
396  return nullptr;
397 }
398 
399 template <class TAuxMemoryManager>
400 inline void PoolMemoryManager<TAuxMemoryManager>::ClearMemoryPool()
401 {
402  if (!_external)
403  {
404  // Clear all memory pool pages
405  while (_current != nullptr)
406  {
407  Page* prev = _current->prev;
408  Page* next = _current->next;
409  _auxiliary.free(_current, sizeof(Page) + _page + alignof(std::max_align_t));
410  if (prev != nullptr)
411  {
412  prev->next = next;
413  _current = prev;
414  }
415  else if (next != nullptr)
416  {
417  next->prev = prev;
418  _current = next;
419  }
420  else
421  _current = nullptr;
422  }
423 
424  // Reset the free block pointer
425  _free_block = nullptr;
426  }
427 }
428 
429 } // namespace CppCommon
static bool IsValidAlignment(size_t alignment) noexcept
Is the given alignment valid?
Definition: memory.inl:13
static T * Align(const T *address, size_t alignment=alignof(T), bool upwards=true) noexcept
Align pointer (upwards or downwards)
Definition: memory.inl:29
Memory pool manager class.
size_t pages() const noexcept
Max pages size.
void reset()
Reset the memory manager.
PoolMemoryManager(TAuxMemoryManager &auxiliary)
Initialize memory pool manager with an auxiliary memory manager.
void * malloc(size_t size, size_t alignment=alignof(std::max_align_t))
Allocate a new memory block of the given size.
void clear()
Clear memory pool.
size_t page() const noexcept
Page size in bytes.
void free(void *ptr, size_t size)
Free the previously allocated memory block.
C++ Common project definitions.
Definition: token_bucket.h:15