171 lines
4.7 KiB
C++
171 lines
4.7 KiB
C++
#ifndef OBJECTPOOL_H
|
|
#define OBJECTPOOL_H
|
|
|
|
#include <boost/lockfree/queue.hpp>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
using AvailableObjectsQueueType =
|
|
boost::lockfree::queue<std::size_t, boost::lockfree::fixed_sized<true>>;
|
|
|
|
template <typename T>
|
|
class ObjectPool;
|
|
|
|
template <typename T>
|
|
class BorrowedObject
|
|
{
|
|
T* obj;
|
|
ObjectPool<T>* poolPtr;
|
|
const std::size_t objectIndex;
|
|
bool returned = false;
|
|
|
|
BorrowedObject() = delete;
|
|
explicit BorrowedObject(T* Borrowed, ObjectPool<T>* PoolPtr, std::size_t ObjIndex)
|
|
: obj(Borrowed), poolPtr(PoolPtr), objectIndex(ObjIndex)
|
|
{
|
|
}
|
|
|
|
public:
|
|
friend ObjectPool<T>;
|
|
void returnObj()
|
|
{
|
|
if (!returned && obj != nullptr) {
|
|
poolPtr->availableObjectsQueue.push(objectIndex);
|
|
poolPtr->availableObjectsCount++;
|
|
returned = true;
|
|
}
|
|
}
|
|
T* getObj() { return obj; }
|
|
~BorrowedObject() { returnObj(); }
|
|
};
|
|
|
|
template <typename T>
|
|
class ObjectPool
|
|
{
|
|
AvailableObjectsQueueType availableObjectsQueue;
|
|
std::size_t objectCount;
|
|
std::vector<T> objects;
|
|
std::atomic_uint64_t availableObjectsCount;
|
|
std::atomic_bool shutdownFlag;
|
|
|
|
public:
|
|
friend BorrowedObject<T>;
|
|
ObjectPool(std::size_t Size, std::function<T(std::size_t)> objectInitializers);
|
|
ObjectPool() = delete;
|
|
ObjectPool(const ObjectPool&) = delete;
|
|
ObjectPool(ObjectPool&&) = delete;
|
|
ObjectPool& operator=(const ObjectPool&) = delete;
|
|
ObjectPool& operator=(ObjectPool&&) = delete;
|
|
|
|
BorrowedObject<T> borrowObj(uint64_t sleepTime_ms = 0);
|
|
BorrowedObject<T> try_borrowObj(bool& success);
|
|
std::size_t size() const;
|
|
uint64_t getAvailableObjectsCount() const;
|
|
std::vector<T>& getInternalObjects_unsafe();
|
|
void shutdown();
|
|
~ObjectPool();
|
|
};
|
|
|
|
template <typename T>
|
|
uint64_t ObjectPool<T>::getAvailableObjectsCount() const
|
|
{
|
|
return availableObjectsCount.load();
|
|
}
|
|
|
|
template <typename T>
|
|
ObjectPool<T>::ObjectPool(std::size_t Size, std::function<T(std::size_t)> 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 <typename T>
|
|
BorrowedObject<T> ObjectPool<T>::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<T> res(&objects[currIndex], this, currIndex);
|
|
assert(availableObjectsCount.load() >= 0);
|
|
return res;
|
|
} else {
|
|
BorrowedObject<T> 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 <typename T>
|
|
BorrowedObject<T> ObjectPool<T>::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<T> res(&objects[currIndex], this, currIndex);
|
|
assert(availableObjectsCount.load() >= 0);
|
|
return res;
|
|
} else {
|
|
BorrowedObject<T> res(nullptr, this, currIndex);
|
|
assert(availableObjectsCount.load() >= 0);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
std::size_t ObjectPool<T>::size() const
|
|
{
|
|
return objectCount;
|
|
}
|
|
|
|
template <typename T>
|
|
std::vector<T>& ObjectPool<T>::getInternalObjects_unsafe()
|
|
{
|
|
return objects;
|
|
}
|
|
|
|
template <typename T>
|
|
void ObjectPool<T>::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 <typename T>
|
|
ObjectPool<T>::~ObjectPool()
|
|
{
|
|
shutdown();
|
|
}
|
|
|
|
#endif // OBJECTPOOL_H
|