Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generic Pool: Policy based design

0.00/5 (No votes)
3 Sep 2004 2  
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 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