| #pragma once
|
|
|
| #include <atomic>
|
| #include <cstddef>
|
| #include <cstdint>
|
| #include <memory>
|
| #include <type_traits>
|
| #include <iostream>
|
| #include <vector>
|
| #include <stdexcept>
|
|
|
| #ifdef _WIN32
|
| #include <Windows.h>
|
| #else
|
| #include <sys/mman.h>
|
| #include <unistd.h>
|
| #endif
|
|
|
| namespace hhb {
|
| namespace core {
|
|
|
|
|
| template <typename T>
|
| struct ObjectPoolConfig {
|
| static constexpr size_t DEFAULT_BLOCK_SIZE = 1024 * 1024;
|
| static constexpr size_t DEFAULT_OBJECTS_PER_BLOCK = 1024;
|
| };
|
|
|
|
|
|
|
| struct alignas(64) BlockHeader {
|
| std::atomic<BlockHeader*> next;
|
| std::atomic<size_t> used;
|
| size_t capacity;
|
| char data[];
|
| };
|
|
|
|
|
| struct FreeNode {
|
| std::atomic<FreeNode*> next;
|
| };
|
|
|
|
|
| template <typename T>
|
| class ObjectPool {
|
| public:
|
|
|
|
|
| ObjectPool(size_t objects_per_block = ObjectPoolConfig<T>::DEFAULT_OBJECTS_PER_BLOCK)
|
| : objects_per_block_(objects_per_block),
|
|
|
| block_size_(sizeof(BlockHeader) + sizeof(T) * objects_per_block),
|
| head_block_(nullptr),
|
| free_list_(nullptr) {
|
| try {
|
|
|
| allocate_block();
|
| } catch (const std::exception& e) {
|
| std::cerr << "Exception in ObjectPool constructor: " << e.what() << std::endl;
|
| throw;
|
| }
|
| }
|
|
|
|
|
| ~ObjectPool() {
|
|
|
| BlockHeader* block = head_block_;
|
| while (block) {
|
| BlockHeader* next = block->next;
|
| deallocate_block(block);
|
| block = next;
|
| }
|
| }
|
|
|
|
|
| ObjectPool(const ObjectPool&) = delete;
|
| ObjectPool& operator=(const ObjectPool&) = delete;
|
| ObjectPool(ObjectPool&&) = delete;
|
| ObjectPool& operator=(ObjectPool&&) = delete;
|
|
|
|
|
| T* allocate() {
|
|
|
| FreeNode* node = free_list_.load(std::memory_order_acquire);
|
| while (node) {
|
|
|
| FreeNode* next = node->next.load(std::memory_order_acquire);
|
|
|
| if (free_list_.compare_exchange_weak(node, next, std::memory_order_acq_rel, std::memory_order_acquire)) {
|
|
|
| return reinterpret_cast<T*>(node);
|
| }
|
| }
|
|
|
|
|
| BlockHeader* block = head_block_.load(std::memory_order_acquire);
|
| while (block) {
|
| size_t used = block->used.load(std::memory_order_acquire);
|
| while (used < block->capacity) {
|
| if (block->used.compare_exchange_weak(
|
| used, used + 1,
|
| std::memory_order_acq_rel,
|
| std::memory_order_acquire)) {
|
| return reinterpret_cast<T*>(
|
| reinterpret_cast<char*>(block) + sizeof(BlockHeader) + sizeof(T) * used);
|
| }
|
| }
|
| block = block->next.load(std::memory_order_acquire);
|
| }
|
|
|
|
|
| allocate_block();
|
| BlockHeader* head = head_block_.load(std::memory_order_acquire);
|
| while (true) {
|
| size_t used = head->used.load(std::memory_order_acquire);
|
| if (used >= head->capacity) {
|
| allocate_block();
|
| head = head_block_.load(std::memory_order_acquire);
|
| continue;
|
| }
|
| if (head->used.compare_exchange_weak(
|
| used, used + 1,
|
| std::memory_order_acq_rel,
|
| std::memory_order_acquire)) {
|
| return reinterpret_cast<T*>(
|
| reinterpret_cast<char*>(head) + sizeof(BlockHeader) + sizeof(T) * used);
|
| }
|
| }
|
| }
|
|
|
|
|
| void deallocate(T* ptr) {
|
|
|
| FreeNode* node = reinterpret_cast<FreeNode*>(ptr);
|
|
|
| FreeNode* old_head = free_list_.load(std::memory_order_acquire);
|
| do {
|
|
|
| node->next.store(old_head, std::memory_order_release);
|
|
|
| } while (!free_list_.compare_exchange_weak(old_head, node, std::memory_order_acq_rel, std::memory_order_acquire));
|
| }
|
|
|
|
|
| size_t size() const {
|
| size_t total = 0;
|
| BlockHeader* block = head_block_.load(std::memory_order_acquire);
|
| while (block) {
|
| total += block->used.load(std::memory_order_acquire);
|
| block = block->next.load(std::memory_order_acquire);
|
| }
|
| return total;
|
| }
|
|
|
|
|
| template <typename Func>
|
| void for_each(Func func) {
|
| BlockHeader* block = head_block_.load(std::memory_order_acquire);
|
| while (block) {
|
| size_t used = block->used.load(std::memory_order_acquire);
|
| for (size_t i = 0; i < used; ++i) {
|
| T* obj = reinterpret_cast<T*>(
|
| reinterpret_cast<char*>(block) + sizeof(BlockHeader) + sizeof(T) * i);
|
| func(obj);
|
| }
|
| block = block->next.load(std::memory_order_acquire);
|
| }
|
| }
|
|
|
|
|
| T& operator[](size_t index) {
|
| size_t current_index = 0;
|
| BlockHeader* block = head_block_.load(std::memory_order_acquire);
|
|
|
|
|
| std::vector<BlockHeader*> blocks;
|
| while (block) {
|
| blocks.push_back(block);
|
| block = block->next.load(std::memory_order_acquire);
|
| }
|
|
|
|
|
| for (auto it = blocks.rbegin(); it != blocks.rend(); ++it) {
|
| BlockHeader* b = *it;
|
| size_t used = b->used.load(std::memory_order_acquire);
|
| if (index < current_index + used) {
|
| size_t offset = index - current_index;
|
| return *reinterpret_cast<T*>(
|
| reinterpret_cast<char*>(b) + sizeof(BlockHeader) + sizeof(T) * offset);
|
| }
|
| current_index += used;
|
| }
|
|
|
| throw std::out_of_range("Index out of range");
|
| }
|
|
|
|
|
| const T& operator[](size_t index) const {
|
| return const_cast<ObjectPool*>(this)->operator[](index);
|
| }
|
|
|
| private:
|
|
|
| void allocate_block() {
|
| try {
|
|
|
| size_t actual_block_size = block_size_;
|
| std::cout << "Allocating block with size: " << actual_block_size << " bytes" << std::endl;
|
|
|
|
|
| void* memory = nullptr;
|
| #ifdef _WIN32
|
|
|
|
|
|
|
| std::cout << "Calling VirtualAlloc" << std::endl;
|
| memory = VirtualAlloc(nullptr, actual_block_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| std::cout << "VirtualAlloc returned: " << memory << std::endl;
|
| #else
|
|
|
|
|
|
|
| memory = mmap(nullptr, actual_block_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
| #endif
|
|
|
| if (!memory) {
|
| std::cerr << "Failed to allocate memory" << std::endl;
|
| throw std::bad_alloc();
|
| }
|
|
|
|
|
| BlockHeader* block = reinterpret_cast<BlockHeader*>(memory);
|
| block->used.store(0, std::memory_order_release);
|
| block->capacity = objects_per_block_;
|
|
|
| BlockHeader* old_head = head_block_.load(std::memory_order_acquire);
|
| do {
|
| block->next.store(old_head, std::memory_order_release);
|
| } while (!head_block_.compare_exchange_weak(old_head, block, std::memory_order_acq_rel, std::memory_order_acquire));
|
| std::cout << "Block allocated successfully" << std::endl;
|
| } catch (const std::exception& e) {
|
| std::cerr << "Exception in allocate_block: " << e.what() << std::endl;
|
| throw;
|
| }
|
| }
|
|
|
|
|
| void deallocate_block(BlockHeader* block) {
|
| #ifdef _WIN32
|
|
|
|
|
| VirtualFree(block, 0, MEM_RELEASE);
|
| #else
|
|
|
| munmap(block, block_size_);
|
| #endif
|
| }
|
|
|
| size_t objects_per_block_;
|
| size_t block_size_;
|
| std::atomic<BlockHeader*> head_block_;
|
| std::atomic<FreeNode*> free_list_;
|
| };
|
|
|
| }
|
| }
|
|
|