![]() |
Platforms, Frameworks & Libraries »
Libraries »
General
Intermediate
Template based Generic Pool using C++By Rohit JoshiTemplate based Generic Pool using C++. |
VC6, VC7, VC7.1, VC8.0, Windows, .NET, MFC, VS.NET2003, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||
Many applications use connection/object pool. A program may require a IMAP connection pool and LDAP connection pool. One could easily implement an IMAP connection pool, then take the existing code and implement a LDAP connection pool. The program grows, and now there is a need for a pool of threads. So just take the IMAP connection pool and convert that to a pool of threads (copy, paste, find, replace????). Need to make some changes to the pool implementation? Not a very easy task, since the code has been duplicated in many places. Re-inventing source code is not an intelligent approach in an object oriented environment which encourages re-usability. It seems to make more sense to implement a pool that can contain any arbitrary type rather than duplicating code. How does one do that? The answer is to use type parameterization, more commonly referred to as templates.
C++ templates allow one to implement a generic Pool<T> template that has a type parameter T. T can be replaced with actual types, for example, class ImapConn, and C++ will generate the class Pool<ImapConn>. Changing the implementation of the Pool becomes relatively simple. Once the changes are implemented in the template Pool<T>, they are immediately reflected in the classes Pool<ImapConn>, Pool<LdapConn>, and Pool<Threads>.
Attached demo project contains:
This article demonstrates how to implement generic pool using templates. Code is been compiled on Windows as well as Linux. Please feel free to modify and use.
Below are the requirements to implement generic pool:
The source code contains:
template<class T> class PoolMgr { typedef ObjectHolder<T> ObjHolder; typedef list<ObjHolder> ObjList; static Mutex m_mPool; // Mutex for Pool static instance Mutex m_mData; // Mutex for Pool data public: // Get the Instance of pool manager static PoolMgr<T>* GetInstance() { if(!m_pPoolMgr) { Lock<Mutex> gaurd(m_mPool); if(!m_pPoolMgr) m_pPoolMgr = new PoolMgr<T>() } return m_pPoolMgr; } // delete the pool static void DeletePool() { if(m_pPoolMgr) { Lock<Mutex> gaurd(m_mPool); if(m_pPoolMgr){ delete m_pPoolMgr; m_pPoolMgr = NULL; } } // Initliaze pool void Init(unsigned nPoolSize, long nExpirationTime, bool bTempObjAllowed, unsigned nWaitTime = 3) { � } // Reset the pool void ResetPool() { ..... } // Initliaze pool void Init(unsigned nPoolSize, long nExpirationTime, bool bTempObjAllowed, unsigned nWaitTime = 3) { ... } // Checkout the Object from pool T* Checkout() { ... } // checkin the Object into pool void Checkin(T *pObj) { ... } private: static PoolMgr<T> *m_pPoolMgr; // static instance of PoolMgr //private constructor PoolMgr() { m_nPoolSize = 0; m_nExpirationTime = 600; // in sec m_bTempObjAllowed = true; m_nWaitTime = 3; } // distructor ~PoolMgr() { } // pool size : default 0 unsigned m_nPoolSize; // wait time: How long calling function can wait to find object unsigned m_nWaitTime; // Object expiration time: default 600 long m_nExpirationTime; // if pool is full, is tempobject allowed bool m_bTempObjAllowed; // reserved objects ObjList m_oReserved; // free objects ObjList m_oFree; }; template<class T> PoolMgr<T>* PoolMgr<T>::m_pPoolMgr = NULL; //initialize static instance template<class T> Mutex PoolMgr<T>::m_mPool;
static PoolMgr<T>* GetInstance(): which returns the instance of PoolMgr.
static void DeletePool(): deletes the pool and frees resources.
void Init(unsigned nPoolSize, long nExpirationTime, bool bTempObjAllowed, unsigned nWaitTime): User must initialize the Pool with the following parameters:
PoolSize: Size of the Pool.
ExpirationTime: Duration in seconds. If object is not used for this duration, object would be considered as expired and would be moved to an available object pool.
TempConnAllowd: If Pool is full, should Pool be allowed to create temporary connections.
WaitTime: If temporary connection is not allowed and Pool is full, how long caller function can wait to get the connection from the expired connections. void ResetPool(): Release all the resources and reset the pool.
T* Checkout(): Check out the resource.
void Checkin(T* pObj): Check in the resource.
template<class T>: Class PoolMgr contains two list of pools. One is for reserved objects and the other for free objects. In a multithreaded environment, it would avoid locking all the objects instead of specific types. E.g., only all free objects or reserved. T which allows storing any generic object class. template<class T> class ObjectHolder { public: // constructor ObjectHolder() { m_nTimeStamp = -1; m_pObj = NULL; } // distructor ~ObjectHolder() { if(m_pObj) { m_pObj->Release(); m_pObj = NULL; } } //Initliaze object void InitObject() { if(!m_pObj) { m_pObj = new T(); m_pObj->Init(); } } private: T *m_pObj; // object pointer long m_nTimeStamp; // timestamp };
GenericObject class. class GenericObject { public: //constructor GenericObject() {} //destrctor ~GenericObject() {} //Initliaze object virtual void Init() {} //Release the resource related to object virtual void Release() {} // Check if object is still usable virtual bool IsUsable() { return true; } // If object is not usable, make it usable virtual bool MakeUsable() { if(!IsUsable()) { Init(); return true; } };
Here:
void Init(): Initialize the object. If object needs to make connection, do it in this function.
void Release(): Release the resources.
bool IsUsabled(): Is this object still usable?
bool MakeUsable(): If it is not usable, try to make it usable, and if successful, return true. This will avoid construction of new object if successfully made reusable. // Lock class template <class T > class Lock { T& obj_; // type object public: // Lock Lock(T& obj):obj_(obj) { obj_.Lock(); } // Unlock ~Lock() { obj_.Unlock(); } }; // Mutex class class Mutex { public: // constructor Mutex() { InitializeCriticalSection(&m_mMutex); } // destructor virtual ~Mutex() { DeleteCriticalSection(&m_mMutex); } // lock bool Lock() { EnterCriticalSection(&m_mMutex); return true; } // unlock bool Unlock() { LeaveCriticalSection(&m_mMutex); return true; } private: CRITICAL_SECTION m_mMutex; // critical section as mutex void operator=(Mutex &m_mMutex) {} // private = operator Mutex( const Mutex &m_mMutex ) {} // private copy constructor };
main function which gets the instance of PoolMgr and checks out and checks in the GenericObject. PoolMgr<GenericObject> *pMgr = PoolMgr<GenericObject>::GetInstance(); if(pMgr) { // Pool size 10, object expiration time 600 sec // and temporary connection not allowed pMgr->Init(10,600, false); GenericObject *pObj = NULL; pObj = pMgr->Checkout(); pMgr->Checkin(pObj); pMgr->ResetPool(); // ExpirationTime test: Pool size is 1, expiration time // is 10 secs, and temporary connections not allowed. // Here when you checkout 2nd object, it would wait for 10 seconds // and the 1st object would be garbage collected // and moved to free resource. pMgr->Init(1,10, false); GenericObject *pObj = NULL; pObj = pMgr->Checkout(); std::cout << "1st Object checked out " << std::endl; pObj = pMgr->Checkout(); std::cout << "2st Object checked out after 10 secs " << std::endl; pMgr->ResetPool(); } PoolMgr<GenericObject>::DeletePool(); // delete and free all resources
Please see my other article about pool design: Generic Pool: Policy based design.
Please let me know how this article would have been improved and made more useful.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 17 Sep 2004 Editor: Chris Maunder |
Copyright 2004 by Rohit Joshi Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |