#ifndef OBJECTPOOL_H #define OBJECTPOOL_H #include #include #include #include #include using AvailableObjectsQueueType = boost::lockfree::queue>; template class ObjectPool; template class BorrowedObject { T* obj; ObjectPool* poolPtr; const std::size_t objectIndex; bool returned = false; BorrowedObject() = delete; explicit BorrowedObject(T* Borrowed, ObjectPool* PoolPtr, std::size_t ObjIndex) : obj(Borrowed), poolPtr(PoolPtr), objectIndex(ObjIndex) { } public: friend ObjectPool; void returnObj() { if (!returned && obj != nullptr) { poolPtr->availableObjectsQueue.push(objectIndex); poolPtr->availableObjectsCount++; returned = true; } } T* getObj() { return obj; } ~BorrowedObject() { returnObj(); } }; template class ObjectPool { AvailableObjectsQueueType availableObjectsQueue; std::size_t objectCount; std::vector objects; std::atomic_uint64_t availableObjectsCount; std::atomic_bool shutdownFlag; public: friend BorrowedObject; ObjectPool(std::size_t Size, std::function objectInitializers); ObjectPool() = delete; ObjectPool(const ObjectPool&) = delete; ObjectPool(ObjectPool&&) = delete; ObjectPool& operator=(const ObjectPool&) = delete; ObjectPool& operator=(ObjectPool&&) = delete; BorrowedObject borrowObj(uint64_t sleepTime_ms = 0); BorrowedObject try_borrowObj(bool& success); std::size_t size() const; uint64_t getAvailableObjectsCount() const; std::vector& getInternalObjects_unsafe(); void shutdown(); ~ObjectPool(); }; template uint64_t ObjectPool::getAvailableObjectsCount() const { return availableObjectsCount.load(); } template ObjectPool::ObjectPool(std::size_t Size, std::function objectInitializers) : availableObjectsQueue(Size) { shutdownFlag.store(false); for (std::size_t i = 0; i < Size; i++) { availableObjectsQueue.push(i); } objects.reserve(Size); for (std::size_t i = 0; i < Size; i++) { objects.push_back(objectInitializers(i)); } objectCount = Size; availableObjectsCount.store(Size); } /// Obj can be null only when shutting down template BorrowedObject ObjectPool::borrowObj(uint64_t sleepTime_ms) { std::size_t currIndex = -1; bool isShuttingDown = false; while (!(isShuttingDown = shutdownFlag.load(std::memory_order_relaxed)) && !availableObjectsQueue.pop(currIndex)) { if (sleepTime_ms) { std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime_ms)); } } if (!isShuttingDown) { availableObjectsCount--; BorrowedObject res(&objects[currIndex], this, currIndex); assert(availableObjectsCount.load() >= 0); return res; } else { BorrowedObject res(nullptr, this, currIndex); assert(availableObjectsCount.load() >= 0); return res; } } /// Obj can be null when shutting down or if the object is not available template BorrowedObject ObjectPool::try_borrowObj(bool& success) { std::size_t currIndex = -1; bool isShuttingDown = shutdownFlag.load(std::memory_order_relaxed); if (!isShuttingDown && availableObjectsQueue.pop(currIndex)) { success = true; } else { success = false; } if (!isShuttingDown && success) { availableObjectsCount--; BorrowedObject res(&objects[currIndex], this, currIndex); assert(availableObjectsCount.load() >= 0); return res; } else { BorrowedObject res(nullptr, this, currIndex); assert(availableObjectsCount.load() >= 0); return res; } } template std::size_t ObjectPool::size() const { return objectCount; } template std::vector& ObjectPool::getInternalObjects_unsafe() { return objects; } template void ObjectPool::shutdown() { // this can be called more than once shutdownFlag.store(true); while (availableObjectsCount != objectCount) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } availableObjectsQueue.consume_all([](std::size_t&) {}); objects.clear(); } template ObjectPool::~ObjectPool() { shutdown(); } #endif // OBJECTPOOL_H