/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbThread
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbThread.h"
#include "xbMsg.h"
#include "xbConfig.h"
#include <eh.h>

#define xbThDbgOutput xbRdbgOutput
xbThreadCenter	g_xbThreadCenter("Base Thread Center");;

int g_xbThTrace = 0;
bool xbThToTrace()
{
	if (!g_xbThTrace)
		g_xbThTrace = g_xbCfgEngine.GetSettingBoolToNum("threadTrace");
	return g_xbThTrace == 1;
}

void xbThTrace(const xbString &txt)
{
	if (!xbThToTrace())
		return;

	xbRelOutput("[TH]"+txt);
}
//////////////////////////////////////////////////////////////////////////////////////////
// xbThreadCenter
sIxbThread *xbThreadCenter::createThread(const xbString &label,sIxbThreadBaseCb *pCb, bool bTryCloseOthers, bool bSuspended)
{
	xbLock lckStart(this);

	Thread *pTh = NULL;
	int cnt = 0;
	for(;;)
	{
		if (++cnt > xbTHREADCREATE_TRYCNT) 
			xbThrow xbEsys("xbThreadCenter::createThread("+label+") failure, too many creation try (thread cnt="+xbString(m_cThread)+")");

		if (m_cUseLock <= 0)
			xbThrow xbEsys("xbThreadCenter::createThread("+label+") failure: thread center closed or not started");

		threadUseLock();
		try
		{
			pTh = xbDnew Thread(this,label,pCb);

			addThread(pTh);

			if (!pTh->CreateThread(bSuspended?CREATE_SUSPENDED:0))
				xbThrow xbEsys(xbHR_LAST,"xbThreadCenter::createThread("+label+") failure (thread cnt="+xbString(m_cThread)+")");

			eProtectTrace(threadUseUnlock(););
			break;
		}
		catch(...)
		{
			eProtectTrace(threadUseUnlock(););

			delete pTh;
			pTh = NULL;
			if (!bTryCloseOthers) throw;
			if (!tryCloseOthers()) throw;
		}
	}
	return pTh;
}

bool xbThreadCenter::tryCloseOthers()
{
	xbSectLock lck(&m_sect);
	int	cThread = m_cThread;
	for (int no = 0; no < m_arrTh.Length; no++)
	{
		Thread *pTh = m_arrTh.GetAt(no);
		if (!pTh || !pTh->m_bInRun || pTh->m_bInClose || pTh->m_eventPtr.ProcessToStop()) continue;

		xbLock lckThread(pTh);
		lck.Unlock();

		if (pTh->CloseIfNotBusy(&lckThread,"Threads center closing free threads")) 
		{
			lckThread.Unlock();
			int cnt = 0;
			while(cnt < xbTHREADNB_TRYCNT)
			{
				cnt++;
				lck.Lock(&m_sect);
				if (cThread > m_cThread) return true;
				lck.Unlock();
				xbSleep(xbTHREADNB_TRYSLEEP);
			}
		}

		lck.Lock(&m_sect);
		if (cThread > m_cThread) return true;
	}

	return cThread > m_cThread;
}

void xbThreadCenter::addThread(Thread *pTh)
{
	xbAssert(pTh->m_CenterId == 0);
	xbSectLock lck(&m_sect);
	Thread	**ptr = m_arrTh.Begin();
	Thread	**pend = m_arrTh.End();
	int		no = 1;
	while(ptr < pend)
	{
		if (!*ptr)
		{
			*ptr = pTh;
			pTh->m_CenterId = no;
			m_cThread++;
			if (m_cThread > m_cThreadPeak)
				m_cThreadPeak = m_cThread;
			return;
		}
		ptr++;
		no++;
	}
	
	pTh->m_CenterId = m_arrTh.Add(pTh)+1;
	m_cThread++;
	if (m_cThread > m_cThreadPeak)
		m_cThreadPeak = m_cThread;
}

void xbThreadCenter::onStart()
{
}

bool xbThreadCenter::onStop(bool bForce)
{
	if (!askCloseAll("Stopping thread center")) 
		return false;
	xbSectLock lck(&m_sect);
	if (!bForce && m_cThread > 0)
		return false;
	m_arrTh.Clear();
	m_cThread = 0;
	lck.Unlock();
	appUnlock();
	return true;
}

bool xbThreadCenter::askCloseAll(LPCSTR pReason,bool bUseEnd)
{
	xbThDbgOutput("xbThreadCenter::askCloseAll("+xbString(bUseEnd?"use end":"dtor")+")");
	xbSectLock lck(&m_sect);
	for (int no = 0; no < m_arrTh.Length; no++)
	{
		Thread *pTh = m_arrTh.GetAt(no);
		if (!pTh || pTh->m_bInClose /*|| pTh->m_eventPtr.ProcessToStop()*/) continue;

		xbLock lckThread(pTh);

		lck.Unlock();
		if (bUseEnd && m_cUseLock)
			return true;

//		if (bUseEnd)
//			pTh->AskClose(pReason);
//		else
			pTh->Close(&lckThread,pReason,xbOp_Optional);

		lck.Lock(&m_sect);
	}
	return m_cThread <= 0;
}

/*
void xbThreadCenter::CloseAll()
{
	askCloseAll();
	xbSectLock lck(&m_sect);
	int cnt = 0;
	while(m_cThread > 0)
	{
		if (++cnt > xbTHREADCLOSEALL_TRYCNT)
			xbThrow xbEsys("xbThreadCenter::CloseAll() : timeout");
		lck.Unlock();
		Sleep(xbTHREADCLOSEALL_TRYSLEEP);
		lck.Lock(&m_sect);
	}
}
*/

//////////////////////////////////////////////////////////////////////////////////////////
// xbThreadCenter::Thread
xbThreadCenter::Thread::Thread(xbThreadCenter *pCenter,const xbString &label,sIxbThreadBaseCb *pCb)
	:m_eventPtr(pCb->GetThreadEvent())
{
	m_pThCenter = pCenter;
	m_label = (LPCSTR)label;
//	m_label.OwnData();
	m_CenterId = 0;
	m_bInRun = false;
	m_pCb = pCb;
	m_bInThreadLock = false;
	m_cThreadLock = 0;
	m_bInClose = false;
	m_cCloseTried = 0;
	m_pMsgCb = NULL;
}

void		xbThreadCenter::Thread::waitLockEnd(xbSectLock *pLck)
{
	xbThDbgOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Lock(label="+m_label+") : thread in close");
	int cnt = 0;
	for(;;)
	{
		pLck->Lock(&m_pThCenter->m_sect);
		m_bInClose = true;
		if (m_cThreadLock<=0) break;
		m_bInThreadLock = true;
		if (++cnt >= xbTHREADLOCK_TRYCNT) 
		{
			m_bInThreadLock = false;
			xbThrow xbEsys("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Run() : thread lock(task termination) timeout");
		}

		pLck->Unlock();
		xbSleep(xbTHREADLOCK_TRYSLEEP);
	}
	m_bInThreadLock = false;
}

void		xbThreadCenter::Thread::closeMsgQ(xbSectLock *pLck)
{	
	//Empty the message queue.
	sIxbThreadMsgCb	*pMsgCb = m_pMsgCb;
	if (!pMsgCb) 
	{
		pLck->Unlock();
		return;
	}

	m_pMsgCb = NULL;
	pLck->Unlock();

	MSG msg;
	while(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
	{
		if (msg.message == WM_QUIT) continue;
		eProtectTrace(pMsgCb->onThreadMsg(this,&msg,true));
	}
}
#ifdef _RELDBG
#define MS_VC_EXCEPTION 0x406D1388

typedef struct tagTHREADNAME_INFO
{
   ULONG_PTR	dwType; // Must be 0x1000.
   LPCSTR		szName; // Pointer to name (in user addr space).
   ULONG_PTR	dwThreadID; // Thread ID (-1=caller thread).
   ULONG_PTR	dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;

void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
{
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = szThreadName;
   info.dwThreadID = dwThreadID;
   info.dwFlags = 0;

   __try
   {
      RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
   }
   __except(EXCEPTION_CONTINUE_EXECUTION)
   {
   }
}
#endif

void		xbThreadCenter::Thread::thTrace(const xbString &txt)
{
	if (!xbThToTrace())
		return;
	eProtectTrace(xbRelOutput("[TH:"+xbString(m_nThreadID,xbStringNum_hexa)+"/"+m_label+"]: "+txt););
}

int		xbThreadCenter::Thread::Run()
{
	int ret = 0;
	eProtectBegin();
	xbThreadContext	threadContext(m_label,this);

#ifdef _RELDBG
	SetThreadName(-1,"["+xbString(::xbThreadId(),xbStringNum_hexa)+"] "+m_label);
#endif

	thTrace("thread started");
	try
	{
		m_pCb->onStart(this);
		xbSectLock lck(&m_pThCenter->m_sect);
		m_bInRun = true;

		if (!shouldStop())
		{
			lck.Unlock();

			sIxbThreadCb	*pProcCb = NULL;
			sIxbThreadMsgCb	*pMsgCb = NULL;
			if (xbCastIn(m_pCb,&pProcCb))
			{
				ret = pProcCb->onThreadProc(this);
				xbRdbgOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Run() : thread stopping (ret="+xbString(ret,xbStringNum_hexa)+")");
			}
			else
			{
				if (!xbCastIn(m_pCb,&pMsgCb))
					xbThrow xbEsys(E_NOINTERFACE,
						"xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Run() : bad thread callback");
				m_pMsgCb = pMsgCb;

				for(;;)
				{
					::xbWait(xbWAIT_TIMEOUTDFLT,m_eventPtr);
					if (shouldStop())
						break;
				}
			}
		}

		waitLockEnd(&lck);
		closeMsgQ(&lck);
//		m_bInRun = false;
		lck.Unlock();

		thTrace("thread stopped (ret="+xbString(ret,xbStringNum_hexa)+")");

		eProtectTrace(ret = m_pCb->onStopForce(this,ret));

//		eProtectTrace(lck.Lock(&m_pThCenter->m_sect););
		m_bInRun = false;
//		lck.Unlock();
	}
	catch(...)
	{
		xbSectLock lck;
		eProtectTrace(waitLockEnd(&lck));
		eProtectTrace(closeMsgQ(&lck));
//		m_bInRun = false;
		lck.Unlock();

		thTrace("abnormal termination : thread stopped");
		ret = -1;
		eProtectTrace(ret = m_pCb->onStopForce(this,-1));

//		eProtectTrace(lck.Lock(&m_pThCenter->m_sect););
		m_bInRun = false;
//		lck.Unlock();

		eProtectTrace(StopCoInit(););
		throw;
	}
	StopCoInit();

	eProtectEndTrace();
	eProtectTrace(delete this);

	return ret;

}

//sIxbThreadContextObject methods
void	xbThreadCenter::Thread::ThreadContextRelease()
{
	xbThrowEsys("Unexpected call to xbThreadCenter::Thread::ThreadContextRelease()");
}

sIxbThreadContextObject *xbThreadCenter::Thread::ThreadContextOld()
{
	return NULL;
}

xbThreadEventObject *xbThreadCenter::Thread::ThreadEvent()
{
	return m_eventPtr;
}

bool xbThreadCenter::Thread::onThreadShouldStop( ulong evMask ) const
{
	return (evMask & (xbTHREADEVENT_KILL|xbTHREADEVENT_STOP|xbTHREADEVENT_STOPTASK))?true:false;
}

bool xbThreadCenter::Thread::onThreadMsg(MSG *pMsg,bool *pbQuit)
{
	if (!m_pMsgCb)
		return false;
	if (m_pMsgCb->onThreadMsg(this,pMsg,false))
		return true;
	if (pbQuit)
		*pbQuit = true;
	m_eventPtr.StopProcess();
	return false;
}

// sIxbLock methods
void	xbThreadCenter::Thread::Lock() const
{
	xbSectLock lck(&m_pThCenter->m_sect);
	if (m_bInClose) 
		xbThrow xbEsys("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Lock(label="+m_label+") : thread in close");
	m_cThreadLock++;
}

void	xbThreadCenter::Thread::Unlock() const
{
	xbSectLock lck(&m_pThCenter->m_sect);
	m_cThreadLock--;
	xbAssert(m_cThreadLock >= 0);
}

// sIxbThread methods
bool	xbThreadCenter::Thread::CloseIfNotBusy(xbLock *pThreadLckToUnlock,LPCSTR pReason)
{
	xbSectLock lck(&m_pThCenter->m_sect);
	if (!m_bInRun) return false;
	lck.Unlock();
	return m_pCb->closeIfNotBusy(this,pThreadLckToUnlock,pReason);
}


DWORD	xbThreadCenter::Thread::Suspend(xbOpMode opMode)
{
	DWORD ret = SuspendThread();
	if (opMode == xbOp_Required && ret == DWORD(-1))
		xbThrow xbEsys(xbHR_LAST,"xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Resume() : SuspendThread error");
	return ret;
}

DWORD	xbThreadCenter::Thread::Resume(xbOpMode opMode)
{
	DWORD ret = ResumeThread();
	if (opMode == xbOp_Required && ret == DWORD(-1))
	{
		if (::GetLastError() == 0) return 0; //Under win95 : if thread is not suspended : the return is -1
		xbThrow xbEsys(xbHR_LAST,"xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Resume() : ResumeThread error");
	}
	return ret;
}

void	xbThreadCenter::Thread::StartCoInit()
{
	m_coInit.Start(this);
}

void	xbThreadCenter::Thread::StopCoInit()
{
	m_coInit.Stop();
}

bool	xbThreadCenter::Thread::IsCoInitStarted() const
{
	return m_coInit.IsStarted();
}

bool	xbThreadCenter::Thread::Close(xbLock *pThreadLckToUnlock,LPCSTR pReason,xbOpMode opMode)
{
	xbSectLock lck(&m_pThCenter->m_sect);
	if (/*m_eventPtr.ProcessToStop() || */ m_bInClose) return true;
	if (opMode != xbOp_Required)
	{
		if (++m_cCloseTried > 10000)
		{
			opMode = xbOp_Required;
			m_eventPtr.KillProcess();
		}
	}
	if (m_eventPtr.ProcessToStop())
	{
		xbThDbgOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Close(label="+m_label+") : Retry Close(try cnt="+
			xbString(m_cCloseTried,xbStringNum_hexa)+")");
	}
	m_eventPtr.StopProcess();
	for(;;)
	{
		DWORD ret = ResumeThread();
		if (ret == DWORD(-1))
		{
			DWORD err = ::GetLastError();
			if (err != 0)
			{
				xbString serr(::xbStrFromHR(err));
				xbRelOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Close(label="+m_label+") : ResumeThread error ("+
					xbString(err,xbStringNum_hexa)+":"+serr+")");
			}
			break;
		}
		if (ret <= 1) 
			break;
	}

	lck.Unlock();

	bool bThreadValid = true;
	DWORD exitcode = 0;
//	eProtectBegin();
	for(;;)
	{
		sIxbThreadCb	*pProcCb = NULL;
		if (!::GetExitCodeThread(m_hThread,&exitcode))
		{
			xbRelOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Close(label="+m_label+") : thread doesn't exist");
			exitcode = 0;
			bThreadValid = false;
			break;
		}
		if (exitcode != STILL_ACTIVE)
		{
			xbRelOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Close(label="+m_label+") : thread closed by system !!");
			bThreadValid = false;
			break;
		}

		try
		{
			m_eventPtr.StopProcess();
			m_pCb->onCloseAsked(this,pReason);
/*			if (xbCastIn(m_pCb,&pProcCb))
			{
				pProcCb->askClose(this,pReason);
			}
			else
			{
				xbPostThreadMessage(m_hThread,m_nThreadID,WM_QUIT,0,0);
			}*/
		}
		catch(xbEseh *pE)
		{
			xbRelOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Close(label="+m_label+") : callback destroyed...");
			::xbTraceException(pE,true,__FILE__,__LINE__);
			break;
/*			m_bInClose = true;
			Suspend(false);
			m_pThCenter->m_cThread--;
			xbRdbgAssert(m_pThCenter->m_cThread>=0);
			return true;*/
		}
		int cnt = 0;
		for(;;)
		{
			lck.Lock(&m_pThCenter->m_sect);
			if (m_bInClose) 
				return true;
			if (++cnt > xbTHREADCLOSE_TRYCNT) 
				xbThrow xbElock("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::Close(label="+m_label+") : timeout");
			if (opMode != xbOp_Required 
				&& cnt > xbTHREADCLOSE_NREQWAITCNT)
				return false;
			lck.Unlock();
			xbSleep(xbTHREADCLOSE_TRYSLEEP);
		}
		xbRdbgAssert(false);
		break;
	}
//	eProtectEndTrace();

//	if (bThreadValid) 
//		SuspendThread();
	m_bInClose = true;
	lck.Unlock();
//	eProtectTrace(m_pCb->onStop(this,exitcode?exitcode:-1));
//	pThreadLckToUnlock->Unlock();
	eProtectTrace(xbThreadLowBase::Destroy());
	return true;
}

void	xbThreadCenter::Thread::AskClose(LPCSTR pReason)
{
	xbSectLock lck(&m_pThCenter->m_sect);
	if (m_eventPtr.ProcessToStop() || m_bInClose) return;
	m_eventPtr.StopProcess();
	for(;;)
	{
		DWORD ret = ResumeThread();
		if (ret == DWORD(-1))
		{
			DWORD err = ::GetLastError();
			if (err != 0)
			{
				xbString serr(::xbStrFromHR(err));
				xbRelOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::AskClose(label="+m_label+") : ResumeThread error ("+
					xbString(err,xbStringNum_hexa)+":"+serr+")");
			}
			break;
		}
		if (ret <= 1) break;
	}
	lck.Unlock();
	m_pCb->onCloseAsked(this,pReason);
/*
	sIxbThreadCb	*pProcCb = NULL;
	if (xbCastIn(m_pCb,&pProcCb))
	{
		pProcCb->askClose(this,pReason);
	}
	else
	{
		xbPostThreadMessage(m_hThread,m_nThreadID,WM_QUIT,0,0);
	}*/
}

HANDLE	xbThreadCenter::Thread::GetHandle()
{
	return m_hThread;
}

DWORD	xbThreadCenter::Thread::GetId()
{
	return m_nThreadID;
}

xbString	xbThreadCenter::Thread::GetLabel()
{
	xbSectLock lck(&m_pThCenter->m_sect);
	return m_label;
}

xbThreadPriority	xbThreadCenter::Thread::GetPriority()
{
	return xbThreadPriority(GetThreadPriority());
}

bool	xbThreadCenter::Thread::SetPriority(xbThreadPriority priority,xbOpMode opm)
{
	if (SetThreadPriority(int(priority)))
		return true;
	if (opm == xbOp_Required)
		xbThrow xbEsys(xbHR_LAST,"xbThreadCenter::Thread("+GetLabel()+") priority changes failed");

	return false;
}

void	xbThreadCenter::Thread::PostMessage( UINT message , WPARAM wParam, LPARAM lParam )
{
	xbPostThreadMessage(m_nThreadID,message,wParam,lParam);
}

void	xbThreadCenter::Thread::removeFromCenter()
{
	eProtectBegin();
	if (m_pCb && m_bInRun)
	{
		xbThDbgOutput("xbThreadCenter::Thread("+xbString(m_nThreadID,xbStringNum_hexa)+")::removeFromCenter() : thread closed by system !!");
		m_pCb->onStopForce(this,-5);
	}
	eProtectEndTrace();
	m_pCb = NULL;
	m_bInRun = false;
	xbSectLock lck(&m_pThCenter->m_sect);
	if (!m_CenterId) return;
	m_pThCenter->m_arrTh.SetAt(m_CenterId-1,NULL);
	m_pThCenter->m_cThread--;
	m_pThCenter->m_cThreadClosed++;
	m_pThCenter->m_cThreadDone++;
	xbRdbgAssert(m_pThCenter->m_cThread>=0);
	m_CenterId = 0;
	if (m_pThCenter->m_cThread <= 0
		&& m_pThCenter->m_bAppLock)	
	{
		lck.Unlock();
		m_pThCenter->appUnlock();
	}

}

int xbThreadCenter::GetThreadCount() const
{
//	xbSectLock lck(&m_sect);
	return m_cThread;
}

int xbThreadCenter::GetThreadPeakCount() const
{
//	xbSectLock lck(&m_sect);
	return m_cThreadPeak;
}

uint xbThreadCenter::GetThreadDoneCount() const
{
//	xbSectLock lck(&m_sect);
	return m_cThreadDone;
}

uint xbThreadCenter::GetThreadClosedCount() const
{
//	xbSectLock lck(&m_sect);
	return m_cThreadClosed;
}

void xbThreadCenter::ResetPerfStats()
{
	xbSectLock lck(&m_sect);
	m_cThreadPeak = 0;
	m_cThreadDone = 0;
}

void xbThreadCenter::SetCoInit(sIxbThreadCoInit *pCb)
{
	xbSectLock lck(&m_sect);
	m_pCoInitCb = pCb;
}

sIxbThreadCoInit *xbThreadCenter::GetCoInit()
{
	xbSectLock lck(&m_sect);
	return m_pCoInitCb;
}

void xbThreadCenter::StartCoInit(sIxbThread *pTh,void **ppCtx)
{
	CheckReady();
	_set_se_translator(xbSEHtranslate);
	set_unexpected(xbUnexpected);
	set_terminate(xbTerminated);
	{
		xbSectLock lck(&m_sect);
		if (m_pCoInitCb && m_pCoInitCb->onThreadCoInitStart(&lck,pTh,ppCtx))
			return;
	}
	#ifndef _PDA
	HRtoE(::CoInitialize(NULL));
	#endif
}

void xbThreadCenter::StopCoInit(sIxbThread *pTh,void *pCtx)
{
	_set_se_translator(NULL);
	set_unexpected(NULL);
	set_terminate(NULL);

	{
		xbSectLock lck(&m_sect);
		if (m_pCoInitCb && m_pCoInitCb->onThreadCoInitStop(&lck,pTh,pCtx))
			return;
	}
	#ifndef _PDA
	::CoUninitialize();
	#endif
}

void xbThreadCoInit::Start(sIxbThread *pTh)
{
	if (m_bInit) 
		return;
	g_xbThreadCenter.StartCoInit(pTh,&m_pCtx);
	m_pTh = pTh;
	m_bInit = true;
}
void xbThreadCoInit::Stop()
{
	if (!m_bInit) 
		return;
	m_bInit = false;
	g_xbThreadCenter.StopCoInit(m_pTh,m_pCtx);
	m_pCtx = NULL;
	m_pTh = NULL;
}

void xbThreadCenter::threadUseLock()
{
	CheckReady();
	::xbInterlockIncrement32(&m_cUseLock);
	appUnlock();
}
void xbThreadCenter::threadUseUnlock()
{
	CheckReady();
	if (xbInterlockDecrement32((LONG*)&m_cUseLock) != 0)
		return;
	appLock();
	eProtectBegin();
	int cTry = 0;
	while(!askCloseAll("Threads use end",true))
	{
		if (++cTry>=xbTHREADCLOSEALL_TRYCNT)
			xbThrow xbEsys("xbThreadCenter::CloseAll() : timeout");
		::xbSleep(xbTHREADCLOSEALL_TRYSLEEP);
	}
	eProtectEndTrace();
}

void xbThreadCenter::appLock()
{
/*	xbSectLock lck(&m_sect);
	if (m_bAppLock || m_cThread <= 0)
		return;
	m_cThread++;
	try
	{
		m_bAppLock = true;
		lck.Unlock();
		AfxOleLockApp();
		xbThDbgOutput("xbThreadCenter::appLock()");
	}
	catch(...)
	{
		eProtectTrace(lck.Lock(&m_sect););
		m_bAppLock = false;
		m_cThread--;
		throw;
	}

	eProtectTrace(lck.Lock(&m_sect););
	m_cThread--;*/
}

void xbThreadCenter::appUnlock()
{
/*	xbSectLock lck(&m_sect);
	if (!m_bAppLock)
		return;
	m_bAppLock = false;
	lck.Unlock();
	xbThDbgOutput("xbThreadCenter::appUnlock()");
	AfxOleUnlockApp();*/
}
