#include #include #include #include #include namespace modern { // Functors namespace (similar to ObjectPoolFunctors) struct ObjectPoolFunctors { using DefaultCreator = std::function; template class Nil { public: void operator()(TYPE *) const {} }; template class Reset { public: void operator()(TYPE *object) const { object->reset(); } }; template class Clear { public: void operator()(TYPE *object) const { object->clear(); } }; }; // Default creator implementation template class DefaultCreator { private: std::pmr::polymorphic_allocator allocator_; public: explicit DefaultCreator( std::pmr::memory_resource *mr = std::pmr::get_default_resource()) : allocator_(mr) {} void operator()(void *arena, std::pmr::memory_resource *mr) { if constexpr (std::uses_allocator_v< TYPE, std::pmr::polymorphic_allocator>) { // TYPE uses allocator - pass it to constructor new (arena) TYPE(std::pmr::polymorphic_allocator(mr)); } else { // TYPE doesn't use allocator - default construct new (arena) TYPE{}; } } }; // Main ObjectPool class template , class RESETTER = ObjectPoolFunctors::Nil> class ObjectPool { private: // Node structure (similar to Bloomberg's design) union ObjectNode { struct { std::atomic next_p; std::atomic refCount; } inUse; alignas(TYPE) char dummy[sizeof(TYPE)]; // Ensure proper alignment }; // Block structure for batch allocation struct BlockNode { BlockNode *next_p; int numObjects; }; // Memory layout constants static constexpr int k_ROUNDED_NUM_OBJECTS = (sizeof(TYPE) + sizeof(ObjectNode) - 1) / sizeof(ObjectNode); static constexpr int k_NUM_OBJECTS_PER_FRAME = 1 + k_ROUNDED_NUM_OBJECTS; static constexpr int k_GROW_FACTOR = 2; static constexpr int k_MAX_NUM_OBJECTS = -32; // Data members std::atomic freeObjectsList_{nullptr}; CREATOR objectCreator_; RESETTER objectResetter_; int numReplenishObjects_; std::atomic numAvailableObjects_{0}; std::atomic numObjects_{0}; BlockNode *blockList_{nullptr}; std::pmr::memory_resource *memoryResource_; std::mutex mutex_; // Exception-safe cleanup helper class AutoCleanup { BlockNode *block_; ObjectNode *head_; std::pmr::memory_resource *mr_; int numNodes_; public: AutoCleanup(BlockNode *block, ObjectNode *head, std::pmr::memory_resource *mr, int numNodes = 0) : block_(block), head_(head), mr_(mr), numNodes_(numNodes) {} ~AutoCleanup() { if (head_) { ObjectNode *current = head_ + 1; // Skip to first object for (int i = 0; i < numNodes_; ++i) { reinterpret_cast(current + 1)->~TYPE(); current += k_NUM_OBJECTS_PER_FRAME; } mr_->deallocate(block_, sizeof(BlockNode) + sizeof(ObjectNode) * numNodes_ * k_NUM_OBJECTS_PER_FRAME, alignof(BlockNode)); } } AutoCleanup &operator++() { ++numNodes_; return *this; } void release() { head_ = nullptr; block_ = nullptr; } }; void replenish() { int numObjects = numReplenishObjects_ >= 0 ? numReplenishObjects_ : -numReplenishObjects_; addObjects(numObjects); if (numReplenishObjects_ < 0) { if (numReplenishObjects_ > k_MAX_NUM_OBJECTS) { numReplenishObjects_ *= k_GROW_FACTOR; } else { numReplenishObjects_ = -numReplenishObjects_; } } } void addObjects(int numObjects) { const size_t bytesPerBlock = sizeof(BlockNode) + sizeof(ObjectNode) * numObjects * k_NUM_OBJECTS_PER_FRAME; // Allocate block using PMR BlockNode *start = static_cast( memoryResource_->allocate(bytesPerBlock, alignof(BlockNode))); start->next_p = blockList_; start->numObjects = numObjects; ObjectNode *last = reinterpret_cast(start + 1); AutoCleanup guard(start, last, memoryResource_, 0); // Create objects for (int i = 0; i < numObjects; ++i, ++guard) { last->inUse.next_p.store(last + k_NUM_OBJECTS_PER_FRAME, std::memory_order_relaxed); last->inUse.refCount.store(0, std::memory_order_relaxed); // Use creator to construct object objectCreator_(last + 1, memoryResource_); last += k_NUM_OBJECTS_PER_FRAME; } last -= k_NUM_OBJECTS_PER_FRAME; last->inUse.refCount.store(0, std::memory_order_relaxed); guard.release(); blockList_ = start; // Link to free list (lock-free) ObjectNode *firstNode = reinterpret_cast(start + 1); ObjectNode *expected = freeObjectsList_.load(std::memory_order_acquire); do { last->inUse.next_p.store(expected, std::memory_order_relaxed); } while (!freeObjectsList_.compare_exchange_weak( expected, firstNode, std::memory_order_release, std::memory_order_acquire)); numObjects_.fetch_add(numObjects, std::memory_order_relaxed); numAvailableObjects_.fetch_add(numObjects, std::memory_order_relaxed); } public: // Constructors explicit ObjectPool(int growBy = -1, std::pmr::memory_resource *mr = std::pmr::get_default_resource()) : objectCreator_(mr), numReplenishObjects_(growBy), memoryResource_(mr) { assert(growBy != 0); } explicit ObjectPool( const CREATOR &creator, int growBy = -1, std::pmr::memory_resource *mr = std::pmr::get_default_resource()) : objectCreator_(creator), numReplenishObjects_(growBy), memoryResource_(mr) { assert(growBy != 0); } ObjectPool(const CREATOR &creator, const RESETTER &resetter, int growBy = -1, std::pmr::memory_resource *mr = std::pmr::get_default_resource()) : objectCreator_(creator), objectResetter_(resetter), numReplenishObjects_(growBy), memoryResource_(mr) { assert(growBy != 0); } ~ObjectPool() { // Destroy all objects for (BlockNode *block = blockList_; block; block = block->next_p) { int numObjects = block->numObjects; ObjectNode *node = reinterpret_cast(block + 1); for (int i = 0; i < numObjects; ++i) { reinterpret_cast(node + 1)->~TYPE(); node += k_NUM_OBJECTS_PER_FRAME; } } // Deallocate blocks while (blockList_) { BlockNode *next = blockList_->next_p; const size_t bytesPerBlock = sizeof(BlockNode) + sizeof(ObjectNode) * blockList_->numObjects * k_NUM_OBJECTS_PER_FRAME; memoryResource_->deallocate(blockList_, bytesPerBlock, alignof(BlockNode)); blockList_ = next; } } // Get object (lock-free fast path) TYPE *getObject() { ObjectNode *node; do { node = freeObjectsList_.load(std::memory_order_acquire); if (!node) { std::lock_guard lock(mutex_); node = freeObjectsList_.load(std::memory_order_relaxed); if (!node) { replenish(); continue; } } // Try to acquire node with atomic reference counting int expected = 0; if (node->inUse.refCount.compare_exchange_weak( expected, 2, std::memory_order_acq_rel, std::memory_order_relaxed)) { // Try to remove from free list ObjectNode *next = node->inUse.next_p.load(std::memory_order_relaxed); if (freeObjectsList_.compare_exchange_weak(node, next, std::memory_order_release, std::memory_order_acquire)) { break; } // Failed to remove, restore ref count node->inUse.refCount.store(0, std::memory_order_release); } } while (true); node->inUse.next_p.store(nullptr, std::memory_order_relaxed); numAvailableObjects_.fetch_sub(1, std::memory_order_relaxed); return reinterpret_cast(node + 1); } // Release object back to pool void releaseObject(TYPE *object) { ObjectNode *node = reinterpret_cast(object) - 1; // Reset object state objectResetter_(object); // Return to free list node->inUse.refCount.store(0, std::memory_order_relaxed); ObjectNode *expected = freeObjectsList_.load(std::memory_order_relaxed); do { node->inUse.next_p.store(expected, std::memory_order_relaxed); } while (!freeObjectsList_.compare_exchange_weak( expected, node, std::memory_order_release, std::memory_order_relaxed)); numAvailableObjects_.fetch_add(1, std::memory_order_relaxed); } // Capacity management void increaseCapacity(int numObjects) { if (numObjects > 0) { std::lock_guard lock(mutex_); addObjects(numObjects); } } void reserveCapacity(int numObjects) { std::lock_guard lock(mutex_); int needed = numObjects - numObjects_.load(std::memory_order_relaxed); if (needed > 0) { addObjects(needed); } } // Accessors int numAvailableObjects() const { return numAvailableObjects_.load(std::memory_order_relaxed); } int numObjects() const { return numObjects_.load(std::memory_order_relaxed); } std::pmr::memory_resource *memory_resource() const { return memoryResource_; } }; } // namespace modern