Created
April 19, 2017 16:25
-
-
Save matrixd/4fe3600ba88183c7567a78fb821ea5fa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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