Click here to Skip to main content
Click here to Skip to main content
Go to top

Template based Generic Pool using C++

, 17 Sep 2004
Rate this:
Please Sign up or sign in to vote.
Template based Generic Pool using C++.

Introduction

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:

  1. Docs: Source code documentation.
  2. Source code and project files.

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:

  • Generic: It should be generic enough to work with any type of resources. E.g., Database Connection pool, Thread pool, other resource pool etc...
  • Pool Size: Size of the pool should be configurable and if required changeable at runtime.
  • Pool Type: If Pool is fixed size, or temporary connection allowed in case of pool is full.
  • Object's lifetime: If user doesn't check-in the resource back, what should be the duration at which the object will be considered as expired and returned back to free resources.
  • Timeout functionality: If Pool is full and temporary connections are not allowed, how long caller function can wait to get object.

The source code contains:

  1. PoolMgr.h which implements the singleton Pool class. It has following functions:
    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.
  2. ObjectHolder.h: which contains the object pointer and timestamp. It is a template class of type 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
            
    };
  3. GenericObject.h: This is a sample generic class which is used for testing this pool. User of this pool needs to either implement following methods in their connection/object class or inherit from 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:

    1. void Init(): Initialize the object. If object needs to make connection, do it in this function.
    2. void Release(): Release the resources.
    3. bool IsUsabled(): Is this object still usable?
    4. 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.
  4. MutexWin.h (Windows) and Mutex.h (Linux):
    // 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
    };
  5. main.c: This is a sample 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.

History

  • 09/16/2004: Added support for thread synchronization.

Please let me know how this article would have been improved and made more useful.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Rohit Joshi
Software Developer
United States United States
Rohit Joshi is a software engineer working for a telecom company in USA. He has development expirience using C, C++ ,C#, VoiceXML, ASR, IMAP, LDAP, HTTP, SIP, H323 on unix/linux and platforms.

Comments and Discussions

 
QuestionCan I use this code in my commercial project? PinmemberMember 87468193-Apr-12 16:40 
AnswerRe: Can I use this code in my commercial project? PinmemberRohit Joshi4-Apr-12 4:04 
Question[Message Deleted] PinmemberNancy Rosenfield19-Nov-09 4:01 
Generalgraphics Pinmembersharmi3-Dec-07 20:37 
QuestionIs there a memory leak in the code? Pinmemberjasonwnz18-Jul-07 19:12 
AnswerRe: Is there a memory leak in the code? Pinmemberjasonwnz18-Jul-07 20:29 
QuestionNice article [modified] Pinmembera_diwakar13-Mar-07 22:54 
AnswerRe: Nice article PinmemberRohit Joshi14-Mar-07 4:40 
GeneralNice PinmemberLudi8316-Jan-07 3:57 
GeneralExcellent work. Two small Bugs found & fixed.. Pinmemberadrian cooper13-Sep-06 4:06 
GeneralPossible deadlock PinmemberCount Dew1-Nov-05 0:27 
QuestionRe: Possible deadlock Pinmemberdrken56716-Jan-08 11:01 
Hi, I'm attempting to do this mutex unlock as suggested, but I am unclear as to the proper syntax to unlock (before sleeping) and re-lock afterwards. Fairly new to template programming, so I think my confusion is there, but I have tried several syntaxes without success. Thanks!
GeneralRelease method... Pinmembersagiv_c25-Mar-05 4:03 
Generalthanks for linux support Pinsussanonymous18-Mar-05 8:38 
GeneralMinor corrections. PinmemberWREY4-Sep-04 8:00 
GeneralRe: Minor corrections. PinmemberRohit Joshi4-Sep-04 18:26 
GeneralIt would be more invaluable ... PinmemberWREY30-Aug-04 3:36 
GeneralRe: It would be more invaluable ... PinmemberRohit Joshi30-Aug-04 5:01 
GeneralRe: It would be more invaluable ... PinmemberRohit Joshi3-Sep-04 21:56 
GeneralNice article PinmemberAnonymous28-Aug-04 12:04 
GeneralRe: Nice article PinmemberRohit Joshi28-Aug-04 12:08 
GeneralRe: Nice article PinmemberApoch200416-Jan-06 9:53 
GeneralRe: Nice article PinmemberRohit Joshi17-Sep-04 4:25 
GeneralCould have been a very interesting article... PinsussAnonymous27-Aug-04 23:59 
GeneralRe: Could have been a very interesting article... PinmemberRohit Joshi28-Aug-04 4:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 17 Sep 2004
Article Copyright 2004 by Rohit Joshi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid