Home / Open source / Pool
Template Pool class
This is an example how to create template pool class to handle different types of resources.
/*
* The Software License
* ====================================================================
* Copyright (c) 2003 The Terimber Corporation. All rights
* reserved.
* ====================================================================
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE TERIMBER CORPORATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
#ifndef _terimber_template_h_
#define _terimber_template_h_
#include "list.h"
#include "thread.h"
#include "gate.h"
// base proto class for creators
template < class T, class A >
class proto_creator
{
public:
typedef T TYPE;
typedef A ARG;
};
// class supports the idea of reusing expensive resources like threads, db connections, socket connections, memory allocators and so on.
// the more abstract template CREATOR is used - class, which knows how to create, destroy, activate, deactivate resource.
// class supports implementation of pool of objects
template < class C > // creator - responsable for creation and deleting objects
class pool
{
// VC 6 specific
typedef typename C::TYPE TYPE;
typedef typename C::ARG ARG;
// class keeps pointer to a pool entry object
// we can't remove internl class into hpp file
// Microsoft specific
class pool_entry
{
public:
// constructor
pool_entry(TYPE* obj) : _obj(obj), _used(true) {}
// copy constructor
pool_entry(const pool_entry& src) { *this = src; }
// assign operator
pool_entry& operator=(const pool_entry& src)
{
if (this != &src)
{
_obj = src._obj;
_used = src._used;
}
return *this;
}
// lock pool entry to prevent multiple owners
inline bool lock(C& creator, const ARG& arg) const
{
if (!_used && creator.find(_obj, arg))
return (_used = true);
else
return false;
}
// unlock pool entry to indicate that the object is available
inline bool unlock(C& creator, const ARG& arg) const
{
if (!_used)
return false;
else
{
creator.back(_obj, arg);
return !(_used = false);
}
}
// destroy object
inline void clear(C& creator, const ARG& arg)
{
if (!_obj) return; // check pointer
assert(!_used); // must not be in use
// destroy object
creator.destroy(_obj, arg);
_obj = 0;
}
// deactivate object
inline void deactivate(C& creator, const ARG& arg)
{
if (_obj && !_used)
creator.deactivate(_obj, arg);
}
// return pointer to object
inline TYPE* get_object() const { return _obj; }
private:
TYPE* _obj; // keep pointer to the object
mutable bool _used; // flag of using
};
// class for STL algorithm find
class loan_pool_object
{
public:
// constructor
explicit loan_pool_object(C& creator, const ARG& arg, TYPE*& obj) :
_creator(creator), _arg(arg), _obj(obj)
{}
// comparision operator
inline bool operator()(const pool_entry& obj) const
{
if (!obj.lock(_creator, _arg)) return false;
_obj = obj.get_object();
return true;
}
private:
C& _creator; // keep the reference to the creator
const ARG& _arg; // keep the reference to the argument for creator
TYPE*& _obj; // reference to pointer to the object
};
// class for STL algorithm find
class return_pool_object
{
public:
// constructor
explicit return_pool_object(C& creator, const ARG& arg, TYPE*& obj) :
_creator(creator), _arg(arg), _obj(obj)
{}
// comparision operator
inline bool operator()(const pool_entry& entry) const
{
if (_obj != entry.get_object() || !entry.unlock(_creator, _arg)) return false;
_obj = 0;
return true;
}
private:
C& _creator; // keep the reference to the creator
const ARG& _arg; // keep the reference to the argument for creator
TYPE*& _obj; // reference to pointer to the object
};
// class for STL algorithm for_each
class clear_pool
{
public:
// constructor
explicit clear_pool(C& creator, const ARG& arg) :
_creator(creator), _arg(arg)
{}
// action function operator
inline void operator()(pool_entry& obj) { obj.clear(_creator, _arg); }
private:
C& _creator; // keep the reference to the creator
const ARG& _arg; // keep the reference to the argument for creator
};
// class for STL algorithm for_each
class deactivate_pool
{
public:
// constructor
explicit deactivate_pool(C& creator, const ARG& arg) :
_creator(creator), _arg(arg)
{}
// action function operator
inline void operator()(pool_entry& obj) { obj.deactivate(_creator, _arg); }
private:
C& _creator; // keep the reference to the creator
const ARG& _arg; // keep the reference to the argument for creator
};
// keep object entries as a STL list
typedef std::list< pool_entry > list_pool_entry_t;
public:
// constructor
// sometimes we don't need to create a special object as a creator, because creation of object can be done in a general way like call new operator
// so all we need is a static function, let call it statis_constructor
pool< C >(C& creator = C::static_constructor(), size_t pool_size = os_def_size);
// destructor
~pool< C >();
// return object
// again let assume that it can be default argument and default timeout as a static functions of creator
inline TYPE* loan_object(const ARG& arg = C::get_default_arg(), size_t timeout = C::get_default_timeout());
// return object back to pool
inline void return_object(TYPE* obj, const ARG& arg = C::get_default_arg());
// clear pool
void clear(const ARG& arg = C::get_default_arg());
// deactivate objects in the pool
void deactivate(const ARG& arg = C::get_default_arg());
private:
C& _creator; // creator reference
gate _gate; // gate controls max number of objects
mutex _mtx; // controls multithreaded access to pool
list_pool_entry_t _pool; // pool of object entries
};
// template class supports smart allocation and deallocation the pointer to the objects
// creator must provide two function T* create(size_t) & destroy(T*)
template < class C > // type of creator
class smart_pointer
{
public:
typedef typename C::TYPE TYPE;
typedef typename C::ARG ARG;
// constructor
// object internally created
explicit smart_pointer< C >(C& crt, ARG n);
// constructor
// object will later assign explicitly
explicit smart_pointer< C >(C& crt);
// destructor
~smart_pointer< C >();
// assign operator
smart_pointer< C >& operator=(const TYPE* src);
// access operators
operator smart_pointer< C >::TYPE*() { return _ptr; }
operator const smart_pointer< C >::TYPE*() const { return _ptr; }
smart_pointer< C >::TYPE* operator->();
const smart_pointer< C >::TYPE* operator->() const;
// access operator to the address of pointer to object
// interface support
smart_pointer< C >::TYPE** operator&();
// check object
bool operator!() const;
// check object
operator bool() const;
// destroy object
void clear();
// detach object
smart_pointer< C >::TYPE* detach();
// attach new object and free old object if specified
void attach(TYPE* obj, bool free = true);
private:
smart_pointer< C >::TYPE* _ptr; // keeps pointer to object
C& _crt; // keeps reference to creator
};
template < class P >
class pool_object_keeper
{
public:
typedef typename P::CREATOR::TYPE TYPE;
typedef typename P::CREATOR::ARG ARG;
explicit pool_object_keeper< P >(P* pool_, TYPE* obj);
explicit pool_object_keeper< P >(P* pool_, const ARG& arg, size_t timeout);
~pool_object_keeper();
inline pool_object_keeper< P >::TYPE* operator->();
inline const pool_object_keeper< P >::TYPE* operator->() const;
inline operator pool_object_keeper< P >::TYPE*() { return _obj; }
inline operator const pool_object_keeper< P >::TYPE*() const { return _obj; }
bool operator!() const;
operator bool() const;
private:
P* _pool;
pool_object_keeper< P >::TYPE* _obj;
};
///////////////////////////////////////////////////////////////////////////////
// Here the implementation starts
///////////////////////////////////////////////////////////////////////////////
// thread safe algorithms
// class supports thread safe STL algorithms
template< class InIt, class Pred >
inline void find_if_safe(InIt first, InIt last, Pred& pr, const mutex& mtx)
{
mutex_keeper keeper(mtx);
std::find_if(first, last, pr);
}
template< class InIt, class Pred >
inline void for_each_safe(InIt first, InIt last, Pred& pr, const mutex& mtx)
{
mutex_keeper keeper(mtx);
std::for_each(first, last, pr);
}
///////////////////////////////////////////////////////////////////////////
// pool implementation
// constructor
template < class C >
pool< C >::pool< C >(C& creator, size_t pool_size) :
_creator(creator), _gate(pool_size ? pool_size : 1), _pool(pool_size ? pool_size : 1)
{
}
// destructor
template < class C >
pool< C >::~pool< C >()
{
clear();
}
// return object
template < class C >
inline
typename pool< C >::TYPE*
pool< C >::loan_object(const ARG& arg, size_t timeout)
{
// wait for availability
if (_gate.entry(timeout))
{
TYPE* obj = 0;
// try to find in the pool
loan_pool_object pred((C&)_creator, arg, obj);
find_if_safe(_pool.begin(), _pool.end(), pred, _mtx);
if (!obj) // need to create new object in the pool
{
if ((obj = _creator.create(arg))) // create successfully
{
pool_entry entry(obj);
_pool.push_back(entry); // stores an object in the pool
}
else // can't create new object
{
_gate.leave(); // leave gate
// return null pointer
return 0;
}
}
// activate object
_creator.activate(obj, arg);
// return result
return obj;
}
// no objects are available
return 0;
}
// return object back to pool
template < class C >
inline
void
pool< C >::return_object(TYPE* obj, const ARG& arg)
{
if (!obj)
return;
return_pool_object pred(_creator, arg, obj);
find_if_safe(_pool.begin(), _pool.end(), pred, _mtx);
if (!obj)
_gate.leave(); // release semaphore
else
assert(false); // try to return an object that is not in the pool
}
// clear pool
template < class C >
inline
void
pool< C >::clear(const ARG& arg)
{
// block internal calls
gate_keeper keeper(_gate,
#ifdef _DEBUG
10000 // 10 second for debug
#else
3000 // 3 second for release
#endif
);
assert(keeper); // can't lock gate - some other code of program didn't return borrowed resource
// destroy objects anyway
if (_pool.size())
{
clear_pool pred(_creator, arg);
for_each_safe(_pool.begin(), _pool.end(), pred, _mtx);
_pool.clear();
}
}
// deactivate objects in the pool
template < class C >
inline
void
pool< C >::deactivate(const ARG& arg)
{
deactivate_pool pred(_creator, arg);
for_each_safe(_pool.begin(), _pool.end(), pred, _mtx);
}
//////////////////////////////////////////////////////////////////
// template class supports smart allocation and deallocation the pointer to the objects
// creator must provide two function T* create(size_t) & void destroy(T*)
// constructor
// object internally created
template < class C >
smart_pointer< C >::smart_pointer< C >(C& crt, ARG n) :
_crt(crt)
{
_ptr = crt.create(n);
}
// constructor
// object will later assign explicitly
template < class C >
smart_pointer< C >::smart_pointer(C& crt) :
_crt(crt), _ptr(0)
{
}
// destructor
template < class C >
smart_pointer< C >::~smart_pointer()
{
clear();
}
// assign operator
template < class C >
inline
smart_pointer< C >&
smart_pointer< C >::operator=(const TYPE* src)
{
clear();
_ptr = (smart_pointer< C >::TYPE*)src;
return *this;
}
template < class C >
inline
typename smart_pointer< C >::TYPE*
smart_pointer< C >::operator->()
{
return _ptr;
}
template < class C >
inline
const typename smart_pointer< C >::TYPE*
smart_pointer< C >::operator->() const
{
return _ptr;
}
// access operator to the address of pointer to object
// interface support
template < class C >
inline
typename smart_pointer< C >::TYPE**
smart_pointer< C >::operator&()
{
return &_ptr;
}
// check object
template < class C >
inline
bool
smart_pointer< C >::operator!() const
{
return !_ptr;
}
// check object
template < class C >
inline
smart_pointer< C >::operator bool() const
{
return _ptr;
}
// destroy object
template < class C >
inline
void
smart_pointer< C >::clear()
{
if (_ptr)
{
_crt.destroy(_ptr);
_ptr = 0;
}
}
template < class C >
inline
typename smart_pointer< C >::TYPE*
smart_pointer< C >::detach()
{
smart_pointer< C >::TYPE* ptr = _ptr;
_ptr = 0;
return ptr;
}
// attach new object and free old object if specified
template < class C >
inline
void
smart_pointer< C >::attach(TYPE* obj, bool free)
{
free ? clear() : detach(); _ptr = obj;
}
/////////////////////////////////////////////////////
template < class P >
pool_object_keeper< P >::pool_object_keeper< P >(P* pool_, TYPE* obj) :
_pool(pool_), _obj(obj)
{
}
template < class P >
pool_object_keeper< P >::pool_object_keeper< P >(P* pool_, const ARG& arg, size_t timeout) :
_pool(pool_)
{
_obj = _pool ? _pool->loan_object(arg, timeout) : 0;
}
template < class P >
pool_object_keeper< P >::~pool_object_keeper< P >()
{
if (_pool && _obj)
_pool->return_object(_obj);
}
template < class P >
inline
typename pool_object_keeper< P >::TYPE*
pool_object_keeper< P >::operator->()
{
return _obj;
}
template < class P >
inline
const typename pool_object_keeper< P >::TYPE*
pool_object_keeper< P >::operator->() const
{
return _obj;
}
template < class P >
inline
bool
pool_object_keeper< P >::operator!() const
{
return !_obj;
}
template < class P >
inline
pool_object_keeper< P >::operator bool() const
{
return _obj;
}
#endif // _terimber_template_h_
//////////////////////////////////////////////////////////
// here is how we can implement pool creator for thread object
// as an example
class thread_creator : public proto_creator< thread, job_task >
{
public:
thread_creator();
static thread_creator& static_constructor();
static const job_task& get_default_arg();
static size_t get_default_timeout();
static thread* create(const job_task& task);
static void activate(thread* obj, const job_task&);
static bool find(thread* obj, const job_task& task);
static void back(thread* obj, const job_task& task);
static void destroy(thread* obj, const job_task& task);
static void deactivate(thread* obj, const job_task& task);
private:
static const job_task s_def_task;
static thread_creator s_constructor;
};
//////////////////////////////////
//job_employer* employer, size_t ident, size_t timeout, void* user_data
//static
const job_task thread_creator::s_def_task(0, 0, INFINITE, 0);
//static
thread_creator thread_creator::s_constructor;
thread_creator::thread_creator()
{
}
// static
thread_creator&
thread_creator::static_constructor()
{
return s_constructor;
}
// static
const job_task&
thread_creator::get_default_arg()
{ return s_def_task; }
//static
size_t
thread_creator::get_default_timeout()
{ return 10000; } // ok 10 second will be enough
// static
thread*
thread_creator::create(const job_task& task)
{
thread* obj = new thread; // don't start thread
obj->start(); // start thread and wait for job
return obj; // return object
}
//static
void
thread_creator::activate(thread* obj, const job_task& task)
{
if (obj->get_state() == THREAD_CLOSE)
obj->start();
obj->assign_job(task); // let assign job to activate thread
}
//static
bool
thread_creator::find(thread* obj, const job_task&)
{
return true; // all threads are equal
}
//static
void
thread_creator::back(thread* obj, const job_task&)
{
obj->cancel_job(); // thread in the pool and we must cancel job, but don't stop thread
}
// static
void
thread_creator::destroy(thread* obj, const job_task&)
{
obj->cancel_job(); // cancel job
obj->stop(); // stop thread
delete obj; // destroy object
}
// static
void
thread_creator::deactivate(thread* obj, const job_task& task)
{
obj->cancel_job(); // cancel job
obj->stop(); // stop thread
}
///////////////////////////////////////////////////////////////////
// and finally the example of using thread pool
typedef pool< thread_creator > thread_pool;
typedef pool_object_keeper< thread_pool > thread_pool_keeper;
// create pool
thread_pool pool_;
{
// define task
job_task task(...);
// keep resource in smart pointer
thread_pool_keeper keeper(&pool_, task, 1000);
// use thread
.......
// on destructor of thread_pool_keeper object thread will be returned to the pool
}
|
|