/***************************************************************************
                   Work Pool Class (WorkPool.cpp)
                -----------------------------------------

	begin      : 2013/06/04
	copyright  : (C) 2013 Solbox Inc.
	author     : Development 1 Team
					- 2013/06/04 - 1st dadamin
	email      : dev1@solbox.com
	version    : 3.2.0

	CopyRight(C) 2005 Solbox Inc. All Rights reserved.
	Redistribution and use in source and binary forms, with or with out
	modification, are not permitted in outside of Solbox Inc.
***************************************************************************/
#include "cc_statd.h"
#include "WorkPool.h"
#include "DataDefine.h"
#include "DBManager.h"
#include "Work.h"
#include "InsertData.h"
#include "Configs.h"
#include "Logger.h"

// class CWorkThread
CWorkThread::CWorkThread()
	:  m_datatype(CWorkThread::UNKNOWN), m_clientfd(SOCKET_NOT_VALID), m_stop(false),
	  m_dbManager(NULL), m_pools(NULL), m_key(0)
{
	pthread_mutex_init(&m_lock, NULL);
	pthread_cond_init(&m_cond, NULL);
}

CWorkThread::~CWorkThread()
{
	m_stop = true;
	pthread_mutex_destroy(&m_lock);
	pthread_cond_destroy(&m_cond);
}

void CWorkThread::WaitSignal()
{	
	_LOG(LDEV, "Work Thread[%u] Waiting...", (unsigned int)pthread_self());
	pthread_mutex_lock(&m_lock);
	pthread_cond_wait(&m_cond, &m_lock);
	pthread_mutex_unlock(&m_lock);
}

void* CWorkThread::WorkFn( void* pdata )
{
	CWorkThread* pObject = reinterpret_cast<CWorkThread *>(pdata);
	_LOG(LDEV1, "Work Thread[%u] Start..", (unsigned int)pthread_self());
	while(!pObject->m_stop)
	{
		// 신호가 도착하면 현재 처리해야할 소켓(or 파일)로부터 데이터 읽음
		pObject->WaitSignal();
		
		// run		
		pObject->Running();

		// complete
		pObject->Completed();		
	}

	_LOG(LDEV1, "Work Thread[%u] End.", (unsigned int)pthread_self());
	return 0;
}

bool CWorkThread::CreateWorkThread( CWorkPool* pools, CDBManager * manager )
{
	pthread_t workthread;
	m_pools = pools;
	m_dbManager = manager;	
	int ret = pthread_create(&workthread, 0,  CWorkThread::WorkFn, (void*)this);
	if (ret != 0) 
	{
		ostringstream msg;
		msg << "Work Thread create failed.[" << errno << "]";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	sleep(0);

	return true;
}

bool CWorkThread::ReadFromSocket()
{
	CWorkSocket s(m_clientfd);
	
	int r = 0;
	int nBodySize = 0;

	s.SetOption();

	// read head
	if((r = s.ReadHead(nBodySize)) <= 0 )
	{
		LOG(LERR, "Failed to read header.[%d][%s]", r, m_clientip.c_str());
		return false;
	}
	
	m_readdata.resize(nBodySize);
	
	// read body
	if((r = s.ReadBody(m_readdata)) <= 0 )
	{
		LOG(LERR, "Failed to read body.[read size: %d][%s]", r, m_clientip.c_str());
		return false;
	}

	return true;
}

bool CWorkThread::ReadFromFile()
{
	CWorkFile f(m_filename);

	if(!f.ReadWorkFile(m_readdata))
		return false;

	return true;
}

bool CWorkThread::ReadData()
{
	bool r = true;
	LOG(LDEV1, "Setp 1. Read Data.");
	switch(m_datatype)
	{
	case CWorkThread::SOCKET_DATA:
		LOG(LDEV1, "Socket data.");
		r = ReadFromSocket();
		break;
	case CWorkThread::FILE_DATA:
		LOG(LDEV1, "File data.");
		r = ReadFromFile();
		break;
	case CWorkThread::UNKNOWN:
	default:
		r = false;
		LOG(LERR, "Unknown data.");
		break;
	}

	return r;
}

bool CWorkThread::GetServiceInfo(const Node* node,  InsertDataMap::iterator &iter)
{
	ostringstream msg;

	const Element* ext = dynamic_cast<const Element*>(node);

	if( ext )
	{		
		string svcRC = ext->get_attribute_value(XML_SVC_RC);
		string svcUserSEQ = ext->get_attribute_value(XML_SVC_USER_SEQ);
		string svcSEQ = ext->get_attribute_value(XML_SVC_SEQ);
		string svcName = ext->get_attribute_value(XML_SVC_NAME);

		if(svcRC.empty())
		{
			msg << "XML Parser error. Service "XML_SVC_RC" empty.";
			LOGACONSOLE(LERR, msg);
			return false;
		}

		if(svcUserSEQ.empty())
		{
			msg << "XML Parser error. Service "XML_SVC_USER_SEQ" empty.";
			LOGACONSOLE(LERR, msg);
			return false;
		}

		if(svcSEQ.empty())
		{
			msg << "XML Parser error. Service "XML_SVC_SEQ" empty.";
			LOGACONSOLE(LERR, msg);
			return false;
		}

		// 2013-11-15 : 신규 rc_statd 에서 해당 정보 보낼 수 없으므로 해당 체크 기능 삭제
		//if(svcName.empty())
		//{
		//	msg << "XML Parser error. Service "XML_SVC_NAME" empty.";
		//	LOGACONSOLE(LERR, msg);
		//	return false;
		//}

		multimap<string,CInsertData>::iterator it;

		iter = m_insertdata.insert(pair<string, CInsertData>
			(svcSEQ, CInsertData(svcRC, svcUserSEQ, svcSEQ, svcName)));
	}
	else 
	{
		msg << "XML Parser error. Service attributes error.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	return true;
}

bool CWorkThread::GetXNLElementValue(const Node* node, string elmentName, string & ret)
{
	xmlpp::Node* n;

	if( elmentName.empty() )
	{
		n = const_cast<xmlpp::Node*>(node);
	}
	else
	{
		n = node->get_children(elmentName).front();

		if( n == NULL || node->get_children(elmentName).size() == 0 )
			return false;
	}

	const xmlpp::Element* nodeElement = dynamic_cast<const Element*>(n);
	if(nodeElement)
	{
		const TextNode*  nodetext = nodeElement->get_child_text();
		if(nodetext)
		{
			ret = nodetext->get_content();
		}
		else
		{
			// 항목은 있으나 값이 없는 경우
			LOG(LNOT, "XML %s value of the item does not exist. And the default(0) value is set.", elmentName.c_str());
			ret = "0";
		}
	}
	else
	{
		return false;
	}

	return true;
}

bool CWorkThread::GetStatValue(string statType, const Node* node, InsertDataMap::iterator &it)
{
	string slist;

	// Data Extraction do list
	if(statType.compare(XML_STAT_ACCESS) == 0)
		slist = XML_STAT_ACCESS_VALUE_LIST;
	else if (statType.compare(XML_STAT_STORAGE) == 0)
		slist = XML_STAT_STORAGE_VALUE_LIST;
	else if (statType.compare(XML_STAT_NETWORK) == 0)
		slist = XML_STAT_NETWORK_VALUE_LIST;
	else if (statType.compare(XML_STAT_TRANSFER) == 0)
		slist = XML_STAT_TRANSFER_VALUE_LIST;
	else 
	{
		LOG(LERR, "XML Parser error. Unknown STAT type.");
		return false;
	}

	_LOG(LDEV1, "STAT items %s", slist.c_str());

	vector< string >  vec;
	StringSplit(slist, ",", vec, false);

	if(vec.size() < 1)
	{
		LOG(LERR, "STAT Items extraction failed.");
		return false;
	}

	// Data Extraction
	string v;
	for (vector<string>::iterator iter = vec.begin() ; iter != vec.end(); ++iter)
	{
		
		if( GetXNLElementValue(node, *iter, v) == false)
		{
			LOG(LERR, "XML Parser error. STAT %s Items %s empty."
				, statType.c_str(), (*iter).c_str());
			return false;
		}	
		
		LOG(LDEV, "XML %s Value : %s = %s", 
			statType.c_str(), (*iter).c_str(), v.c_str());
		
		if( it->second.SetStat(*iter, v) == false)
		{
			LOG(LERR, "XML Parser error. STAT %s Items %s duplicate."
				, statType.c_str(), (*iter).c_str());
			return false;
		}
	}

	return true;
}

bool CWorkThread::GetStatTimeValue(string statType, const Node* node, InsertDataMap::iterator &iter)
{
	ostringstream msg;
		
	const Element* ext = dynamic_cast<const Element*>(node);
	if(ext)
	{
		// get Time attribute
		string svctime = ext->get_attribute_value(XML_STAT_TIME);
		msg << "XML DATA : STAT " << statType << " TIME = " << svctime;
		LOGACONSOLE(LDEV1, msg);

		if( svctime.empty() )
		{
			msg << "XML Parser error. " << statType << " time value empty.";
			LOGACONSOLE(LERR, msg);
			return false;
		}

		iter->second.MakeStatKey(statType, svctime);
		if( iter->second.SetStat(XML_STAT_TIME, svctime) == false)
		{
			msg << "XML Parser error. " << statType << " time value duplicate.";
			LOGACONSOLE(LERR, msg);
			return false;
		}
	}
	else 
	{
		msg << "XML Parser error. " << statType << " attribute empty.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	return true;
}

int CWorkThread::GetStatSubData(string statType, const Node* node, InsertDataMap::iterator &it)
{
	ostringstream msg;

	// statType : ACCESS, STORAGE, NETWORK, TRANSFER
	Node::NodeList list = node->get_children(statType);
	
	msg << "XML DATA : STAT " << statType << " Data size = " << list.size();
	LOGACONSOLE(LDBG, msg);

	for(Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
	{
		msg << "XML DATA : STAT " << statType <<" Index[" << distance(list.begin(), iter) 
			<< "],[" << (*iter)->get_name() <<"]";
		LOGACONSOLE(LDEV1, msg);

		// TIME
		if(GetStatTimeValue(statType, *iter, it) == false)
			return -1;

		// value
		if(GetStatValue(statType, *iter, it) == false)
			return -1;

		it->second.ClearKey();
	}

	return list.size();
}

bool CWorkThread::GetStatSubList(const Node* node, InsertDataMap::iterator &iter)
{
	int r = 0, n = 0;

	// ACCESS
	n = GetStatSubData(XML_STAT_ACCESS, node, iter);
	if(n < 0 ) return false;
	r += n;

	// STORAGE
	n = GetStatSubData(XML_STAT_STORAGE, node, iter);
	if(n < 0 ) return false;
	r += n;

	// NETWORK
	n = GetStatSubData(XML_STAT_NETWORK, node, iter);
	if(n < 0 ) return false;
	r += n;

	// TRANSFER
	n = GetStatSubData(XML_STAT_TRANSFER, node, iter);
	if(n < 0 ) return false;
	r += n;

	if( r == 0 )
	{
		LOG(LERR, "XML Parser error. %s data empty.", XML_STAT);
	}

	return (r > 0);
}

bool CWorkThread::GetStatData(const Node* node, InsertDataMap::iterator &it)
{
	bool r = true;
	ostringstream msg;

	Node::NodeList statlist = node->get_children(XML_STAT);
	msg << "XML DATA : STAT Total = " << statlist.size();
	LOGACONSOLE(LDEV, msg);

	if( statlist.size() < 1)
	{
		r = false;
		msg << "XML Parser error. " XML_STAT " empty.";
		LOGACONSOLE(LERR, msg);
	}

	unsigned int success = 0;
	for(Node::NodeList::iterator iter = statlist.begin(); iter != statlist.end(); ++iter)
	{
		msg << "XML DATA : STAT Index[" << distance(statlist.begin(), iter) 
			<< "],[" << (*iter)->get_name() <<"]";
		LOGACONSOLE(LDEV1, msg);

		// ACCESS, STORAGE, NETWORK, TRANSFER
		if(GetStatSubList(*iter, it) == false)
		{
			r = false;
			break;
		}

		++ success;
	}

	if(success != statlist.size()) 
	{
		msg << "XML Parser error. STAT Total(" << statlist.size() 
			<< ")/Success(" << success << ")";

		LOGACONSOLE(LERR, msg);
	}

	return r;
}

bool CWorkThread::GetFailDatabaseType(const Node* node, InsertDataMap::iterator &it)
{
	ostringstream msg;

	const Element* ext = dynamic_cast<const Element*>(node);
	if(ext)
	{
		// get Time attribute
		string failtype = ext->get_attribute_value(XML_FAIL_TYPE);
		msg << "XML DATA : FAIL DATABASE TYPE = " << failtype;
		LOGACONSOLE(LDEV1, msg);

		if( failtype.empty() )
		{
			msg << "XML Parser error. "XML_FAIL_DATABASE" "XML_FAIL_TYPE " value empty.";
			LOGACONSOLE(LERR, msg);
			return false;
		}

		it->second.SetFailType(failtype);
	}
	else 
	{
		msg << "XML Parser error. "XML_FAIL_DATABASE" attribute empty.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	return true;
}

bool CWorkThread::GetFailDatabaseList(const Node* node, InsertDataMap::iterator &it)
{
	ostringstream msg;

	Node::NodeList list = node->get_children(XML_FAIL_DATABASE);

	if( list.size() < 1 ) 
	{
		msg << "XML Parser error. " XML_FAIL_DATABASE " etmpy.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	msg << "XML DATA : FAIL Data size = " << list.size();
	LOGACONSOLE(LDBG, msg);

	for(Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
	{
		// TYPE
		if(GetFailDatabaseType(*iter, it) == false)
			return false;
	}

	return true;
}

bool CWorkThread::GetFailData(const Node* node, InsertDataMap::iterator &it)
{
	ostringstream msg;

	// FAIL
	Node::NodeList faillist = node->get_children(XML_FAIL);
	msg << "Fail Cnt = " << faillist.size();
	LOGACONSOLE(LDEV, msg);

	if( faillist.size() < 1)
	{
		msg << "XML "XML_FAIL" empty.";
		LOGACONSOLE(LDBG, msg);
	}

	for(Node::NodeList::iterator iter = faillist.begin(); iter != faillist.end(); ++iter)
	{
		// DATABASE 
		msg << "XML DATA : FAIL Index[" << distance(faillist.begin(), iter) 
			<< "],[" << (*iter)->get_name() <<"]";
		LOGACONSOLE(LDEV1, msg);
		if( GetFailDatabaseList(*iter, it) == false )
			return false;
	}

	return true;
}

bool CWorkThread::DataParsing(Document *doc)
{
	ostringstream msg;
	if(!doc)
	{
		msg << "XML Parser error. Document is NULL.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	
	Element *root = doc->get_root_node();

	if(!root)
	{
		msg << "XML Parser error. ROOT is NULL.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	// check root
	if(root->get_name() != XML_STR_ROOT)
	{
		msg << "XML Parser error. XML root node different.[" 
			<< root->get_name() << "," << XML_STR_ROOT <<"]";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	// check service
	Node::NodeList svclist = root->get_children(XML_SVC);

	if( svclist.size() < 1 )
	{
		msg << "XML Parser error. XML "XML_SVC" empty.";
		LOGACONSOLE(LERR, msg);
		return false;
	}

	bool r = true;
	// service list
	for(Node::NodeList::iterator iter = svclist.begin(); iter != svclist.end(); ++iter)
	{  
		InsertDataMap::iterator it;
		// get service attribute
		if(GetServiceInfo(*iter, it) == false )
		{
			r = false;
			break;
		}

		msg << "Service Info : RC("<< it->second.GetRC() <<"),UserSEQ(" 
			<< it->second.GetUserSEQ() <<"),";
		msg << "Service SEQ("<< it->second.GetSvcSEQ() <<"),Service Name(" 
			<< it->second.GetSvcName() << ")";
		LOGACONSOLE(LDEV, msg);

		// get stat
		if( GetStatData(*iter, it) == false )
		{
			r = false;
			break;
		}

		// get fail
		if( GetFailData(*iter, it) == false )
		{
			r = false;
			break;
		}
	}

	return r;
}

bool CWorkThread::XMLParser()
{
	bool r = true;
	ostringstream msg;
	DomParser parser;

	LOG(LDEV1, "Setp 2. XML Parser.");
	try
	{
		parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.

		parser.parse_memory(m_readdata);

		if(parser)
		{
			r = DataParsing(parser.get_document());
		}
		else 
		{
			r = false;
			msg << "XML Parser error. Parser is NULL";
			LOGACONSOLE(LERR, msg);
		}
	}
	catch(const std::exception& ex)
	{
		r = false;
		msg << "XML Parser error. Exception caught: " << ex.what();
		LOGACONSOLE(LERR, msg);
	}

	if( r != true )
	{
		msg << "XML DATA  : START " << endl;
		msg << m_readdata;
		msg << "XML DATA  : END " << endl;
		LOGACONSOLE(LERR, msg);
	}
	
	return r;
}

void CWorkThread::SetUsedDB(CInsertData &idata, vector<string> & odata)
{
	int n = idata.FailTypeSize();

	if( n > 0 )
	{
		for(int i = 0; i < n; ++i)
		{
			string k = idata.GetFailType(i);
			if( CMyConfig::GetInstance()->FindUsedDBType(k) == false )
			{
				LOG(LWAR, "It is not in the database type a value of '%s'", 
							k.c_str());
			}
			else
			{
				odata.push_back(k);
			}
		}
	}
	else 
	{
		n = CMyConfig::GetInstance()->GetUsedDBTypeCnt();
		for(int i = 0; i < n; ++i)
		{
			odata.push_back(CMyConfig::GetInstance()->GetUsedDBType(i));
		}
	}
}

bool CWorkThread::InsertData()
{
	LOG(LDEV1, "Setp 3. Insert Data.");
	
	for( InsertDataMap::iterator it =  m_insertdata.begin(); it != m_insertdata.end(); ++it)
	{
#ifdef _DEBUG
		it->second.PrintStatAll();
#endif // _DEBUG

		// 삽입할 데이데 베이스 종류 정의
		vector<string> usedDbType;
		SetUsedDB(it->second, usedDbType);
		if( usedDbType.empty() )
		{
			LOG(LNOT, "Type of database that you want to insert the data does not exist.");
			continue;
		}

		// insert
		for(unsigned int i = 0; i < usedDbType.size(); ++i)
		{
			LOG(LDEV1, "Used DB Type %s", usedDbType[i].c_str());
			it->second.ClearJobLog();
			it->second.MakeInsertFailKey(usedDbType[i]);
						
			it->second.ExecuteInsert(usedDbType[i], m_dbManager);
			_LOG(LINF, "Work Log: [%s] [%s] => SUCCESS: %s, FAIL: %s", usedDbType[i].c_str(),
				it->second.GetSvcSEQ(), it->second.GetSuccess(), it->second.GetFail())
		}
	}

	return true;
}

int CWorkThread::WriteFailLog()
{
	LOG(LDEV1, "Setp 4. Write Fail Log.");
	ostringstream msg;

	try
	{
		Document document;
		// CCSTAT
		Element* nodeRoot = document.create_root_node(XML_STR_ROOT, "", "");

		for( InsertDataMap::iterator it =  m_insertdata.begin(); it != m_insertdata.end(); ++it)
		{
	#ifdef _DEBUG
			it->second.PrintInsertFailAll();
	#endif // _DEBUG
			it->second.MakeXMLNode( nodeRoot );
		}

		Node::NodeList faillist = nodeRoot->get_children();

		// save fail xml
		if(faillist.size() > 0)
		{			
			int key = rand() %10000;
			// Get Current Data & Time
			time_t now = time( NULL );
			struct tm timeNow;			
			localtime_r( &now, &timeNow );

			char failname[255] = {0};

			// 로그 파일명 규칙 : cc_statd_YYYYMMDDHHmmSS_RandomKey.xml						
			sprintf(failname, "%s/%s_%04d%02d%02d%02d%02d%02d_%04d.xml"
					, CMyConfig::GetInstance()->GetFailLogPath(), PROG_NAME
					, timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday
					, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec, key);
			_LOG(LINF, "Save Fail xml : %s", failname);
			document.write_to_file(failname);

			return 1;
		}
	}
	catch(const std::exception& ex)
	{
		msg << "Exception caught: " << ex.what();
		LOGACONSOLE(LERR, msg);
		return -1;
	}

	return 0;
}

void CWorkThread::Running()
{
	if(m_stop) 		return;

	int nstep = 0;
	bool success = false, stepend = false;
	_LOG(LDBG, "Work Thread[%u] Running...", (unsigned int)pthread_self());
	
	do 
	{
		++nstep;
		switch(nstep)
		{
		case 1: // read data
			success = ReadData();
			break;
		case 2: // XML Parser
			success = XMLParser();
			break;
		case 3: // insert data
			success = InsertData();
			break;
		case 4: // write fail log
			stepend = true;
			// 부분 오류를 대비하여 fail로그 작업이 있다면
			// 현재 요청에 대해서 실패로 처리함
			success =  (WriteFailLog() == 0) ? true : false; 
			break;
		default:
			success = false;
			LOG(LERR, "Unknown job step.");
			break;
		}

		LOG(LDBG, "Work Thread[%u] : Setp %d => Result %d",  
			(unsigned int)pthread_self(), nstep, success);

		if(stepend)
			break;
	} while (success);

	// write log
	if(success)
	{
		LOG(LINF, "Work Success. [%s]"
			, (m_clientip.empty() ? m_filename.c_str() : m_clientip.c_str()));
	}
	else 
	{
		LOG(LERR, "Work Fail. [%s]"
			, (m_clientip.empty() ? m_filename.c_str() : m_clientip.c_str()));
	}
}

void CWorkThread::Completed()
{
	if(m_stop) 		return;

	_LOG(LDEV, "Work Thread[%u] Completion.", (unsigned int)pthread_self());
	m_datatype = CWorkThread::UNKNOWN;
	m_filename.clear();
	m_clientip.clear();
	m_clientfd = SOCKET_NOT_VALID;
	m_readdata.clear();
	m_insertdata.clear();

	ReleasePool();
}

void CWorkThread::Stop()
{
	_LOG(LDEV1, "Work Thread[%u] Stop.", (unsigned int)pthread_self());
	m_stop = true;	
	pthread_cond_signal(&m_cond);
	pthread_join(pthread_self(), NULL);
}

void CWorkThread::RunWork(int client, string ipstr)
{
	if(m_stop) 		return;

	m_datatype = CWorkThread::SOCKET_DATA;
	m_clientfd = client;
	m_clientip = ipstr;
	pthread_cond_signal(&m_cond);
}

void CWorkThread::RunWork(string path)
{
	if(m_stop) 		return;

	m_datatype = CWorkThread::FILE_DATA;
	m_filename = path;
	pthread_cond_signal(&m_cond);
}

void CWorkThread::ReleasePool()
{
	if(m_stop) 		return;

	if(m_pools)
	{
		m_pools->ReleaseWork(this);		
	}
}

// class CWorkPool
CWorkPool::CWorkPool()
	: m_size(0), m_exit(false)
{
	pthread_mutex_init(&m_lock, NULL);
}

CWorkPool::~CWorkPool()
{
	Finalized();
	pthread_mutex_destroy(&m_lock);	
}

void CWorkPool::Finalized()
{
	if(m_exit)	return;

	m_exit = true;
	_LOG(LDEV1, "Work Pool Finalized.");
	multimap<int, CWorkThread*>::iterator mi;
	mi = m_pools.begin();
	while(mi != m_pools.end())
	{
		mi->second->Stop();
		delete mi->second;
		mi ++;
	}

	m_pools.clear();
}

bool CWorkPool::CreatePool(int size, CDBManager * manager)
{
	_LOG(LDBG, "Work Threand CNT = %d", size);
	srand(time( NULL));
	m_size = size;
	for (int i = 0; i < m_size; i++)
	{
		CWorkThread * work = new CWorkThread();		
		if(work)
		{
			if(work->CreateWorkThread(this, manager) == false)
				return false;
			m_pools.insert( pair<int, CWorkThread*>(0, work));
		}
		else
		{
			LOG(LERR, "Failed Work Thread allocation.");
			return false;
		}
	}

	return true;
}

CWorkThread* CWorkPool::GetWork()
{
	CWorkThread *p = NULL;
	
	while( !m_exit && p == NULL )
	{
		pthread_mutex_lock(&m_lock);
		multimap<int, CWorkThread*>::iterator it;
		it = m_pools.begin();
		if( it->first )
		{
			LOG(LNOT, "Work Pool Full.[PID %d]", getpid());
			pthread_mutex_unlock(&m_lock);
			sleep(1);
		}
		else 
		{
			int key = rand() %100 +1;
			p =it->second;
			m_pools.erase(it);
			it = m_pools.insert(pair<int, CWorkThread*>(key, p));
			LOG(LDEV, "Get Work Key [%d]", key);
			p->SetPoolKey(key);
			pthread_mutex_unlock(&m_lock);
		}		
	}
	return p;
}

void CWorkPool::ReleaseWork(CWorkThread * work )
{
	pthread_mutex_lock(&m_lock);

	pair <multimap<int, CWorkThread*>::iterator, multimap<int, CWorkThread*>::iterator> ret;
	ret = m_pools.equal_range(work->GetPoolKey());
	LOG(LDEV, "Release Work Key [%d]", work->GetPoolKey());
	for (multimap<int, CWorkThread*>::iterator it=ret.first; it!=ret.second; ++it)
	{
		if( it->second == work )
		{
			m_pools.erase(it);
			m_pools.insert(pair<int, CWorkThread*>(0, work));
			break;
		}
	}

	pthread_mutex_unlock(&m_lock);
}
