Skip to content

Instantly share code, notes, and snippets.

@matrixd
Created April 19, 2017 16:25
Show Gist options
  • Save matrixd/4fe3600ba88183c7567a78fb821ea5fa to your computer and use it in GitHub Desktop.
Save matrixd/4fe3600ba88183c7567a78fb821ea5fa to your computer and use it in GitHub Desktop.
#ifndef CONNECTIONPOOL_HPP
#define CONNECTIONPOOL_HPP
#include <memory>
#include <functional>
#include <deque>
#include <mutex>
/**
* ObjectPool
*
* You can use this class to save resources when you have
* reusable objects with costly initialization/construction.
*
* You can set the start size of the pool, normal size and also limit the resource usage
* via setting the maximum pool size.
*
* It's always better to use ObjectPoolBuilder to create ObjectPool.
* If you use ObjectPool without ObjectPool::Ptr - don't call ~ObjectPool(),
* use ObjectPool::destroy() instead.
*/
template <typename Object>
class ObjectPool
{
public:
using ObjectPtr = std::unique_ptr<Object, std::function<void(Object*)>>;
using ObjectFreeFunction = std::function<void(Object*)>;
using ObjectBackToPoolFunction = std::function<void(Object*)>;
using ObjectFabric = std::function<Object*()>;
using Ptr = std::shared_ptr<ObjectPool<Object>>;
ObjectPtr object();
explicit ObjectPool(ObjectFabric fabric, ObjectFreeFunction freeFunc, ObjectBackToPoolFunction backFunc, int startSize, int maxSize, int normalSize);
void destroy();
private:
void freeConnection(Object *object);
std::deque<Object*> m_objects;
ObjectFabric m_fabric;
ObjectFreeFunction m_freeFunc;
ObjectBackToPoolFunction m_backFunc;
std::mutex m_objectsMutex;
int m_maxSize;
int m_normalSize;
bool m_shouldDestroy;
int m_objectCount;
};
/**
* Builder for ObjectPool.
*/
template <typename Connection>
class ObjectPoolBuilder
{
public:
ObjectPoolBuilder();
ObjectPoolBuilder &setConnectionBackToPoolFunc(typename ObjectPool<Connection>::ObjectBackToPoolFunction connectionBackToPoolFunc);
ObjectPoolBuilder &setConnectionFreeFunc(typename ObjectPool<Connection>::ObjectFreeFunction connectionFreeFunc);
ObjectPoolBuilder &setConnectionFabric(typename ObjectPool<Connection>::ObjectFabric connectionFabric);
/**
* @brief setStartPoolSize
* @param size
* @return
*
* Start pool size - is the initial size of the pool.
* e.g. if start pool size == 5 - ObjectPool will create 5 objects when the constructor is called.
*/
ObjectPoolBuilder &setStartPoolSize(int size);
ObjectPoolBuilder &setMaxPoolSize(int size);
ObjectPoolBuilder &setNormalPoolSize(int size);
Ptr createPool();
private:
typename ObjectPool<Connection>::ObjectBackToPoolFunction m_connectionBackToPoolFunc;
typename ObjectPool<Connection>::ObjectFreeFunction m_connectionFreeFunc;
typename ObjectPool<Connection>::ObjectFabric m_connectionFabric;
int m_maxPoolSize;
int m_startPoolSize;
int m_normalSize;
};
template<typename Connection>
typename ObjectPool<Connection>::ObjectPtr ObjectPool<Connection>::object()
{
Connection *con = nullptr;
m_objectsMutex.lock();
if (!m_objects.empty())
{
con = m_objects.front();
m_objects.pop_front();
}
m_objectsMutex.unlock();
if (!con)
{
con = m_fabric();
}
m_objectCount++;
return ObjectPtr(con, [this](Connection *ptr){ this->freeConnection(ptr); });
}
template<typename Connection>
ObjectPool<Connection>::ObjectPool(
ObjectPool::ObjectFabric fabric,
ObjectPool::ObjectFreeFunction freeFunc,
ObjectPool::ObjectBackToPoolFunction backFunc,
int startSize,
int maxSize,
int normalSize
)
: m_fabric(fabric)
, m_freeFunc(freeFunc)
, m_backFunc(backFunc)
, m_maxSize(maxSize)
, m_normalSize(normalSize)
, m_shouldDestroy(false)
, m_objectCount(0)
{
while (startSize)
{
m_objects.push_back(m_fabric());
startSize--;
}
}
template<typename Connection>
void ObjectPool<Connection>::destroy()
{
m_objectsMutex.lock();
m_shouldDestroy = true;
while (!m_objects.empty())
{
m_freeFunc(m_objects.front());
m_objects.pop_front();
}
m_objectsMutex.unlock();
}
template<typename Connection>
void ObjectPool<Connection>::freeConnection(Connection *connection)
{
m_objectsMutex.lock();
if (m_shouldDestroy || (m_objectCount > m_maxSize && m_maxSize > 0))
{
m_freeFunc(connection);
}
else
{
m_backFunc(connection);
m_objects.push_back(connection);
}
m_objectCount--;
if (m_shouldDestroy && m_objectCount == 0)
{
//should prvent double-free I hope
m_objectCount++;
m_objectsMutex.unlock();
delete this;
}
m_objectsMutex.unlock();
}
template<typename Connection>
ObjectPoolBuilder<Connection> &ObjectPoolBuilder<Connection>::setConnectionBackToPoolFunc(typename ObjectPool<Connection>::ObjectBackToPoolFunction connectionBackToPoolFunc)
{
m_connectionBackToPoolFunc = connectionBackToPoolFunc;
return *this;
}
template<typename Connection>
ObjectPoolBuilder<Connection> &ObjectPoolBuilder<Connection>::setConnectionFreeFunc(typename ObjectPool<Connection>::ObjectFreeFunction connectionFreeFunc)
{
m_connectionFreeFunc = connectionFreeFunc;
return *this;
}
template<typename Connection>
ObjectPoolBuilder<Connection> &ObjectPoolBuilder<Connection>::setConnectionFabric(typename ObjectPool<Connection>::ObjectFabric connectionFabric)
{
m_connectionFabric = connectionFabric;
return *this;
}
template<typename Connection>
ObjectPoolBuilder<Connection> &ObjectPoolBuilder<Connection>::setStartPoolSize(int size)
{
m_startPoolSize = size;
return *this;
}
template<typename Connection>
ObjectPoolBuilder<Connection> &ObjectPoolBuilder<Connection>::setMaxPoolSize(int size)
{
m_maxPoolSize = size;
return *this;
}
template<typename Connection>
ObjectPoolBuilder &ObjectPoolBuilder::setNormalPoolSize(int size)
{
m_normalSize = size;
return *this;
}
template<typename Connection>
ObjectPoolBuilder<Connection>::ObjectPoolBuilder()
: m_maxPoolSize(-1)
, m_startPoolSize(3)
, m_normalSize(10)
{
}
template<typename Connection>
typename ObjectPoolBuilder<Connection>::ConnectionPoolPtr ObjectPoolBuilder<Connection>::createPool()
{
if (!m_connectionFabric)
{
m_connectionFabric = [] () { return new Connection(); };
}
if (!m_connectionBackToPoolFunc)
{
m_connectionBackToPoolFunc = [] (Connection*) {};
}
if (!m_connectionFreeFunc)
{
m_connectionFreeFunc = [] (Connection *ptr) { delete ptr; };
}
ObjectPool<Connection> *pool = new ObjectPool<Connection>(
m_connectionFabric,
m_connectionFreeFunc,
m_connectionBackToPoolFunc,
m_startPoolSize,
m_maxPoolSize
);
return ObjectPool<Connection>::Ptr(pool, [](ObjectPool<Connection> *pool) { pool->destroy(); });
}
#endif // CONNECTIONPOOL_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment