31 #if defined(__clang__) || defined(__GNUC__) 32 static __attribute__ ((destructor))
void s_gpool_destroy() {
52 Arena * next_arena, * prev_arena;
65 uint32_t num_slots()
const {
66 return static_cast<uint32_t
>(
67 (total_size -
sizeof(Arena)) /
sizeof(Slot));
70 Slot * begin() {
return &head_slot + 1; }
71 Slot * end() {
return &head_slot + 1 + num_slots(); }
72 Slot * slot(
size_t i) {
return &head_slot + 1 + i; }
74 void * find_free(
size_t size);
80 struct Pool::ObjectArena {
84 ObjectArena* next_arena, * prev_arena;
91 char * begin(
size_t num_flags) {
92 return reinterpret_cast<char*
>(flags + num_flags);
96 class Pool::ObjectPool
99 ObjectPool(
size_t size);
103 void AllocateObjectArena();
118 ObjectArena*
free_ =
nullptr;
120 ObjectArena* full_ =
nullptr;
122 static const size_t default_arena_size = 16384;
130 size_t total_slots_ = 0;
132 size_t total_free_ = 0;
135 Pool::ObjectPool::ObjectPool(
size_t size)
139 8 * (default_arena_size -
sizeof(ObjectArena) +
sizeof(
size_t))
143 num_flags_ = (num_slots_ + (8 *
sizeof(size_t) - 1)) / (8 *
sizeof(
size_t));
146 printf(
"ObjectPool() size_=%zu num_slots_=%zu num_flags_=%zu\n",
147 size_, num_slots_, num_flags_);
151 default_arena_size >=
153 sizeof(ObjectArena) -
sizeof(
size_t)
155 + num_flags_ *
sizeof(
size_t)
157 + num_slots_ *
size_);
160 Pool::ObjectPool::~ObjectPool() {
161 if (total_slots_ != total_free_) {
162 printf(
"~ObjectPool() size_=%zu total_used_=%zu\n",
163 size_, total_slots_ - total_free_);
167 void Pool::ObjectPool::AllocateObjectArena() {
169 ObjectArena* new_arena =
170 reinterpret_cast<ObjectArena*
>(
174 fprintf(stderr,
"out-of-memory - mem::ObjectPool cannot allocate a new ObjectArena." 175 " size_=%zu\n",
size_);
181 reinterpret_cast<ObjectArena*>(
182 reinterpret_cast<uintptr_t>(new_arena) & ~(default_arena_size - 1)));
184 new_arena->magic = 0xAEEA1111AEEA2222LLU +
size_;
185 new_arena->prev_arena =
nullptr;
186 new_arena->next_arena =
free_;
188 free_->prev_arena = new_arena;
191 new_arena->free_slots = num_slots_;
192 for (
size_t i = 0; i < num_flags_; ++i)
193 free_->flags[i] = ~
size_t(0);
195 total_slots_ += num_slots_;
196 total_free_ += num_slots_;
199 void* Pool::ObjectPool::allocate() {
201 printf(
"ObjectPool::allocate() size_=%zu\n",
size_);
205 while (
free_ ==
nullptr || total_free_ <= num_slots_)
206 AllocateObjectArena();
208 size_t slot = size_t(-1);
209 for (
size_t i = 0; i < num_flags_; ++i) {
212 slot = i * 8 *
sizeof(size_t) + (s - 1);
213 free_->flags[i] &= ~(size_t(1) << (s - 1));
218 void* ptr =
free_->begin(num_flags_) + slot *
size_;
220 if (--
free_->free_slots == 0)
222 ObjectArena* next_free =
free_->next_arena;
225 free_->next_arena = full_;
227 full_->prev_arena =
free_;
232 next_free->prev_arena =
nullptr;
240 void Pool::ObjectPool::deallocate(
void* ptr) {
242 printf(
"ObjectPool::deallocate() size_=%zu\n",
size_);
246 ObjectArena*
const arena =
247 reinterpret_cast<ObjectArena*
>(
248 reinterpret_cast<uintptr_t
>(ptr) & ~(default_arena_size - 1));
251 if (!(ptr >= arena->begin(num_flags_) &&
252 ptr < arena->begin(num_flags_) + num_slots_ *
size_)) {
253 assert(!
"deallocate() of memory not in any arena.");
259 (
reinterpret_cast<char*
>(ptr) - arena->begin(num_flags_)) /
size_;
261 size_t fa = slot / (8 *
sizeof(size_t));
262 size_t fb = slot % (8 *
sizeof(size_t));
263 size_t mask = (size_t(1) << fb);
267 arena->flags[fa] |= mask;
269 if (arena->free_slots == 0)
272 printf(
"ObjectPool::deallocate() splice free arena from full_ list\n");
275 if (arena->prev_arena)
276 arena->prev_arena->next_arena = arena->next_arena;
279 full_ = arena->next_arena;
281 if (arena->next_arena)
282 arena->next_arena->prev_arena = arena->prev_arena;
286 free_->prev_arena = arena;
287 arena->next_arena =
free_;
288 arena->prev_arena =
nullptr;
295 if (arena->free_slots == num_slots_ && total_free_ > 16 * num_slots_)
298 printf(
"ObjectPool::deallocate() splice empty arena from free_ list\n");
301 if (arena->prev_arena)
302 arena->prev_arena->next_arena = arena->next_arena;
305 free_ = arena->next_arena;
307 if (arena->next_arena)
308 arena->next_arena->prev_arena = arena->prev_arena;
311 total_free_ -= num_slots_;
312 total_slots_ -= num_slots_;
316 void Pool::ObjectPool::self_verify() {
318 printf(
"ObjectPool::print() size_=%zu\n",
size_);
321 size_t total_slots = 0, total_free = 0, total_used = 0;
323 for (ObjectArena* arena =
free_; arena !=
nullptr;
324 arena = arena->next_arena)
326 size_t arena_free = 0;
328 for (
size_t i = 0; i < num_slots_; ++i) {
329 size_t fa = i / (8 *
sizeof(size_t));
330 size_t fb = i % (8 *
sizeof(size_t));
332 if ((arena->flags[fa] & (
size_t(1) << fb)) == 0) {
344 total_slots += num_slots_;
346 if (arena->next_arena)
347 die_unless(arena->next_arena->prev_arena == arena);
348 if (arena->prev_arena)
349 die_unless(arena->prev_arena->next_arena == arena);
352 for (ObjectArena* arena = full_; arena !=
nullptr;
353 arena = arena->next_arena)
355 size_t arena_free = 0;
357 for (
size_t i = 0; i < num_slots_; ++i) {
358 size_t fa = i / (8 *
sizeof(size_t));
359 size_t fb = i % (8 *
sizeof(size_t));
361 if ((arena->flags[fa] & (
size_t(1) << fb)) == 0) {
373 total_slots += num_slots_;
375 if (arena->next_arena)
376 die_unless(arena->next_arena->prev_arena == arena);
377 if (arena->prev_arena)
378 die_unless(arena->prev_arena->next_arena == arena);
383 die_unequal(total_used, total_slots_ - total_free_);
402 return (
size_t(1) << (bin - 1));
410 std::unique_lock<std::mutex> lock(
mutex_);
412 for (
size_t i = 0; i <
num_bins + 1; ++i)
428 std::unique_lock<std::mutex> lock(
mutex_);
430 std::cout <<
"~Pool() pool still contains " 431 <<
sizeof(Slot) *
size_ <<
" bytes" << std::endl;
433 for (
size_t i = 0; i <
allocs_.size(); ++i) {
434 if (
allocs_[i].first ==
nullptr)
continue;
435 std::cout <<
"~Pool() has ptr=" <<
allocs_[i].first
436 <<
" size=" <<
allocs_[i].second << std::endl;
451 Slot* prev_slot = &arena->head_slot;
452 Slot* curr_slot = arena->begin() + prev_slot->next;
454 while (curr_slot != arena->end() && curr_slot->size < n) {
455 prev_slot = curr_slot;
456 curr_slot = arena->begin() + curr_slot->next;
463 arena->free_size -= n;
465 prev_slot->next += n;
469 if (curr_slot->size > n) {
471 Slot* next_slot = arena->begin() + prev_slot->next;
472 assert(next_slot != arena->end());
474 next_slot->size = curr_slot->size - n;
475 next_slot->next = curr_slot->next;
479 prev_slot->next = curr_slot->next;
488 std::cout <<
"Recategorize arena, previous free " 489 << arena->free_size + n
490 <<
" now free " << arena->free_size
491 <<
" from bin " << bin
492 <<
" to bin " << new_bin
495 assert(bin != new_bin);
498 if (arena->prev_arena)
499 arena->prev_arena->next_arena = arena->next_arena;
503 if (arena->next_arena)
504 arena->next_arena->prev_arena = arena->prev_arena;
507 arena->prev_arena =
nullptr;
521 for (i = 0; i <
allocs_.size(); ++i) {
522 if (
allocs_[i].first ==
nullptr) {
529 assert(!
"Increase allocs array in Pool().");
534 return reinterpret_cast<void*
>(curr_slot);
540 std::cout <<
"AllocateFreeArena()" 541 <<
" arena_size=" << arena_size
542 <<
" die_on_failure=" << die_on_failure << std::endl;
547 reinterpret_cast<Arena*
>(
550 if (!die_on_failure)
return nullptr;
551 fprintf(stderr,
"out-of-memory - mem::Pool cannot allocate a new Arena." 552 " size_=%zu\n",
size_);
558 reinterpret_cast<Arena*>(
561 new_arena->magic = 0xAEEAAEEAAEEAAEEALLU;
562 new_arena->total_size = arena_size;
570 new_arena->oversize =
false;
574 new_arena->oversize =
true;
577 new_arena->prev_arena =
nullptr;
578 new_arena->next_arena = *root;
580 (*root)->prev_arena = new_arena;
583 new_arena->free_size = new_arena->num_slots();
584 new_arena->head_slot.next = 0;
586 new_arena->slot(0)->size = new_arena->num_slots();
587 new_arena->slot(0)->next = new_arena->num_slots();
589 free_ += new_arena->num_slots();
592 reinterpret_cast<Arena*
>(
594 die_unless(check_arena->magic == 0xAEEAAEEAAEEAAEEALLU);
600 std::unique_lock<std::mutex> lock(
mutex_);
605 for (
size_t i = 0; i <=
num_bins; ++i) {
607 while (curr_arena !=
nullptr) {
608 Arena* next_arena = curr_arena->next_arena;
610 curr_arena = next_arena;
621 return arena_size -
sizeof(Arena);
627 std::unique_lock<std::mutex> lock(
mutex_);
630 std::cout <<
"Pool::allocate() bytes " << bytes
645 static_cast<uint32_t
>((bytes +
sizeof(Slot) - 1) /
sizeof(Slot));
651 std::cout <<
"Allocate overflow arena of size " 652 << n *
sizeof(Slot) << std::endl;
666 std::cout <<
"Searching in bin " << bin << std::endl;
670 while (curr_arena !=
nullptr)
673 if (curr_arena->free_size >= n)
681 curr_arena = curr_arena->next_arena;
697 die(
"Pool::allocate() failed, no memory available.");
703 if (ptr ==
nullptr)
return;
705 std::unique_lock<std::mutex> lock(
mutex_);
707 std::cout <<
"Pool::deallocate() ptr " << ptr
708 <<
" bytes " << bytes << std::endl;
713 for (i = 0; i <
allocs_.size(); ++i) {
714 if (
allocs_[i].first != ptr)
continue;
715 if (bytes !=
allocs_[i].second) {
716 assert(!
"Mismatching deallocate() size in Pool().");
724 assert(!
"Unknown deallocate() in Pool().");
740 =
static_cast<uint32_t
>((bytes +
sizeof(Slot) - 1) /
sizeof(Slot));
745 reinterpret_cast<Arena*
>(
747 die_unless(arena->magic == 0xAEEAAEEAAEEAAEEALLU);
749 if (!(ptr >= arena->begin() && ptr < arena->end())) {
750 assert(!
"deallocate() of memory not in any arena.");
754 Slot* prev_slot = &arena->head_slot;
755 Slot* ptr_slot =
reinterpret_cast<Slot*
>(ptr);
758 while (arena->begin() + prev_slot->next < ptr_slot) {
759 prev_slot = arena->begin() + prev_slot->next;
763 ptr_slot->next = prev_slot->next;
766 prev_slot->next =
static_cast<uint32_t
>(ptr_slot - arena->begin());
769 if (prev_slot == &arena->head_slot)
770 prev_slot = arena->begin() + arena->head_slot.next;
772 while (prev_slot->next != arena->num_slots() &&
773 prev_slot->next == prev_slot - arena->begin() + prev_slot->size)
776 Slot* next_slot = arena->begin() + prev_slot->next;
777 prev_slot->size += next_slot->size;
778 prev_slot->next = next_slot->next;
781 arena->free_size += n;
789 std::cout <<
"destroy special arena" << std::endl;
792 if (arena->prev_arena)
793 arena->prev_arena->next_arena = arena->next_arena;
797 if (arena->next_arena)
798 arena->next_arena->prev_arena = arena->prev_arena;
800 free_ -= arena->num_slots();
807 if (arena->free_size == arena->num_slots() &&
811 std::cout <<
"destroy empty arena" << std::endl;
816 if (arena->prev_arena)
817 arena->prev_arena->next_arena = arena->next_arena;
821 if (arena->next_arena)
822 arena->next_arena->prev_arena = arena->prev_arena;
825 free_ -= arena->num_slots();
835 std::cout <<
"recategorize arena into larger chain." << std::endl;
841 std::cout <<
"Recategorize arena, previous free " 843 <<
" now free " << arena->free_size + n
844 <<
" from bin " << bin
845 <<
" to bin " << new_bin
850 if (arena->prev_arena)
851 arena->prev_arena->next_arena = arena->next_arena;
855 if (arena->next_arena)
856 arena->next_arena->prev_arena = arena->prev_arena;
859 arena->prev_arena =
nullptr;
869 std::cout <<
"Pool::print()" 870 <<
" size_=" <<
size_ <<
" free_=" <<
free_ << std::endl;
873 size_t total_free = 0, total_size = 0;
875 for (
size_t bin = 0; bin <
num_bins; ++bin)
877 for (Arena* curr_arena =
arena_bin_[bin]; curr_arena !=
nullptr;
878 curr_arena = curr_arena->next_arena)
880 std::ostringstream oss;
885 size_t slot = curr_arena->head_slot.next;
886 size_t size = 0,
free = 0;
891 while (slot != curr_arena->num_slots()) {
893 oss <<
" slot[" << slot
894 <<
",size=" << curr_arena->slot(slot)->size
895 <<
",next=" << curr_arena->slot(slot)->next <<
']';
897 if (curr_arena->slot(slot)->next <= slot) {
898 std::cout <<
"invalid chain:" << oss.str() << std::endl;
902 free += curr_arena->slot(slot)->size;
903 size += curr_arena->slot(slot)->next - slot - curr_arena->slot(slot)->size;
904 slot = curr_arena->slot(slot)->next;
908 std::cout <<
"arena[" << bin <<
':' << curr_arena <<
"]" 909 <<
" free_size=" << curr_arena->free_size
910 <<
" head_slot.next=" << curr_arena->head_slot.next
920 if (curr_arena->next_arena)
921 die_unless(curr_arena->next_arena->prev_arena == curr_arena);
922 if (curr_arena->prev_arena)
923 die_unless(curr_arena->prev_arena->next_arena == curr_arena);
size_t max_size() const noexcept
maximum size possible to allocate
static uint_pair max()
return an uint_pair instance containing the largest value possible
static unsigned ffs(int i)
find first set bit in integer, or zero if none are set.
ObjectPool * object_32_
object areas for small fixed size items
std::vector< std::pair< void *, size_t > > allocs_
array of allocations for checking
void * bypass_aligned_alloc(size_t alignment, size_t size) noexcept
bypass malloc tracker and access aligned_alloc() directly
static unsigned integer_log2_floor_template(IntegerType i)
calculate the log2 floor of an integer type
static size_t bin_lower_bound(size_t bin)
lowest size still in bin
#define die(msg)
Instead of std::terminate(), throw the output the message via an exception.
void bypass_aligned_free(void *ptr, size_t size) noexcept
bypass malloc tracker and access aligned_alloc() directly
size_t bytes_per_arena(size_t arena_size)
calculate maximum bytes fitting into an Arena with given size.
void self_verify()
Print out structure of the arenas.
static constexpr bool debug_check_pairing
debug flag to check pairing of allocate()/deallocate() client calls
A simple memory allocation manager.
void * ArenaFindFree(Arena *curr_arena, size_t bin, size_t n, size_t bytes)
find free area inside an Arena
Arena * AllocateFreeArena(size_t arena_size, bool die_on_failure=true)
allocate a new Arena blob
size_t min_free_
minimum amount of spare memory to keep in the Pool.
void * allocate(size_t bytes)
allocate a continuous segment of n bytes in the arenas
Pool & GPool()
singleton instance of global pool for I/O data structures
#define die_unequal(X, Y)
static size_t calc_bin_for_size(size_t size)
determine bin for size.
void print(bool debug=true)
Print out structure of the arenas.
Pool(size_t default_arena_size=16384) noexcept
construct with base allocator
void IntDeallocateAll()
deallocate all Arenas
static const size_t bytes
number of bytes in uint_pair
size_t free_
number of free slots in all arenas
void DeallocateAll()
deallocate all Arenas
void free(void *ptr) NOEXCEPT
exported free symbol that overrides loading from libc
static constexpr bool debug
size_t size_
overall number of used slots
Arena * arena_bin_[num_bins+1]
static constexpr size_t check_limit
size_t default_arena_size_
size of default Arena allocation
void deallocate(void *ptr, size_t bytes)
static const size_t num_bins
number of bins