/****************************************************************************************************

		MCSMPSafeCriticalSections.h
		Copyright: (c) 2002 Eovia Corp. All rights reserved.

		Author:	Arnaud
		Date:	7/28/2007

****************************************************************************************************/

#ifndef __MCSMPSafeCriticalSections__
#define __MCSMPSafeCriticalSections__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#ifdef qIsInExtension
#include "IShThreadUtilities.h"
#include "COMSafeUtilities.h"
#else
#include "MCThread.h"
#endif


///This class is intended to protect cooperative threads (and preemptive ones)
///on the pc cooperative threads are not affected by critical sections, 
///on the mac they deadlock. Both cases are bad.
///For now this class is to protect a single function. 
///When we'll need the same thread to enter twice in the section, we'll need to store the cooperative thread id.
class TMCSafeSection : public TMCObject
{
public:
	TMCSafeSection	()	{ fMainThreadInside= false;		fCS = NewCS(); }
	~TMCSafeSection ()	{ MCAssert(!fMainThreadInside); DeleteCS(fCS); }

	inline TMCCriticalSection*	GetCS() { return fCS; }

	inline boolean		IsMainThreadInside	() const			{ return fMainThreadInside; }
	inline void			SetMainThreadInside	(boolean value)		{ fMainThreadInside= value; }

	inline void			Enter				()					{ EnterCS(fCS); }
	inline void			Leave				()					{ LeaveCS(fCS); }

protected:
	TMCCriticalSection*	fCS;
	boolean				fMainThreadInside;
};


class CWhileInSafeCS : public TMCObject
{
public:
	CWhileInSafeCS	(TMCSafeSection& theCS) : fCS(theCS), fIsInMainSMPProcess(IsInMainSMPProcess())
	{		
		if (MCVerify(&fCS && fCS.GetCS()))
		{
			if (fIsInMainSMPProcess)
			{
				while(fCS.IsMainThreadInside())
				{
					//we need to wait
#ifdef qIsInExtension
					gShellThreadUtilities->YieldProcesses(0);
#else
					TMCThreadUtilities::YieldThreadsNow();
#endif
				}

				fCS.SetMainThreadInside(true);
				fCS.Enter();
			}
			else
			{
				fCS.Enter();
			}
		}
	}

	~CWhileInSafeCS()
	{ 
		if (MCVerify(fCS.GetCS()))
		{
			if (fIsInMainSMPProcess)
			{
				fCS.SetMainThreadInside(false);
				fCS.Leave();
			}
			else
			{
				fCS.Leave();
			}
		}
	}

protected:
	TMCSafeSection& fCS;
	boolean			fIsInMainSMPProcess;
};




#endif

