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

#include "xbLock.h"
#include "xbException.h"
#include "xbThread.h"

#pragma warning(push)
#pragma warning(disable: 4311) //'cast de type' : troncation de pointeur de 'xbSectShareBase *' à 'xbNptr'
/*
Idée pas mal sur une optim sur les locks: eviter le InterlockedCompareExchange 
dans le cas ou le thread essayant d'obtenir le lock est le dernier thread a avoir obtenu ce meme lock.
(Sauf que ca demande de l'assembleur)
http://www.jstage.jst.go.jp/article/ipsjdc/2/0/759/_pdf

xbCritSectEx source, tres bonnes perfs
http://www.codeproject.com/KB/threads/CritSectEx.aspx?display=Print
*/

/*
Doc :
http://softwarecommunity.intel.com/articles/eng/2807.htm
http://www.intel.com/design/intarch/papers/cache6.pdf
http://en.wikipedia.org/wiki/MESI_protocol
*/

xbHeapDnewImplOnly(xbThreadContext,g_xbEngineCenter.m_heap);
xbHeapDnewImplOnly(xbCtxBase,g_xbEngineCenter.m_heap);

void xbLockTracePb(const xbString &msg)
{
	xbRelOutput("[LOCK][PB] "+msg);
#ifdef _RELDBG
	if (xbIsDebuggerPresent() && g_xbExceptionEngine.IsToStop("xbElock"))
		::xbDebugBreak();
#endif
}

void xbLockTraceWarn(const xbString &msg)
{
	xbRelOutput("[LOCK][WARN] "+msg);
}

xbString xbLockThInfo(xbThreadContext *pThCtx)
{
	xbString ret("[TH:"+xbString(pThCtx->m_id,xbStringNum_hexa)+"/"+pThCtx->m_pName+"]");
	eProtectBegin();
	sIxbProgressInfo *pPrg;
	if (pThCtx->m_pExecCtx && xbCastIn<sIxbProgressInfo>(pThCtx->m_pExecCtx,&pPrg))
	{
		ret += " in ";
		int no = 1;
		xbString stageName,stageLabel;
		while(pPrg->stageInfoGet(no,&stageName,&stageLabel,NULL,NULL,NULL,NULL,NULL))
		{
			if (no > 1)
				ret += "<<";
			ret += stageName+"-"+stageLabel;
			no++;
		}
	}
	eProtectEndRdbgTrace();
	return ret;
}

xbString xbSectShareBase::lockInfo(xbThreadContext *pThCtx)
{
	eProtectBegin();
	xbString ret;
	if (!pThCtx)
		pThCtx = xbThreadContext::Current();
	ret = "waiting thid="+xbLockThInfo(pThCtx);

	int cnt = 0;
	{
		xbSectLockSimple lck(&m_thListShare);
		xbThreadContext *pTh = (xbThreadContext*)m_pThWaitingLast;
		while(pTh)
		{
			if (pTh != pThCtx)
			{
				cnt++;
				if (cnt >= 10)
				{
					ret += "\n[...]";
					break;
				}
				ret += "\n- waiting too: "+xbLockThInfo(pTh);
			}
			pTh = (xbThreadContext*)pTh->m_pThWaitPrev;
		}
	}
	return ret;
	eProtectEndRdbgTrace();
	return "???"; 
}

/*
Thread Information Block -> retrieve adresse with mov eax:[fs:0];

typedef struct _NT_TIB {
	struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
	PVOID StackBase;
	PVOID StackLimit;
	PVOID SubSystemTib;
	_ANONYMOUS_UNION union {
		PVOID FiberData;
		DWORD Version;
	} DUMMYUNIONNAME;
	PVOID ArbitraryUserPointer;
	struct _NT_TIB *Self;
*/

#ifdef	XDCBASE_X86
#	define _INTEL_X86
#endif

#ifndef _INTEL_X86
	__declspec( thread ) xbThreadContext *tls_thCtx = NULL;
#	pragma message("XDCBASE: threadContext = __declspec( thread )" )
#else
#	pragma message("XDCBASE: threadContext = _asm fs:[0x14]" )
#endif

#ifdef xbSectShareBase_DEADLOCKPREVENT
ulong	xbSectShareBase::s_idcurrent = 0;
#	ifdef xbSectShareSTD_DEADLOCKPREVENT
#		pragma message("XDCBASE: lock STD: deadlock prevent = ON" )
#	else
#		pragma message("XDCBASE: lock STD: deadlock prevent = OFF" )
#	endif
#endif

#ifdef xbSectShareSTD_DEADLOCKDETECT
#	pragma message("XDCBASE: lock STD: deadlock detect = ON" )
#else
#	pragma message("XDCBASE: lock STD: deadlock detect = OFF" )
#endif

// #################################################################################################################
// xbThreadContext
// #################################################################################################################
xbThreadContext::xbThreadContext( LPCSTR pName, sIxbThread *pThread )
{
	m_pThis = this;
	m_pThread = pThread;
	m_pExceptionCtx = NULL;
	m_pExecCtx = pThread;
	m_pExecCtxFeeder = NULL;
	m_pCurEseh = NULL;
	m_pCurEstack = NULL;
	m_pThWaitPrev = NULL;
	m_pThWaitNext = NULL;
	memset(&m_rsrv2,0,sizeof(m_rsrv2));
	
	// Id of the tread
	m_id				= xbThreadId( );
	if (m_id == 0 /*|| m_id == xbSectShareSpinRW_READTHID*/)
		xbThrowElock("xbThreadContext: invalid thread id("+xbString(m_id,xbStringNum_hexa)+") !");

	// Name of the thread
	if (pName && *pName)
		m_pName = xbFormat("Thread #%i - %s", m_id, pName ).StrAlloc();
	else
		m_pName = xbFormat("Thread #%i",m_id).StrAlloc();

	m_cShareStack = 0;
//	m_rwLockWaited = xbLockNoop;
	if(!(m_shareEvent	= ::CreateEvent( NULL, FALSE, FALSE, NULL )))
		xbThrowElock(xbHR_LAST,"xbThreadContext: event creation failure !");

	// Do not allow reentrance for the thread head !
	if ( Current()!=NULL )
		xbThrowElock("xbThreadContext: Twice xbThreadContext initiate !");

	// Distribute ref along with this thread !!!
#ifndef _INTEL_X86
	tls_thCtx = this;
#else
	xbThreadContext* pSelf = this;
	__asm	mov eax,pSelf;
	__asm	mov	fs:[0x14],eax;
#endif


	// Finishing normally
	AssertThreadCommitment( );
}

xbThreadContext::~xbThreadContext( )
{
	if ( Current()==this )
	{
#ifndef _INTEL_X86
		tls_thCtx = NULL;
#else
		__asm	mov eax,NULL;
		__asm	mov	fs:[0x14],eax;
#endif
	}

	xbRdbgAssert(m_pCurEseh == NULL);
	xbRdbgAssert(m_pCurEstack == NULL);
	xbRdbgAssert(m_pThis == this);
	::CloseHandle( m_shareEvent );
//	m_count --;
//	xbAssert( m_count>=0 );
	m_pThis = NULL;

	if (m_pName)
	{
		xbString::StrFree(m_pName);
		m_pName = NULL;
	}
}

void xbThreadContext::AssertThreadCommitment( ) const
{
	if ( Current( )!=this )
		xbThrowElock( "xbThreadContext: xbThreadContext invalid with current thread !");
}

#ifdef _INTEL_X86
__declspec( naked ) 
#endif
	xbThreadContext* xbThreadContext::Current( )
{
#ifndef _INTEL_X86
	return tls_thCtx;
#else
	__asm	mov eax,fs:[0x14];
	__asm	ret;
#endif
}

void xbSectShareBase::waitLink(xbThreadContext *pThCtx)
{
	xbSectLockSimple lck(&m_thListShare);
	xbRdbgAssert(!pThCtx->m_pThWaitNext && !pThCtx->m_pThWaitPrev);

	pThCtx->m_pThWaitNext = m_pThWaitingFirst;
	m_pThWaitingFirst = pThCtx;
	if (pThCtx->m_pThWaitNext) 
		pThCtx->m_pThWaitNext->m_pThWaitPrev = pThCtx;
	else
	{
		xbRdbgAssert(!m_pThWaitingLast);
		m_pThWaitingLast = pThCtx;
	}
	pThCtx->m_pThWaitPrev = NULL;
}

void xbSectShareBase::waitUnlink(xbThreadContext *pThCtx, bool bSignalNext)
{
	xbSectLockSimple lck(&m_thListShare);

	if (pThCtx->m_pThWaitNext) 
		pThCtx->m_pThWaitNext->m_pThWaitPrev = pThCtx->m_pThWaitPrev;
	else
	{
		xbRdbgAssert(m_pThWaitingLast == pThCtx);
		m_pThWaitingLast = pThCtx->m_pThWaitPrev;
	}
	if (pThCtx->m_pThWaitPrev)
	{
		pThCtx->m_pThWaitPrev->m_pThWaitNext = pThCtx->m_pThWaitNext;
	}
	else
	{
		xbRdbgAssert(m_pThWaitingFirst == pThCtx);
		m_pThWaitingFirst = pThCtx->m_pThWaitNext;
	}
	pThCtx->m_pThWaitPrev = NULL;
	pThCtx->m_pThWaitNext = NULL;

	if (bSignalNext)
	{
		xbThreadContext *pLast = (xbThreadContext*)m_pThWaitingLast;
		if (pLast)
			::SetEvent(pLast->GetShareEvent());
	}
}

void xbSectShareBase::waitSignal()
{
	xbSectLockSimple lck(&m_thListShare);
	xbThreadContext *pLast = (xbThreadContext*)m_pThWaitingLast;
	if (pLast)
		::SetEvent(pLast->GetShareEvent());
}

bool xbSectShareBase::waitedByThread(ulong thid)
{
	xbSectLockSimple lck(&m_thListShare);
	xbThreadContext *pThCtx = (xbThreadContext*)m_pThWaitingLast;
	while(pThCtx)
	{
		if (pThCtx->m_id == thid)
			return true;
		pThCtx = (xbThreadContext*)pThCtx->m_pThWaitPrev;
	}
	return false;
}
#ifdef xbSectShareBase_DEADLOCKPREVENT
struct xbSectShareDeadInfoItem
{
	xbSectShareBase *m_pShare;
	xbuint			m_shareId;
	ulong			m_cDeadLock;
};
#define xbSectShareDeadInfo_SIZEMAX	32
class xbSectShareDeadInfo
{
public:
	xbSectShareDeadInfo()
	{
		m_cItem=0;
	}
//	xbHeapDnewImpl(xbSectShareDeadInfo,g_xbEngineCenter.m_heap);
	xbSectShareDeadInfoItem *shareFind(xbSectShareBase *pShare)
	{
		xbSectShareDeadInfoItem *pItem = m_item;
		xbSectShareDeadInfoItem *pEnd = pItem+m_cItem;
		while(pItem < pEnd)
		{
			if (pItem->m_pShare == pShare && pItem->m_shareId == pShare->m_id)
				return pItem;
			pItem++;
		}
		return NULL;
	}
	xbSectShareDeadInfoItem *shareAdd(xbSectShareBase *pShare)
	{
		xbSectShareDeadInfoItem *pItem = m_item;
		xbSectShareDeadInfoItem *pEnd = pItem+m_cItem;
		while(pItem < pEnd)
		{
			if (pItem->m_pShare == pShare)
			{
				if (pItem->m_shareId == pShare->m_id)
					return pItem;
				pItem->m_shareId = pShare->m_id;
				pItem->m_cDeadLock = 0;
				return NULL;
			}
			pItem++;
		}
		if (m_cItem < xbSectShareDeadInfo_SIZEMAX)
			m_cItem++;
		else
		{
			::memmove(&m_item,&m_item[1],(xbSectShareDeadInfo_SIZEMAX-1)*sizeof(xbSectShareDeadInfoItem));
			pItem--;
		}

		pItem->m_pShare = pShare;
		pItem->m_shareId = pShare->m_id;
		pItem->m_cDeadLock = 0;
		return NULL;
	}
protected:
	ulong						m_cItem;
	xbSectShareDeadInfoItem		m_item[xbSectShareDeadInfo_SIZEMAX];
private:
	xbDclNoCopyForObject(xbSectShareDeadInfo);
};

xbSectShareBase::~xbSectShareBase()
{	
	if (m_pDeadInfo)
		eProtect(delete m_pDeadInfo;);
}

void xbSectShareBase::lockOrderStatusCheck(xbSectShareBase *pShareBefore)
{
	if (pShareBefore == this)
		return;
	if (!lockOrderStatusOk(pShareBefore))
		xbLockTracePb("Deadlock case detected: order invalid: share1("+xbString(pShareBefore->m_id,xbStringNum_hexa)+") locked before share2("+xbString(m_id,xbStringNum_hexa)+")");
	if (pShareBefore->m_deadStatus & xbSectShareBaseDEAD_FINAL)
		xbLockTracePb("Deadlock case detected: order invalid: final share1("+xbString(pShareBefore->m_id,xbStringNum_hexa)+") locked before share2("+xbString(m_id,xbStringNum_hexa)+")");
	if ((m_deadStatus & xbSectShareBaseDEAD_PRIORALL)
		&& !(pShareBefore->m_deadStatus & xbSectShareBaseDEAD_BEFOREPRIOR))
		xbLockTraceWarn("Deadlock case detected: order invalid: prior all share2("+xbString(m_id,xbStringNum_hexa)+") locked before share1("+xbString(pShareBefore->m_id,xbStringNum_hexa)+")");
}
#endif

#ifdef xbSectShareSTD_DEADLOCKPREVENT
void xbSectShareBase::dlpLockedAfterCheck(xbSectShareBase *pShareBefore)
{
	xbSectShareDeadInfoItem *pItem;
	if (m_pDeadInfo && (pItem = m_pDeadInfo->shareFind(pShareBefore))!=NULL)
	{
		pItem->m_cDeadLock++;
		xbLockTracePb("Possible deadlock case detected: share1("+xbString(pShareBefore->m_id,xbStringNum_hexa)+") is also locked after share2("+xbString(m_id,xbStringNum_hexa)+")");
	}
}

void xbSectShareBase::dlpLockedBefore(xbSectShareBase *pShareAfter)
{
	return; //??
	xbRdbgAssert(pShareAfter != this);
	if (pShareAfter == this)
		return;
	if (!m_pDeadInfo)
		m_pDeadInfo = new xbSectShareDeadInfo();
	xbSectShareDeadInfoItem *pItem;
	if ((pItem = m_pDeadInfo->shareAdd(pShareAfter))!=NULL)
	{
		if (pItem->m_cDeadLock > 0)
			xbLockTracePb("Possible deadlock case detected: original case: share1("+xbString(m_id,xbStringNum_hexa)+") locked before share2("+xbString(pShareAfter->m_id,xbStringNum_hexa)+")");

		return;
	}
}
#endif


#ifdef xbSectShareRW_DEADLOCKPREVENT
void xbSectShareBase::dlpLockedAfterCheckRW(xbSectShareBase *pShareBefore)
{
	xbSectLockSimple lck(&m_thListShare);
	xbSectShareDeadInfoItem *pItem;
	if (m_pDeadInfo && (pItem = m_pDeadInfo->shareFind(pShareBefore))!=NULL)
	{
		pItem->m_cDeadLock++;
		xbLockTracePb("RW: Possible deadlock case detected: share1("+xbString(pShareBefore->m_id,true)+") is also locked after share2("+xbString(m_id,true)+")");
	}
}

void xbSectShareBase::dlpLockedBeforeRW(xbSectShareBase *pShareAfter)
{
	if (pShareAfter == this)
		return;

	xbSectLockSimple lck(&m_thListShare);
	if (!m_pDeadInfo)
		m_pDeadInfo = new xbSectShareDeadInfo();
	xbSectShareDeadInfoItem *pItem;
	if ((pItem = m_pDeadInfo->shareAdd(pShareAfter))!=NULL)
	{
		if (pItem->m_cDeadLock > 0)
			xbLockTracePb("RW: Possible deadlock case detected: original case: share1("+xbString(m_id,true)+") locked before share2("+xbString(pShareAfter->m_id,true)+")");

		return;
	}
}
#endif

void xbThreadContext::shareLeaveFind(xbSectShareBase *pShare)
{
	shareStackSt *pbegin = m_shareStack;
	shareStackSt *ptr = pbegin+m_cShareStack;
	int no = m_cShareStack;
	while(ptr-- > pbegin)
	{
		no--;
		if (ptr->m_pShare == pShare)
		{
			memmove(&m_shareStack[no],&m_shareStack[no+1],
				(m_cShareStack-no)*sizeof(shareStackSt));
//			if (no < m_cShareStack-1)
//				xbLockTraceWarn("xbThreadContext::shareLeaveFind: nested locks unlocked not in reverse order !");
			return;
		}
	}

	xbLockTracePb("xbThreadContext::shareLeaveFind: unlocked lock not found !");
}

#ifdef xbSectShareSTD_DEADLOCKDETECT
void xbThreadContext::shareEnter(xbSectShareBase *pShare)
{
	int no = m_cShareStack++;
	if (no >= xbStackShare_SIZEMAX)
	{
		xbLockTracePb("xbThreadContext::shareEnter: too many locks in stack ! ("+xbString(m_cShareStack)+")");
		return;
	}
	m_shareStack[no].set(pShare);
#ifdef xbSectShareSTD_DEADLOCKPREVENT
	if (no >= 40)
	{
		xbLockTraceWarn("xbThreadContext::shareEnter: more than 40 locks in stack("+xbString(no+1)+") !");
	}
#endif
#ifdef xbSectShareSTD_DEADLOCKPREVENT
	bool bDeadPrevDisabled = (pShare->m_deadStatus & xbSectShareBaseDEAD_DISABLED)?true:false;
	for(int no = 0; no < m_cShareStack-1;no++)
	{
		xbSectShareBase *pShareBefore = m_shareStack[no].m_pShare;
		pShare->lockOrderStatusCheck(pShareBefore);
		if (bDeadPrevDisabled || (pShareBefore->m_deadStatus & xbSectShareBaseDEAD_DISABLED))
			continue;
		pShare->dlpLockedAfterCheck(pShareBefore);
		pShareBefore->dlpLockedBefore(pShare);
	}
#endif
}
void xbThreadContext::shareLeave(xbSectShareBase *pShare)
{
	if (m_cShareStack<=0)
	{
		xbLockTracePb("xbThreadContext::shareLeave: unexpected call !");
		return;
	}
	m_cShareStack--;
	if (m_cShareStack < xbStackShare_SIZEMAX && m_shareStack[m_cShareStack].m_pShare != pShare)
		shareLeaveFind(pShare);
}
void xbThreadContext::shareDeadLockCheck(ulong thidWaited,xbThreadWaitLock *pWaitLock)
{
//	xbRelOutput("[TH] Thread deadlock check");
	int lg = m_cShareStack > xbStackShare_SIZEMAX?xbStackShare_SIZEMAX:m_cShareStack;
	for(int no = 0; no < lg;no++)
	{
		if (m_shareStack[no].m_pShare->waitedByThread(thidWaited))
		{
			pWaitLock->Unlock();
			xbString stkInfo("\nShare stack: length="+xbString(lg)+", share in dead lock="+xbString(no)+"\n");
			for(int no2 = 0; no2 < lg;no2++)
			{
				stkInfo+="-";
				if (no2 == no)
					stkInfo += "["+xbString((xbNptr)m_shareStack[no2].m_pShare,xbStringNum_hexa)+"]";
				else
					stkInfo += xbString((xbNptr)m_shareStack[no2].m_pShare,xbStringNum_hexa);
			}

			xbThrowElock("Lock operation failed, deadlock case detected !",
				"xbSectShareSpin("+xbString((xbNptr)pWaitLock->m_pShare,xbStringNum_hexa)+")::Lock()"
						+"\nWaited thid=[TH:"+xbString(thidWaited,xbStringNum_hexa)+"], "+m_shareStack[no].m_pShare->lockInfo(this)
						+stkInfo);
		}
	}
}
#endif

void xbSectShareSpin::longLock(ulong thid)
{
	ulong				tiStart=0;
	xbThreadContext		*pThCtx = xbThreadContext::Current();

	{
		ulong				ret,diff;
		xbThreadWaitLock	waitLck(pThCtx,this);
		ulong				cSpin=0;

		for(;;)
		{
//			if (cSpin > 0)
			{
				if (xbInterlockCompareExchange32((long*)&m_thid,thid,0) == 0)
					break;
			}
			if (cSpin < xbSectShareSpin_SPINCOUNT)
			{
				cSpin++;
				xbSectShare_YIELD(cSpin);
				continue;
			}
			ret = ::GetTickCount();
			if (!tiStart) 
			{
				tiStart = ret;
				diff = 0;
//				pThCtx = xbThreadContext::Current();
			}
			else
			{
				diff = xbDiffTime(ret,tiStart);
				if (diff >= xbSectShareSpin_TIMEOUT)
				{
					waitLck.Abort();
					xbThrowElock(xbHR_TIMEOUT,"Time out waiting object","xbSectShareSpin("+xbString((xbNptr)this,xbStringNum_hexa)+")::Lock(), spent time="+xbString(diff)
						+"\nWaited thid=[TH:"+xbString(m_thid,xbStringNum_hexa)+"], "+lockInfo(pThCtx));
				}
/*#ifdef xbSectShareSTD_DEADLOCKDETECT
				if (diff >= xbStackShare_CHECKTIMEMAX)
					waitLck.DeadLockCheck(m_thid);
#endif*/
			}
			if (!pThCtx)
			{
				::Sleep(50);
				continue;
			}

			ret = waitLck.Wait(xbSectShareSpin_TIMEOUT - diff);
			if (ret == WAIT_TIMEOUT)
			{
	#ifdef xbSectShareSTD_DEADLOCKDETECT
				waitLck.DeadLockCheck(m_thid);
	#endif
				continue;
			}
			if (ret == WAIT_FAILED)
			{
				waitLck.Unlock();
				xbThrowElock(xbHR_LAST,"Waiting for object operation failure",
				"xbSectShareSpin("+xbString((xbNptr)this,xbStringNum_hexa)+")::Lock()");
			}
		}
	}

#ifdef xbSectShare_DEBUG
	if (m_thid != thid || m_cLock)
	{
		xbThrowElock("Waiting for object operation failure, bad thread id or lock count",
		"xbSectShareSpin("+xbString((xbNptr)this,xbStringNum_hexa)+")::Lock(): cLock="+xbString(m_cLock)+", thid="+xbString(thid,xbStringNum_hexa)+", m_thid="+xbString(m_thid,xbStringNum_hexa));
	}
#endif
	m_cLock++;

#ifdef xbSectShareSTD_DEADLOCKDETECT
	if (pThCtx)
		pThCtx->shareEnter(this);
#endif

#ifdef xbSectShareSTD_STAT
	if (tiStart)
		onThreadWaitTime(tiStart);
#endif
}


//DWORD xbCritSectEx::s_dwProcessors = 0;

void xbCritSectEx::AllocateKernelSemaphore()
{
	if (!m_hSemaphore)
	{
		HANDLE hSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
		xbCritSectExTestSys(!hSemaphore);
		if (InterlockedCompareExchangePointer(&m_hSemaphore, hSemaphore, NULL))
			xbMustBe(CloseHandle(hSemaphore)); // we're late
	}
}

bool xbCritSectEx::PerfLock(DWORD dwThreadID, DWORD dwTimeout)
{
	// Attempt spin-lock
	for (DWORD dwSpin = 0; dwSpin < m_dwSpinMax; dwSpin++)
	{
		if (PerfLockImmediate(dwThreadID))
			return true;

		YieldProcessor();
	}

	// Ensure we have the kernel event created
	AllocateKernelSemaphore();

	bool bVal = PerfLockKernel(dwThreadID, dwTimeout);
	WaiterMinus();

	return bVal;
}

bool xbCritSectEx::PerfLockKernel(DWORD dwThreadID, DWORD dwTimeout)
{
	bool bWaiter = false;

	for (DWORD dwTicks = GetTickCount(); ; )
	{
		if (!bWaiter)
			WaiterPlus();

		if (PerfLockImmediate(dwThreadID))
			return true;

		DWORD dwWait;
		if (INFINITE == dwTimeout)
			dwWait = INFINITE;
		else
		{
			dwWait = GetTickCount() - dwTicks; // how much time elapsed
			if (dwTimeout <= dwWait)
				return false;
			dwWait = dwTimeout - dwWait;
		}

		xbAssert(m_hSemaphore);
		switch (WaitForSingleObject(m_hSemaphore, dwWait))
		{
		case WAIT_OBJECT_0:
			bWaiter = false;
			break;
		case WAIT_TIMEOUT:
			bWaiter = true;
			break;
		default:
			xbCritSectExTestSys(TRUE);
		}
	}

	// unreachable
}

void xbCritSectEx::SetSpinMax(DWORD dwSpinMax)
{
	if (g_xbEngineCenter.GetSysCoreCount() > 1)
		m_dwSpinMax = dwSpinMax;
}

void xbCtxBase::checkStop() const
{
	if (!shouldStop())
		return;
	xbThrow xbEuser(E_ABORT,"Operation cancelled");
}
void xbCtxBase::checkStop(const xbString &oplabel) const
{
	if (!shouldStop())
		return;
	xbThrow xbEuser(E_ABORT,oplabel+" cancelled");
}

#pragma warning(pop)
