Click here to Skip to main content
Click here to Skip to main content

Generic Pool: Policy based design

, 3 Sep 2004
Rate this:
Please Sign up or sign in to vote.
Generic Pool: Policy based design.

Introduction

In my previous article (Template based Generic Pool using C++), I explained how to implement generic pool using templates and C++. There are a few limitations in this pool design, and I thought it would be good learning experience to overcome these limitations by applying the policy based design approach.

Readers are assumed to have basic understanding of C++, templates, and STL. I will not go in to details of policy based design. There are plenty of resources available on the Internet. You can also refer to one of the best C++ books, Modern C++ Design by Andrei Alexandrescu, or his article (Policy-Based Class Design in C++). He has explained policy based design in details.

Andrei describes policy based class as:

policy-based class design fosters assembling a class with complex behavior out of many little classes (called policies), each of which takes care of only one behavioral or structural aspect. As the name suggests, a policy establishes an interface pertaining to a specific issue. You can implement policies in various ways as long as you respect the policy interface”.

Note:

  • For simplicity, “Connection” is used as resource object which needs to be stored into the pool. It could be any server connection or any other type of resource. E.g., class objects, thread objects etc..
  • This article just shows partial code to explain the implementation. You need to look at the source files for complete code.

Let’s find out different requirements for the pool in real life scenario.

  1. Our customers are distributed on multiple LDAP servers and we want to access the LDAP connection from the Pool for given LDAP server. Current design doesn’t provide the support to store the connections with its identifier. It only stores non identifiable connections.
  2. We do not want connections to be released till the life time of the process, or release the connections if they are idle for more than specified time. Our current design only supports the object expiration requirement.
  3. When pool creates the connections, application may want to do specific initialization before storing it into the pool, or release associated resources before destroying the connections. Current design doesn’t provide the flexibility for custom requirements for creation and deletion of connections.
  4. We used STL list to store the connections into the pool. What if we want to use either vector or our custom container? In current design, access to the container is very tightly integrated with PoolMgr class. If you change the container, you will have to change the implementation completely.
  5. We may want our pool size to be fixed, and if pool is full, caller has to wait to retrieve the connection from the pool, or we may want to allow temporary connections. We may want to resize the pool based on the load. Current design doesn’t support these requirements.

Let’s look at each requirement and see how we can fulfill it. For the 1st requirement, we need two types of connection objects to be stored in a pool, connections with ID (identifiable) and connections without ID (Generic). This can be implemented using “HasIdPolicy” which has two types of classes.

Note: I could have used structure, but class would be clearer to “C” programmers.

class WithId
{
public:
static bool HasId() { return true;}
};
class WithoutId
{
public:
static bool HasId() { return false;}
};

For the 2nd requirement, we need two types of classes. One which expires after certain amount of idle time, other that does not expire through out the life time of the process. This can be implemented by “ObjectExpirationPolicy” which has two classes as:

class ObjectWithExpiration
{
public:
// is object expire
static bool IsExpire() {return true;….}
};
class ObjectWithoutExpiration
{
public:
// is expire :no
static bool IsExpire(){return false;}
};

For 3rd requirement, we can define “ObjectCreationPolicy” were connection creation and destroy functionality is implemented. User can define his custom creation policy. I have defined default creation policy as:

template<class Object>
class ObjectCreationDefault //: public ObjectCreationPolicyBase<Object>
{
public:
// create and initialize the object
static Object* Create()
{
return new Object();
}
// Uninitialize and destroy the object
static Destroy(Object* pObj)
{
if(pObj) {
delete pObj;
}
pObj = NULL;
}
// Validate the object if not valid
static bool Validate(Object *pObj)
{
return true;
}
};

For 4th requirement, we need to implement “ContainerInterface”. I have implemented two interfaces as below. I have derived these two classes from ContainerInterfaceBase which defines the containers as member variables of base class which are inherited by both of these policy classes.

These are the template based classes which use above defined policies. These policy classes are dependent on class “HasIdPolicy” and two types of traits which define the actual container. I will explain traits later in this article. Both of these traits define specific type based on selected policies.

template<class Object,
class HasIdPolicy,
template<CLASS> class CreationPolicy, 
class ObjectExpirationPolicy,
typename HolderTraits = ObjectHolderTraits< Object, 
CreationPolicy,ObjectExpirationPolicy>,
typename ContainerTraits = ObjectContainerTraits<SmartHolder, 
HasIdPolicy>
>
class ContainerInterfaceBase
{
public:
ContainerInterfaceBase(){}
virtual ~ContainerInterfaceBase(){}
protected:
.......
Container m_oFree;
Container m_oReserved;
};
template<class Object,
class HasIdPolicy = WithId,
template<class> class CreationPolicy = ObjectCreationDefault, 
class ObjectExpirationPolicy = ObjectWithExpiration,
typename HolderTraits = ObjectHolderTraits< Object, 
CreationPolicy,ObjectExpirationPolicy>,
typename ContainerTraits = ObjectContainerTraits< 
Loki::SmartPtr<typename HolderTraits::ObjectHolder>, WithId>
>
class ContainerInterfaceWithId
{.....};

and

template<class Object,
class HasIdPolicy = WithoutId,
template<class> class CreationPolicy = ObjectCreationDefault, 
class ObjectExpirationPolicy = ObjectWithExpiration,
typename HolderTraits = ObjectHolderTraits< Object, 
CreationPolicy,ObjectExpirationPolicy>,
typename ContainerTraits = ObjectContainerTraits< 
Loki::SmartPtr<typename HolderTraits::ObjectHolder>, WithId>
>
class ContainerInterfaceWithoutId
{.....};

For 5th requirement, we will define “PoolSizeType” policy and it has two class implementations as:

class PoolSizeFixed 
{
public:
// Is Sizable
static bool IsSizable()
{
return false;
}
.....
};
class PoolSizeDynamic
{
public:
// is pool sizable
static bool IsSizable()
{
return true;
}
.....
};

We have found the solution to fulfill these requirements. Now, let's look at how can we use these policies. If we want to use the “ObjectExpirationPolicy”, then timestamp of the connection creation/access time needs to be stored along with connection object. One way to do is to modify the connection class and add timestamp member variable and get/set methods on this class. Another approach is to store the timestamp along with connection object into another wrapper class as connection holder.

The first approach is easier but the disadvantage is that all the connection classes need to be modified to have connection expiration supported which is not feasible. More over, you might want to store some connections/objects which you do not have access to source code… So, I decided to implement the 2nd approach where we will create connection holder class based on “ObjectExpirationpolicy”. Holder class will be more useful when you want to store additional information like username and password of the server etc…

In the case of connections not expiring, we don’t need to store the timestamp value. We will create two types of connection holder classes, one with connection timestamp as member variables, other without timestamp.

Other problem is how do we know which holder class and when to use based on selected policy? Should we create instance of both the classes and use “if else” to check which one to use based on selected policy?

To overcome this problem, we need to look at the traits. What are traits?

Nathan C. Myers gives short definition of traits in his article (Traits: a new and useful template technique) as:

A class used in place of template parameters. As a class, it aggregates useful types and constants; as a template, it provides an avenue for that "extra level of indirection" that solves all software problems.”

template<class Object,
template<typename> class ObjectCreationPolicy, 
class ObjectExpirationPolicy>
class ObjectHolderTraits;

ObjectHolderTraits” uses creation policy to create and destroy the connections, and expiration policy to define timestamp variable.

We will partially specialize this template with “ObjectWithExpiration” and “ObjectWithoutExpiration” policies as:

template<class Object, template<typename> class ObjectCreationPolicy>
class ObjectHolderTraits<Object,ObjectCreationPolicy,ObjectWithExpiration>
{
class Holder {
. . . .
;
};

template<class Object, template<typename> class ObjectCreationPolicy>
class ObjectHolderTraits<Object,ObjectCreationPolicy,ObjectWithoutExpiration>
{
class Holder {
. . . .
;
};

4th requirement is partially fulfilled by “ContainerInterfacePolicy” by adding the abstraction layer to access the containers. But what if we want to use vector instead of list or multimap instead of map? Again, traits provide the solution to this problem.

We will define object container traits and partially specialize with “HasIdPolicy”.

template<class Object, class HasIdPolicy>
class ObjectContainerTraits;

Here, we have used STL “List” container for without ID policy, and STL “map” container for without ID policy.

template<class Object>
class ObjectContainerTraits<Object,WithoutId>
{
public:
typedef list<Object> Container;
};

template<class Object>
class ObjectContainerTraits<Object,WithId>
{
public:
typedef map<string,Object> Container;
};

The objects inside the container are stored as smart pointers. There are two reasons.

  1. When you move connection from free to reserved or reserved to free, if connection holder object is stored into container, it will make a copy of the object and store into container which can not be used in our case as the connection is a pointer object which might have socket connection to specific server which can not be copied. Other reason, it may not have copy constructor implemented.
  2. If we store as pointer then who will delete the memory? STL container doesn’t delete the pointers as it assumes that objects are stored inside the container and its destructor will cleanup the resources associated with it.

To solve these problems, smart pointer is the way to go where we don’t have to worry about memory leak and reference counting. I have used Loki::SmartPtr as smart pointers. For more details: SourceForge.

It’s time to put all together and implement the Generic Pool. The Pool class is supposed to be implemented as singleton. I decide to use policy based designed Loki::SingletonHolder<> class which is part of the Loki library, instead of writing my own singleton class.

There is a really good article (Singleton Pattern: A review and analysis of existing C++ implementations) on The Code Project.

The class is defined as:

template<class Object,
class HasIdPolicy = WithoutId,
class PoolSizePolicy = PoolSizeFixed,
class ObjectExpirationPolicy = ObjectWithExpiration,
template<class> class CreationPolicy = ObjectCreationDefault,
typename HolderTraits = ObjectHolderTraits<Object,CreationPolicy,
                                           ObjectExpirationPolicy>,
typename ContainerTraits = ObjectContainerTraits<Loki::SmartPtr<typename 
                                    HolderTraits::ObjectHolder>, HasIdPolicy>,
typename ContainterInterfaceTraits = ContainerInterfaceTraits<Object, 
                                    HasIdPolicy>
>
class PoolMgr
{
public:
  void Init(unsigned nPoolSize, unsigned nExpirationTimeSec);
  Object* Checkout(string &sId) {...} //withid
  Object* Checkout() {...} //without id
  bool Checkin(string &sId) {...} //withid
  bool Checkin(Object *pObj) {...} // without id
  void ResizePool(unsigned nNewSize){...} //resize/reset
private:
  typename ContainterInterfaceTraits::ContainerInterfacePolicy m_oContainer;
};

In this class, I have provided basic functionality of the pool. You can modify it to have custom behavior. E.g., how long caller function should wait before returning when pool is full and it’s not sizable (See 1). If object expiration policy is allowed, you can spawn a thread which will wake up at certain time to clean up the expired connections.

Finally, below is the code to test our pool implementation. Please make sure you use the Loki::SingletonHolder<> class to create an instance of the pool. Try it out by changing different policies to PoolMgr template parameters.

// My connection class.
class MyConnection
{
public:
  MyConnection()
  {
    LOG(LOG_INFO, "MyConnection()");
  }
  ~MyConnection()
  {
    LOG(LOG_INFO, "~MyConnection()");
  }
  string & Get()
  {
    return m_sString;
  }
  void Set(string &sStr)
  {
    m_sString = sStr;
  }
private:
  string m_sString;
};

//
int main()
{
// create a singleton using Loki::SingletonHolder
  //without id 
  { typedef PoolMgr< MyConnection, WithoutId, PoolSizeFixed,
    ObjectWithExpiration> Pool;
    Pool *pMgr = &Loki::SingletonHolder:: Instance();
    pMgr->Init(1,5);
    MyConnection *pConn = pMgr->Checkout();
    pMgr->Checkin(pConn);
    pMgr->ResizePool(0);  
   }
//with id 
  { typedef PoolMgr< MyConnection, WithId, PoolSizeFixed,
    ObjectWithExpiration> Pool;
    Pool *pMgr = &Loki::SingletonHolder:: Instance();
    string sId = "1";
    pMgr->Init(1,5);
    MyConnection *pConn = pMgr->Checkout(sId);
    pMgr->Checkin(sId);
    pMgr->ResizePool(0);  
   }
}

Note: I have included Windows version of Loki library along with my project. You can download latest version at SourceForge.

References:

  1. Template based Generic Pool using C++ Rohit Joshi.
  2. Policy-Based Class Design in C++ Andrei Alexandrescu.
  3. Book: Modern C++ Design Andrei Alexandrescu.
  4. Traits: a new and useful template technique Nathan C. Myers.

Please do vote and comment on this article which will help me to improve quality of my next article. Thanks.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

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

 
GeneralJust a small note Pinmember__PPS__14-Oct-04 11:13 
GeneralRe: Just a small note PinmemberRohit Joshi15-Oct-04 3:57 
GeneralRe: Just a small note Pinmember__PPS__15-Oct-04 5:36 
GeneralRe: Just a small note PinmemberRohit Joshi15-Oct-04 8:24 
Generalsubmitting to Loki or source forge PinsussAnonymous4-Sep-04 5:25 
GeneralRe: submitting to Loki or source forge PinmemberRohit Joshi4-Sep-04 6:10 
GeneralReally good article PinsussAnonymous3-Sep-04 21:53 
GeneralRe: Really good article PinsussAnonymous3-Sep-04 22:01 
Good job. does it provide multithreading support???
GeneralRe: Really good article PinmemberRohit Joshi4-Sep-04 6:07 
GeneralRe: Really good article PinmemberRohit Joshi4-Sep-04 6:08 

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
Web04 | 2.8.140814.1 | Last Updated 4 Sep 2004
Article Copyright 2004 by Rohit Joshi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid