#include <atomic>
#include <functional>
#include <memory_resource>
#include <mutex>
#include <type_traits>
namespace modern {
// Functors namespace (similar to ObjectPoolFunctors)
struct ObjectPoolFunctors {
using DefaultCreator =
std::function<void(void *, std::pmr::memory_resource *)>;
template <class TYPE> class Nil {
public:
void operator()(TYPE *) const {}
};
template <class TYPE> class Reset {
public:
void operator()(TYPE *object) const { object->reset(); }
};
template <class TYPE> class Clear {
public:
void operator()(TYPE *object) const { object->clear(); }
};
};
// Default creator implementation
template <class TYPE> class DefaultCreator {
private:
std::pmr::polymorphic_allocator<TYPE> 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>>) {
// TYPE uses allocator - pass it to constructor
new (arena) TYPE(std::pmr::polymorphic_allocator<TYPE>(mr));
} else {
// TYPE doesn't use allocator - default construct
new (arena) TYPE{};
}
}
};
// Main ObjectPool class
template <class TYPE, class CREATOR = DefaultCreator<TYPE>,
class RESETTER = ObjectPoolFunctors::Nil<TYPE>>
class ObjectPool {
private:
// Node structure (similar to Bloomberg's design)
union ObjectNode {
struct {
std::atomic<ObjectNode *> next_p;
std::atomic<int> 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<ObjectNode *> freeObjectsList_{nullptr};
CREATOR objectCreator_;
RESETTER objectResetter_;
int numReplenishObjects_;
std::atomic<int> numAvailableObjects_{0};
std::atomic<int> 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<TYPE *>(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<BlockNode *>(
memoryResource_->allocate(bytesPerBlock, alignof(BlockNode)));
start->next_p = blockList_;
start->numObjects = numObjects;
ObjectNode *last = reinterpret_cast<ObjectNode *>(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<ObjectNode *>(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<ObjectNode *>(block + 1);
for (int i = 0; i < numObjects; ++i) {
reinterpret_cast<TYPE *>(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<std::mutex> 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<TYPE *>(node + 1);
}
// Release object back to pool
void releaseObject(TYPE *object) {
ObjectNode *node = reinterpret_cast<ObjectNode *>(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<std::mutex> lock(mutex_);
addObjects(numObjects);
}
}
void reserveCapacity(int numObjects) {
std::lock_guard<std::mutex> 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