Overview
Features
Download
Documentation
Community
Add-Ons & Services

Object pool implementation

Discussion of ideas for features and new projects based on POCO.

Object pool implementation

Postby myk » 07 Apr 2011, 16:05

I couldn't find an implementation of an object pool in POCO libraries (such as http://www.boost.org/doc/libs/1_46_1/li ... _pool.html).
It would be useful for embedded and/or real-time systems.
Is there any alternative to it in POCO ?
myk
 
Posts: 4
Joined: 27 Oct 2010, 13:28

Re: Object pool implementation

Postby guenter » 07 Apr 2011, 19:17

Some time ago I've written a simple ObjectPool implementation for a project I've done.
You can find it below. Let me know if it works for you - it may be integrated into POCO some day.

Code: Select all
//
// ObjectPool.h
//
// $Id: //projects/AIC/include/AIC/ObjectPool.h#1 $
//
// Copyright (c) 2010, Applied Informatics Software Engineering GmbH.
// All rights reserved.
//


#ifndef AIC_ObjectPool_INCLUDED
#define AIC_ObjectPool_INCLUDED


#include "Poco/Poco.h"
#include "Poco/Mutex.h"
#include "Poco/AutoPtr.h"
#include "Poco/SharedPtr.h"
#include <vector>
#include <cctype>


namespace AIC {


template <class C, class P = C*>
class PoolableObjectFactory
   /// A PoolableObjectFactory is responsible for creating and resetting
   /// objects managed by an ObjectPool.
   ///
   /// Together with an ObjectPool, a PoolableObjectFactory is used as
   /// a policy class to change the behavior of the ObjectPool when
   /// creating new objects, returning used objects back to the pool
   /// and destroying objects, when the pool itself is destroyed or
   /// shrinked.
{
public:
   P createObject()
      /// Create and return a new object.
   {
      return new C;
   }
   
   bool validateObject(P pObject)
      /// Checks whether the object is still valid
      /// and can be reused.
      ///
      /// Returns true if the object is valid,
      /// false otherwise.
      ///
      /// To maintain the integrity of the pool, this method
      /// must not throw an exception.
   {
      return true;
   }
   
   void activateObject(P pObject)
      /// Called before an object is handed out by the pool.
      /// Also called for newly created objects, before
      /// they are given out for the first time.
   {
   }
   
   void deactivateObject(P pObject)
      /// Called after an object has been given back to the
      /// pool and the object is still valid (a prior call
      /// to validateObject() returned true).
      ///
      /// To maintain the integrity of the pool, this method
      /// must not throw an exception.
   {
   }
   
   void destroyObject(P pObject)
      /// Destroy an object.
      ///
      /// To maintain the integrity of the pool, this method
      /// must not throw an exception.
   {
      delete pObject;
   }
};


template <class C>
class PoolableObjectFactory <C, Poco::AutoPtr<C> >
{
public:
   Poco::AutoPtr<C> createObject()
   {
      return new C;
   }
   
   bool validateObject(Poco::AutoPtr<C> pObject)
   {
      return true;
   }
   
   void activateObject(Poco::AutoPtr<C> pObject)
   {
   }
   
   void deactivateObject(Poco::AutoPtr<C> pObject)
   {
   }
   
   void destroyObject(Poco::AutoPtr<C> pObject)
   {
   }
};


template <class C>
class PoolableObjectFactory <C, Poco::SharedPtr<C> >
{
public:
   Poco::SharedPtr<C> createObject()
   {
      return new C;
   }
   
   bool validateObject(Poco::SharedPtr<C> pObject)
   {
      return true;
   }
   
   void activateObject(Poco::SharedPtr<C> pObject)
   {
   }
   
   void deactivateObject(Poco::SharedPtr<C> pObject)
   {
   }
   
   void destroyObject(Poco::SharedPtr<C> pObject)
   {
   }
};


template <class C, class P = C*, class F = PoolableObjectFactory<C, P> >
class ObjectPool
   /// An ObjectPool manages a pool of objects of a certain class.
   ///
   /// The number of objects managed by the pool can be restricted.
   ///
   /// When an object is requested from the pool:
   ///   - If an object is available from the pool, an object from the pool is
   ///     removed from the pool, activated (using the factory) and returned.
   ///   - Otherwise, if the peak capacity of the pool has not yet been reached,
   ///     a new object is created and activated, using the object factory, and returned.
   ///   - If the peak capacity has already been reached, null is returned.
   ///
   /// When an object is returned to the pool:
   ///   - If the object is valid (checked by calling validateObject()
   ///     from the object factory), the object is deactivated. If the
   ///     number of objects in the pool is below the capacity,
   ///     the object is added to the pool. Otherwise it is destroyed.
   ///   - If the object is not valid, it is destroyed immediately.
{
public:
   ObjectPool(std::size_t capacity, std::size_t peakCapacity):
      /// Creates a new ObjectPool with the given capacity
      /// and peak capcacity.
      ///
      /// The PoolableObjectFactory must have a public default constructor.
      _capacity(capacity),
      _peakCapacity(peakCapacity),
      _size(0)
   {
      poco_assert (capacity <= peakCapacity);
   }
   
   ObjectPool(const F& factory, std::size_t capacity, std::size_t peakCapacity):
      /// Creates a new ObjectPool with the given PoolableObjectFactory,
      /// capacity and peak capacity. The PoolableObjectFactory must have
      /// a public copy constructor.
      _factory(factory),
      _capacity(capacity),
      _peakCapacity(peakCapacity),
      _size(0)
   {
      poco_assert (capacity <= peakCapacity);
   }
   
   ~ObjectPool()
      /// Destroys the ObjectPool.
   {
      for (typename std::vector<P>::iterator it = _pool.begin(); it != _pool.end(); ++it)
      {
         _factory.destroyObject(*it);
      }
   }
      
   P borrowObject()
      /// Obtains an object from the pool, or creates a new object if
      /// possible.
      ///
      /// Returns null if no object is available.
      ///
      /// If activating the object fails, the object is destroyed and
      /// the exception is passed on to the caller.
   {
      Poco::FastMutex::ScopedLock lock(_mutex);
      
      if (!_pool.empty())
      {
         P pObject = _pool.back();
         _pool.pop_back();
         return activateObject(pObject);
      }
      else if (_size < _peakCapacity)
      {
         P pObject = _factory.createObject();
         activateObject(pObject);
         _size++;
         return pObject;
      }
      else return 0;
   }
      
   void returnObject(P pObject)
      /// Returns an object to the pool.
   {
      Poco::FastMutex::ScopedLock lock(_mutex);

      if (_factory.validateObject(pObject))
      {
         _factory.deactivateObject(pObject);
         if (_pool.size() < _capacity)
         {
            _pool.push_back(pObject);
         }
         else
         {
            _factory.destroyObject(pObject);
            _size--;
         }
      }
      else
      {
         _factory.destroyObject(pObject);
      }
   }

protected:
   P activateObject(P pObject)
   {
      try
      {
         _factory.activateObject(pObject);
      }
      catch (...)
      {
         _factory.destroyObject(pObject);
         throw;
      }
      return pObject;
   }
   
private:
   ObjectPool();
   ObjectPool(const ObjectPool&);
   ObjectPool& operator = (const ObjectPool&);
   
   F _factory;
   std::size_t _capacity;
   std::size_t _peakCapacity;
   std::size_t _size;
   std::vector<P> _pool;
   Poco::FastMutex _mutex;
};


} // namespace AIC


#endif // AIC_ObjectPool_INCLUDED
guenter
 
Posts: 1121
Joined: 11 Jul 2006, 16:27
Location: Austria

Re: Object pool implementation

Postby myk » 13 Apr 2011, 13:16

The given object pool implementation is great and can be useful in many cases.
In my particular case I need the object pool that provides so-called managed pointers.
The managed pointer is a kind of a shared smart pointer that has in its context (in addition to a reference count and the object pointer)
a pointer/reference to the object owner/manager (i.e. the object pool in this case), so the managed pointer returns the managed object pointer to its owner when a reference counter hits zero.
In this case managed pointers' clients don't need to know when, to whom and how to return the object to its owner/pool.

Below, you can find a prototype of the extension to ObjectPool that provides managed pointers.
Unfortunately, I had to use std::tr1::shared_ptr instead of Poco::SharedPtr because the last doesn't allow to specify
a release policy/deleter object but just its class and the implementation calls the static method of this class when a reference counter hits zero.
As result, I wasn't able to specify/utilize the release policy object that has the object pool pointer in its context.
(Other options are to extend Poco::SharedPtr or to embed the object pool pointer into the object, but I find both of them not appropriate).

Thanks for your feedback.

Code: Select all
#ifndef MANAGEDOBJECTPOOL_H
#define MANAGEDOBJECTPOOL_H

#include "ObjectPool.h"
#include <tr1/memory>

using AIC::ObjectPool;

namespace Poco {

template <class C>
class  ManagedObjectPool;

template <class C>
class ManagedPtrDeleter {

public:
  ManagedPtrDeleter(ManagedObjectPool<C>*  pObjectPool): _pObjectPool(pObjectPool){}
 
 void operator()(C* pObject) {
  _pObjectPool->returnObject(pObject);
}

private:

  ManagedObjectPool<C>*   _pObjectPool;

};


template <class C>
class ManagedPtr: public std::tr1::shared_ptr<C> {

public:
ManagedPtr(C*  pObject, ManagedObjectPool<C>* pObjectPool):
 std::tr1::shared_ptr<C>(pObject, ManagedPtrDeleter<C>(pObjectPool)) {}
 
 ManagedPtr():std::tr1::shared_ptr<C>(){}

};

template <class C>
class  ManagedObjectPool:     
public  ObjectPool<C> {

public:

ManagedObjectPool(std::size_t capacity, std::size_t peakCapacity):
ObjectPool<C>(capacity, peakCapacity) {}

ManagedPtr<C> borrowObject() {
  return ManagedPtr<C>(ObjectPool<C>::borrowObject(), this);
}

private:

void returnObject(C* pObject) {
  ObjectPool<C>::returnObject(pObject);
}

friend class ManagedPtrDeleter<C>;

};

} //namespace Poco


#endif // MANAGEDOBJECTPOOL_H




Code: Select all

#include "ManagedObjectPool.h"
#include <iostream>

class IBuffer;
typedef Poco::ManagedObjectPool<IBuffer>  SharedBufPool;
typedef Poco::ManagedPtr<IBuffer>         SharedBuf;

// sample object to share
class IBuffer {

public:
  IBuffer() {
    buf = new char [100];
  }
 
  ~IBuffer() {
    if (buf != NULL) {
      delete[] buf;
      buf = NULL;
   }
  }
 
private:
  char* buf; 

};

static uint           thePoolCapacity = 2;
static SharedBufPool  thePool(thePoolCapacity , thePoolCapacity);

SharedBuf getSharedBuffer()
{
  return thePool.borrowObject();
}

#define LOG_RESULT(result) (std::cout << ((result)?"Ok":"Failed") << std::endl)

//Gets all objects from the pool and doesn't call pool->returnObject() explicitly.
//SharedBuf (ManagedPtr<>) should do the trick and returns the object to the pool
// once the reference counter hits zero.

bool getAllObjects(bool log)
{
  SharedBuf   buffer[thePoolCapacity];
  uint        ii = 0;
 
  for (ii = 0; ii < thePoolCapacity; ii++) {
 
    if (log)  std::cout << "Trying to get the buffer #" << (ii + 1) << "...";   
    buffer[ii] = getSharedBuffer();
    if (log) LOG_RESULT( NULL != buffer[ii].get() );
       
  }
 
  if (thePoolCapacity == ii)  return 1;
  else return 0;
 
}


int main()
{
  bool result = 0;
 
  std::cout << "Let's get all objects from the pool..." << std::endl;
  result = getAllObjects(1);
  LOG_RESULT(result);
 
  std::cout << "Let's check whether the objects were returned back to the pool...";
  result = getAllObjects(0);
  LOG_RESULT(result);
   
}
myk
 
Posts: 4
Joined: 27 Oct 2010, 13:28


Return to Wishlist

Who is online

Users browsing this forum: No registered users and 1 guest

cron