/*<<<***************************************************************************
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: xbEngine
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<04/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbEngine.h"
#include "xbArray.h"
#include "xbString.h"
#include "xbException.h"
#include "xbSync.h"
/*#include "xbVersion.h"
*/

xbEimpl(xbEsys,xbEbase);

extern bool xbDoEvents (bool *pbQuit,int cMsgMax = 5);

struct xbEngineStartStopStack
{
	bool				m_bStart;
	xbArray<xbEngine*>	m_arrEng;
	xbEngineStartStopStack(bool bStart)
	{
		m_bStart = bStart;
	}
	void	SetLevel(xbEngine *ptr,int level);
	void	DumpStack(int level);
};


#pragma warning(disable: 4074)
#pragma init_seg(compiler) 

xbLowExceptionHeap g_xbLowExceptionHeap("Low exception heap");

xbEngineCenter g_xbEngineCenter;

bool xbIsNTos()
{
	return g_xbEngineCenter.IsNTos();
}

bool xbIs95os()
{
	return g_xbEngineCenter.Is95os();
}

DWORD xbGetOSver()
{
	return g_xbEngineCenter.GetOSver();
}

DWORD xbGetOSminorVer()
{
	return g_xbEngineCenter.GetOSminorVer();
}

xbString xbGetOSlabel()
{
	return g_xbEngineCenter.GetOSlabel();
}

xbEngineCenter::xbEngineCenter()
	:m_heap("Epro main memory heap")
{
	m_sectStartStop.DeadLockOrderPriorAll();

	xbSectLock lck(&m_sectStartStop);
	m_pThis = this;
	m_pFirst = NULL;
	m_cUse = 0;
	m_bOsInfoOk = false;
	m_bSysInfoOk = false;
	m_bAllStarted = false;
}

xbEngineCenter::~xbEngineCenter()
{
	xbSectLock lck(&m_sectStartStop);
	m_pThis = NULL;
	if (m_pFirst != NULL)
		xbRelOutputLow("[PB] xbEngineCenter::~xbEngineCenter() : staying engine");
	if (m_cUse != 0)
		xbRelOutputLow("[PB] xbEngineCenter::~xbEngineCenter() : use count="+xbString(m_cUse));
}

const SYSTEM_INFO &xbEngineCenter::GetSysInfo()
{
	CheckReady();
	if (m_bSysInfoOk) return m_sysInfo;
	xbSectLock lck(&m_sectData);
	if (m_bSysInfoOk) return m_sysInfo;
	::GetSystemInfo(&m_sysInfo);
	m_bSysInfoOk = true;
	return m_sysInfo;
}

ulong xbEngineCenter::GetSysCoreCount()
{
	ulong ret = GetSysInfo().dwNumberOfProcessors;
	if (!ret)
		return 1;
	return ret;
}

ulong xbEngineCenter::GetSysPageGranularity()
{
	ulong ret = GetSysInfo().dwAllocationGranularity;
	if (!ret)
		return 4096;
	return ret;
}

const OSVERSIONINFOEX &xbEngineCenter::GetOsInfo()
{
	CheckReady();
	if (m_bOsInfoOk) return m_osInfo;
	xbSectLock lck(&m_sectData);
	if (m_bOsInfoOk) return m_osInfo;
	m_osInfo.dwOSVersionInfoSize = sizeof(m_osInfo);
	if (!::GetVersionEx((OSVERSIONINFO*)&m_osInfo))
		xbThrow xbEsys(xbHR_LAST,"xbEngineCenter::GetOsInfo() : failure");
	m_bOsInfoOk = true;
	return m_osInfo;
}

xbString	xbEngineCenter::GetOSlabel()
{
	GetOsInfo();
	xbString ret;

	if (m_osInfo.dwPlatformId == VER_PLATFORM_WIN32s) ret = "Win32s ";
	else if (m_osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) ret = "Windows ";
	else if (m_osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) 
	{
		switch(m_osInfo.dwMajorVersion)
		{
		case 4:
			ret = "Windows NT ";
			break;
		case 5:
			switch(m_osInfo.dwMinorVersion)
			{
			case 0:
				ret = "Windows 2000 ";
				break;
			case 1:
				ret = "Windows XP ";
				break;
			case 2:
				ret = "Windows Server 2003 ";
				break;
			default:
				ret = "Windows NT5? ";
				break;
			}
			break;
		case 6:
			switch(m_osInfo.dwMinorVersion)
			{
			case 0:
				if (m_osInfo.wProductType == VER_NT_WORKSTATION)
					ret = "Windows Vista ";
				else
					ret = "Windows Server 2008 ";
				break;
			case 1:
				if (m_osInfo.wProductType == VER_NT_WORKSTATION)
					ret = "Windows 7 ";
				else
					ret = "Windows Server 2008 R2 ";
				break;
			case 2:
				if (m_osInfo.wProductType == VER_NT_WORKSTATION)
					ret = "Windows 8 ";
				else
					ret = "Windows Server 2008 R2 ";
				break;
			default:
				if (m_osInfo.wProductType == VER_NT_WORKSTATION)
					ret = "Windows 8? ";
				else
					ret = "Windows Server 2008? ";
				break;
			}
			break;
		default:
			ret = "Windows NT? ";
			break;
		}
	}
	else 
		ret = "??? ";

	ret += xbString(m_osInfo.dwMajorVersion)+"."+xbString(m_osInfo.dwMinorVersion); //xbVersion(m_osInfo.dwMajorVersion,m_osInfo.dwMinorVersion).ToStr('.');
	if (m_osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
	{
		ret += " Build ";
		ret += xbFormat("%u.%u",(m_osInfo.dwBuildNumber & 0xFF000000) >> 24,
			(m_osInfo.dwBuildNumber & 0x00FF0000) >> 16);
		ret += ' ';
		ret += xbString(m_osInfo.dwBuildNumber & 0xFFFF);
	}
	else if (m_osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) 
		ret += " Build "+xbString(m_osInfo.dwBuildNumber);
	ret += " "+xbString(m_osInfo.szCSDVersion);

	return ret.RetTrim();
}

bool		xbEngineCenter::IsNTos()
{
	return GetOsInfo().dwPlatformId == VER_PLATFORM_WIN32_NT;
}

bool		xbEngineCenter::Is95os()
{
	return GetOsInfo().dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
}

ulong		xbEngineCenter::GetOSver()
{
	return GetOsInfo().dwMajorVersion;
}

ulong		xbEngineCenter::GetOSminorVer()
{
	return GetOsInfo().dwMinorVersion;
}

void xbInvalid_parameter_handler(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t)
{
	xbThrow xbEsys(xbHR_INVALIDARG,"xdcBase: Function called with invalid arguments (crt)");
}

void xbPurecall()
{
	xbThrow xbEseh(xbHR_ABORT,"xdcBase: Object in destruction (purecall)");
}

void xbEngineCenter::Start()
{
	CheckReady();

	xbDbgOutput("xbEngineCenter::Start() called");
	xbSectLock lck(&m_sectStartStop);
	for(;;)
	{
		if (m_cUse <= 0)
			xbThrow xbEsys("xbEngineCenter()::Start() : engine center is not 'use locked'");

		if (m_bAllStarted)
			return;
		m_bAllStarted = true;

		_set_invalid_parameter_handler(xbInvalid_parameter_handler);
		_set_purecall_handler(xbPurecall);

		xbEngineStartStopStack	stack(true);

		xbEngine	*ptr = m_pFirst;

		while(ptr)
		{
			xbEngine *pCur = ptr;
			ptr = ptr->m_pNext;
			if (pCur->isNowStarted() || pCur->m_mode != xbEngineStart_Auto) continue;
			pCur->start(&stack,0);
		}
		if (m_bAllStarted) break;
		xbDbgOutput("xbEngineCenter::Start() retry, not all started");
	}
}

void xbEngineCenter::Stop()
{
	CheckReady();
	
	xbSectLock lck(&m_sectStartStop);
	m_bAllStarted = false;

	xbEngineStartStopStack	stack(false);

	xbEngine	*ptr = m_pFirst,*pWaited = NULL;
	int			WaitCnt = 0;
	int			WaitCntForce = 0;

	bool bForce = false;
	while(ptr)
	{
		xbEngine *pCur = ptr;
		ptr = ptr->m_pNext;
		if (pCur->m_bStopped) 
			continue;
		if (!bForce && WaitCntForce > 3)
			bForce = true;
		if (pCur->stop(&stack,0,bForce)) 
			continue;
		if (pWaited != pCur) 
		{
			WaitCnt = 1;
//			WaitCntForce = 0;
			pWaited = pCur;
		}
		else if (++WaitCnt > xbENGINE_TRYCNT) 
		{
			if (isInUse() || bForce)
				xbThrowEsysFmt(xbHR_TIMEOUT,"xbEngineCenter::Stop() : timeout on %s",pCur->Label);
			if ((++WaitCntForce) > 4)
				xbThrowEsysFmt(xbHR_TIMEOUT,"xbEngineCenter::Stop() : timeout on %s (stop forced)",pCur->Label);
			bForce = true;
			WaitCnt = 0;
		}
		lck.Unlock();
		if (!xbDoEvents(NULL)) 
			SleepEx(xbENGINE_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
		lck.Lock(&m_sectStartStop);
		ptr = m_pFirst;
	}
}

void xbEngineCenter::useLock(bool bStart,xbSectLock *pLck)
{
	CheckReady();
	xbSectLock lck(&m_sectStartStop);
	m_cUse++;
	if (bStart)
	{
		try
		{
			if (pLck)
				pLck->Unlock();

			Start();
		}
		catch(...)
		{
			eProtectBegin();
			lck.Unlock();
			useUnlock(pLck);
			eProtectEndTrace();
			throw;
		}
	}
}

void xbEngineCenter::useUnlock(xbSectLock *pLck)
{
	CheckReady();
	xbSectLock lck(&m_sectStartStop);
	m_cUse--;
	xbAssert(m_cUse>=0);
	if (m_cUse > 0) return;

	xbEngineStartStopStack	stack(false);

	xbEngine	*ptr = m_pFirst,*pWaited = NULL;
	int			WaitCnt = 0;
	int			WaitCntForce = 0;

	bool bForce = false;
	while(ptr && m_cUse <= 0)
	{
		xbEngine *pCur = ptr;
		ptr = ptr->m_pNext;
		if (pCur->m_bStopped) 
			continue;
		if (!bForce && WaitCntForce > 3)
			bForce = true;
		if (pCur->stop(&stack,0,bForce)) 
			continue;
		if (pWaited != pCur) 
		{
			WaitCnt = 1;
//			WaitCntForce = 0;
			pWaited = pCur;
		}
		else if ((++WaitCnt) > xbENGINE_TRYCNT) 
		{
			if (bForce)
				xbThrowEsysFmt(xbHR_TIMEOUT,"xbEngineCenter::useUnlock() : timeout on %s",pCur->Label);
			if ((++WaitCntForce) > 4)
				xbThrowEsysFmt(xbHR_TIMEOUT,"xbEngineCenter::useUnlock() : timeout on %s (stop forced)",pCur->Label);
			bForce = true;
			WaitCnt = 0;
		}
		lck.Unlock();
		if (pLck)
		{
			pLck->Unlock();
			pLck = NULL;
		}
		if (!xbDoEvents(NULL)) 
			SleepEx(xbENGINE_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
		lck.Lock(&m_sectStartStop);
		ptr = m_pFirst;
	}
}

xbEngine::xbEngine(xbEngineCenter *pCenter,xbEngineStartMode startMode,const char *pLabel,const xbEngines &engUsed)
{
	if (!pCenter) pCenter = &g_xbEngineCenter;
	pCenter->CheckReady();
	m_pCenter = pCenter;
	xbSectLock lck(&m_pCenter->m_sectStartStop);
	m_pNext = m_pPrev = NULL;
	m_cStartLock = 0;
	m_pThis = this;
	m_mode = startMode;
	m_pLabel = pLabel;
	m_engUsed = engUsed;
	m_bStarted = m_bLinked = false;
	m_bStopped = true;
	centerLink();
}

xbEngine::xbEngine(xbEngineStartMode startMode,const char *pLabel,const xbEngines &engUsed)
{
	g_xbEngineCenter.CheckReady();
	m_pCenter = &g_xbEngineCenter;
	xbSectLock lck(&g_xbEngineCenter.m_sectStartStop);
	m_pNext = m_pPrev = NULL;
	m_cStartLock = 0;
	m_pThis = this;
	m_mode = startMode;
	m_pLabel = pLabel;
	m_engUsed = engUsed;
	m_bStarted = m_bLinked = false;
	m_bStopped = true;
	centerLink();
}

xbEngine::~xbEngine()
{
	eProtectBegin();
	m_pCenter->CheckReady();
	xbSectLock lck(&m_pCenter->m_sectStartStop);
	xbAssert(!isNowStarted());
	centerUnlink();
	m_pThis = NULL;
	eProtectEndTrace();
}

void xbEngine::Destroy()
{
	eProtectBegin();
	m_pCenter->CheckReady();
	xbSectLock lck(&m_pCenter->m_sectStartStop);
	m_mode = xbEngineStart_Disabled;
	if (!m_bStopped) 
	{
		eProtectBegin();
		int cnt = 0;
		int	WaitCntForce = 0;
		xbEngineStartStopStack	stack(false);
		bool bForce = false;
		while(!stop(&stack,0,bForce))
		{
			if (++cnt > xbENGINE_TRYCNT) 
			{
				if (bForce)
					xbThrow xbEsys(xbHR_TIMEOUT,"xbEngine("+xbString(m_pLabel)+")::Destroy() : timeout, can't stop this engine !");
				if ((++WaitCntForce) > 4)
					xbThrow xbEsys(xbHR_TIMEOUT,"xbEngine("+xbString(m_pLabel)+")::Destroy() : timeout, can't stop this engine ! (stop forced)");
				bForce = true;
				cnt = 1;
			}
			else
			{
				if (!bForce && WaitCntForce > 3)
					bForce = true;
			}
			lck.Unlock();
			if (xbDoEvents(NULL)) 
			{
				lck.Lock(&m_pCenter->m_sectStartStop);
				if (stop(&stack,0,bForce)) break;
				lck.Unlock();

				if (!bForce && WaitCntForce > 3)
					bForce = true;
			}
			SleepEx(xbENGINE_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
			lck.Lock(&m_pCenter->m_sectStartStop);
		}
		eProtectEndTrace();
		m_bStarted = false;
		m_bStopped = true;
	}
	eProtectEndTrace();
}

void xbEngine::centerLink()
{
	if (m_bLinked) return;

	m_pNext = m_pCenter->m_pFirst;
	m_pCenter->m_pFirst = this;
	if (m_pNext) m_pNext->m_pPrev = this;
	m_pPrev = NULL;
	m_pCenter->m_bAllStarted = false;
	m_bLinked = true;
}

void xbEngine::centerUnlink()
{
	if (!m_bLinked) return;
	if (m_pNext) m_pNext->m_pPrev = m_pPrev;
	if (m_pPrev) m_pPrev->m_pNext = m_pNext;
	else m_pCenter->m_pFirst = m_pNext;

	m_bLinked = false;
}

void xbEngineStartStopStack::SetLevel(xbEngine *pEng,int level)
{
	xbAssert(level <= m_arrEng.Length);
	if (level>0)
	{
		xbEngine	**ptr = m_arrEng.Begin();
		int			no = 0;

		while(no < level)
		{
			if (*ptr == pEng)
				xbThrowEsysFmt("xbEngineStartStopStack::SetLevel(%s,level=%d) : recursive start/stop call in infinite loop !!",pEng->Label,level);
			no++;
			ptr++;
		}
	}
	if (level >= m_arrEng.Length) 
		m_arrEng.Length = level + 1;
	m_arrEng.SetAt(level, pEng);
}

void xbEngineStartStopStack::DumpStack(int level)
{
	if (level < 0)
	{
		xbRelOutput("  >> xbEngineStartStopStack::DumpStack(int level = "+xbString(level)+") : " + (m_bStart?"Start stack : ROOT, stack empty":"Stop stack : ROOT, stack empty"));
	}
	if (level >= m_arrEng.Length) level = m_arrEng.Length - 1;
	xbEngine	**ptr = m_arrEng.Begin();
	int			no = 0;

	xbRelOutput(xbStringPad(level*2+1)+"  >> xbEngineStartStopStack::DumpStack(int level = "
		+xbString(level)+") : " + (m_bStart?"Start stack : <<":"Stop stack : <<"));
	while(no <= level)
	{
		xbRelOutput(xbStringPad(level*2+1)+"      - xbEngineStartStopStack, level = "+xbString(no)+" : "+(*ptr)->Label);
		no++;
		ptr++;
	}
}

void xbEngine::start(xbEngineStartStopStack *pStack, int level)
{
	if (m_bStarted) return;
	xbDbgOutput(xbStringPad(level*2+1)+">>xbEngine("+Label+")::start() : enter start : level="+xbString(level));
	try
	{
		if (isLockedByMe())
			xbThrow xbEsys("xbEngine("+xbString(m_pLabel)+")::start() : engine must not be locked to start !");
		if (m_mode == xbEngineStart_Disabled)
			xbThrow xbEsys("xbEngine("+xbString(m_pLabel)+")::start() : this engine is disabled");
		if (level > 0)
		{
			CheckReady();
			if (m_mode == xbEngineStart_Manual)
				xbThrow xbEsys("xbEngine("+xbString(m_pLabel)+")::start() : this engine is not started, and is to start manually/explicitly");
		}

		pStack->SetLevel(this,level);
	}
	catch(...)
	{
		pStack->DumpStack(level-1);
		throw;
	}

#ifdef _DEBUG
	{
		xbEngine	**ptr = m_engUsed.Begin();
		xbEngine	**pend = m_engUsed.End();

		while(ptr < pend)
		{
			xbDbgOutput(xbStringPad(level*2+1)+"  - xbEngine("+Label+")::start() : engine to start : "+(*ptr)->Label);
			ptr++;
		}
	}
#endif

	xbEngine	**ptr = m_engUsed.Begin();
	xbEngine	**pend = m_engUsed.End();

	while(ptr < pend)
	{
		(*ptr)->start(pStack,level+1);
		ptr++;
	}

	try
	{
		xbDbgOutput(xbStringPad(level*2+1)+"  >xbEngine("+Label+")::start() : starting : level="+xbString(level));
		onStart();
		xbDbgOutput(xbStringPad(level*2+1)+"  <xbEngine("+Label+")::start() : started : level="+xbString(level));
	}
	catch(...)
	{
		pStack->DumpStack(level);
		throw;
	}
	m_bStarted = true;
	m_bStopped = false;
	xbDbgOutput(xbStringPad(level*2+1)+"<<xbEngine("+Label+")::start() : exit start : level="+xbString(level));
}

bool xbEngine::stop(xbEngineStartStopStack *pStack, int level, bool &bForce)
{
	if (m_bStopped) return true;

	if (!bForce && (m_cStartLock > 0 
		|| (m_bStarted && !onCanStop()))) return false;

	xbDbgOutput(xbStringPad(level*2+1)+">>xbEngine("+Label+")::stop() : enter stop : level="+xbString(level));

	try
	{
		pStack->SetLevel(this,level);
	}
	catch(...)
	{
		pStack->DumpStack(level-1);
		throw;
	}

	xbEngine	*ptr = m_pCenter->m_pFirst;

	while(ptr)
	{
		xbEngine *pCur = ptr;
		ptr = ptr->m_pNext;
		if (pCur == this || m_bStopped) continue;
		if (!pCur->IsUsed(this)) continue;
		if (!pCur->stop(pStack,level+1,bForce)) return false;
	}

	if (!bForce && m_cStartLock > 0) 
		return false;

	m_pCenter->m_bAllStarted = false;
	m_bStarted = false;
	if (!m_pCenter->isInUse() || m_mode == xbEngineStart_Disabled) 
	{
		eProtectBegin();
		try
		{
			xbDbgOutput(xbStringPad(level*2+1)+"  <xbEngine("+Label+")::stop(for ever) : stopping : level="+xbString(level));
			if (!onStop(bForce)) 
			{
				bForce = false;
				xbAssert(!bForce);
				return false;
			}
			xbDbgOutput(xbStringPad(level*2+1)+"  >xbEngine("+Label+")::stop(for ever) : stopped : level="+xbString(level));
		}
		catch(...)
		{
			pStack->DumpStack(level);
			throw;
		}
		eProtectEndTrace();
		bForce = false;
		m_bStopped = true;
		xbDbgOutput(xbStringPad(level*2+1)+"<<xbEngine("+Label+")::stop(for ever) : exit stop : level="+xbString(level));
		return true;
	}
	bForce = false;
	try
	{
		xbDbgOutput(xbStringPad(level*2+1)+"  >xbEngine("+Label+")::stop() : stopping : level="+xbString(level));
		if (!onStop(false)) return false;
		xbDbgOutput(xbStringPad(level*2+1)+"  <xbEngine("+Label+")::stop() : stopped : level="+xbString(level));
	}
	catch(...)
	{
		pStack->DumpStack(level);
		throw;
	}
	m_bStopped = true;
	xbDbgOutput(xbStringPad(level*2+1)+"<<xbEngine("+Label+")::stop() : exit stop : level="+xbString(level));
	return true;
}

bool xbEngine::IsUsed(xbEngine *pEng) const
{
	xbEngine*const *ptr = m_engUsed.Begin();
	xbEngine*const *pend = m_engUsed.End();

	while(ptr < pend)
	{
		if ((*ptr) == pEng) return true;
		ptr++;
	}
	return false;
}

void xbEngine::Start()
{
	if (m_bStarted) return;
	CheckReady();
	if (isLockedByMe())
		xbThrow xbEsys("xbEngine("+xbString(m_pLabel)+")::Start() : engine must not be locked to start !");
	xbSectLock lck(&m_pCenter->m_sectStartStop);
	
	if (!m_pCenter->isInUse()) 
		xbThrow xbEsys("xbEngine("+xbString(m_pLabel)+")::Start() : engine center is not in use !");
	
	xbEngineStartStopStack	stack(true);
	
	start(&stack,0);
}

void xbEngine::Stop()
{
	CheckReady();
	xbSectLock lck(&m_pCenter->m_sectStartStop);

	xbEngineStartStopStack	stack(false);

	bool bForce = false;
	int cnt = 0;
	while(!stop(&stack,0,bForce))
	{
		if (++cnt > xbENGINE_TRYCNT) 
		{
			if (bForce || m_pCenter->isInUse())
				xbThrow xbEsys(xbHR_TIMEOUT,"xbEngine("+xbString(m_pLabel)+")::Stop() : timeout, can't stop this engine");
			cnt = 1;
			bForce = true;
		}
		lck.Unlock();
		bool bMsg = false;
		if (xbDoEvents(NULL)) 
		{
			lck.Lock(&m_pCenter->m_sectStartStop);
			if (stop(&stack,0,bForce)) break;
			lck.Unlock();
		}
		SleepEx(xbENGINE_TRYSLEEP,xbWAITSLEEP_ALERTABLE);
		lck.Lock(&m_pCenter->m_sectStartStop);
	}
}

bool xbEngine::TryStop()
{
	CheckReady();
	xbSectLock lck(&m_pCenter->m_sectStartStop);

	xbEngineStartStopStack	stack(false);

	bool bForce = false;
	return stop(&stack,0,bForce);
}

xbEngine	*xbEngineCenter::GetEngine(int &idx,const CLSID *pInterfId,xbLock *pLckStart)
{
	xbSectLock lck(&m_sectStartStop);
	int i = 0;
	xbEngine	*ptr = m_pFirst;
	while(ptr)
	{
		xbEngine *pCur = ptr;
		ptr = ptr->m_pNext;
		i++;
		bool bOk = true;
		if (pLckStart)
		{
			if (!pCur->isNowStarted()) bOk = false;
		}
		if (bOk && pInterfId)
		{
			void *pInterf;
			if (!pCur->GetInterface(*pInterfId,&pInterf))
				bOk = false;
		}
		if (bOk && i >= idx)
		{
			idx = i+1;
			return pCur;
		}
	}
	return NULL;
}

/*

IAtlStringMgr* xbGetStringManager()
{
	return &g_xbEngineCenter;
}

CStringData* xbEngineCenter::Allocate( int nChars, int nCharSize ) throw()
{
	size_t nTotalSize;
	CStringData* pData;
	size_t nDataBytes;

	xbRdbgAssert(nCharSize > 0 && nChars >= 0);
	
//	if(nChars < 0)
//		return NULL;
	
	nDataBytes = (nChars+1)*nCharSize;
	nTotalSize = sizeof( CStringData )+nDataBytes;
	pData = (CStringData*)g_xbEngineCenter.m_heap.xbAlloc( nTotalSize );
	pData->pStringMgr = this;
	pData->nRefs = 1;
	pData->nAllocLength = nChars;
	pData->nDataLength = 0;

	return pData;
}

void xbEngineCenter::Free( CStringData* pData ) throw()
{
	g_xbEngineCenter.m_heap.Free(pData);
}

CStringData* xbEngineCenter::Reallocate( CStringData* pData, int nChars, int nCharSize ) throw()
{
	CStringData* pNewData;
	size_t nTotalSize;
	size_t nDataBytes;
	
	xbRdbgAssert(nCharSize > 0 && nChars >= 0);
	
//	if(nChars < 0)
//		return NULL;
	
	nDataBytes = (nChars+1)*nCharSize;
	nTotalSize = sizeof( CStringData )+nDataBytes;
	pNewData = (CStringData*)g_xbEngineCenter.m_heap.xbReAlloc( pData, nTotalSize );
	pNewData->nAllocLength = nChars;

	return pNewData;
}


CStringData* xbEngineCenter::GetNilString() throw()
{
	m_nil.AddRef();
	return &m_nil;
}

IAtlStringMgr* xbEngineCenter::Clone() throw()
{
	return this;
}
*/

#ifdef xbUSE_HEAP
void	*_xbAlloc(size_t cByte,xbAllocMode AllocMode
#ifdef _xbALLOCDEBUG
		,const char *pszFile,int nLine
#endif
		)
{
	return g_xbEngineCenter.m_heap.Alloc(cByte,AllocMode
#ifdef _xbALLOCDEBUG
		,pszFile,nLine
#endif
		);
}

void	*_xbReAlloc(void *ptr,size_t cByte,xbAllocMode AllocMode
#ifdef _xbALLOCDEBUG
		,const char *pszFile,int nLine
#endif
		)
{
	return g_xbEngineCenter.m_heap.ReAlloc(ptr,cByte,AllocMode
#ifdef _xbALLOCDEBUG
		,pszFile,nLine
#endif
		);
}

void	_xbFree(void *ptr)
{
	g_xbEngineCenter.m_heap.Free(ptr);
}
#endif


void xbThrowEsys(xbHR hr,LPCSTR pMsg)
{
	xbThrow xbEsys(hr,pMsg);
}

void xbThrowEsys(LPCSTR pMsg)
{
	xbThrow xbEsys(pMsg);
}

void xbThrowEsysFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEsys(xbHR_FAIL,pMsg,ap);
	va_end(ap);
}

void xbThrowEsysFmt(xbHR hr,LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEsys(hr,pMsg,ap);
	va_end(ap);
}

void xbThrowEsysLow(xbHR hr,LPCSTR pMsg)
{
	xbThrow xbEsysLow(hr,pMsg);
}

void xbThrowEsysLow(LPCSTR pMsg)
{
	xbThrow xbEsysLow(pMsg);
}

void xbThrowEsysLowFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEsysLow(xbEctor_final,xbHR_FAIL,pMsg,ap);
	va_end(ap);
}

void xbThrowEsysLowFmt(xbHR hr,LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEsysLow(xbEctor_final,hr,pMsg,ap);
	va_end(ap);
}

void xbThrowEmem(LPCSTR pMsg)
{
	xbThrow xbEmem(pMsg);
}

void xbThrowEmemFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEmem(xbEctor_final,pMsg,ap);
	va_end(ap);
}


void xbThrowElock(xbHR hr,LPCSTR pMsg,LPCSTR pCtx)
{
	xbThrow xbElock(hr,pMsg,pCtx);
}

void xbThrowElock(LPCSTR pMsg,LPCSTR pCtx)
{
	xbThrow xbElock(pMsg,pCtx);
}

void xbThrowElockFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbElock(xbEctor_final,xbHR_FAIL,pMsg,ap);
	va_end(ap);
}

void xbThrowEsehFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEseh(xbEctor_final,xbHR_FAIL,pMsg,ap);
	va_end(ap);
}

void xbThrowEsizeLimitFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEsizeLimit(xbEctor_final,pMsg,ap);
	va_end(ap);
}

void xbThrowEarg(LPCSTR pMsg,LPCSTR pCtx)
{
	xbThrow xbEarg(pMsg,pCtx);
}

void xbThrowEargFmt(LPCSTR pMsg,...)
{
	va_list ap;
	va_start(ap,pMsg);
	xbThrow xbEarg(xbEctor_final,pMsg,ap);
	va_end(ap);
}
