Thread portable class


Gate portable class


Timer class


Pool class


Terimber 2.0


About C++


Downloads Products & Services Support Clients Open Source About



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
}



© Copyright Terimber 2003-.