
/*
  File: include/basic/thread.h
  Description: Sonique 2 thread objects
  Part of the Mantis project
  (c) 2002 Lycos Inc.

  This file is distributed for the sole purpose of allowing you to write
  software to interface with Sonique 2 and other Mantis programs. It is
  not to be modified or used for any other purpose without written
  authorization.

  If you redistribute this file, it must be in the original package
  complete with all other files provided, including the usage agreement.

  Do not remove this notice.
*/

#ifndef _THREAD_H_
#define _THREAD_H_

#include "threadport.h"
#include "ResourceCache.h"	

class AutoCriticalSection
{
	CriticalSectionObject* pCS;
public:
	AutoCriticalSection( CriticalSectionObject* pcs )
	{
		pCS = pcs;
		pCS->Enter();
	}

	~AutoCriticalSection()
	{
		if( pCS )
			pCS->Leave();
	}

	void Leave()
	{
		pCS->Leave();
		pCS = NULL;
	}
};


template <class TYPE, bool bManage >
class ThreadLocalStorageObject
{
	class TreadLocalStorageItem
	{
	public:
		uint32					uiThreadID;
		TYPE*					pData;
		TreadLocalStorageItem*  pNext;	

		TreadLocalStorageItem() : pData(NULL) {}
		
	};

	mutable TreadLocalStorageItem*		pHead;
	mutable CriticalSectionObject		cs;
public:	

	static TYPE* NewObj()
	{
		return new TYPE;
	}
	
	static void  DeleteObj( TYPE* pType )
	{
		delete pType;
	}
	
	ThreadLocalStorageObject()
	{
		pHead = NULL;
	};

	~ThreadLocalStorageObject()
	{
		Flush();
	}
	void Flush()
	{
		cs.Enter();
		while( pHead )
		{
			TreadLocalStorageItem* temp = pHead;
			pHead = pHead->pNext;

			if( bManage )
				DeleteObj( temp->pData );

			delete temp;
		}

		cs.Leave();
	};


	void Set( TYPE* pObj )
	{
		uint32 uiCurID				= ThreadObject::GetCurrentThreadID();
		TreadLocalStorageItem* pCur = pHead;

		while( pCur && pCur->uiThreadID != uiCurID )
			pCur = pCur->pNext;
		
		if( !pCur ) // didnt' find it
		{
			pCur				= new TreadLocalStorageItem;
			pCur->uiThreadID	= uiCurID;

			cs.Enter();
			pCur->pNext			= pHead;
			pHead				= pCur;
			cs.Leave();
		}

		if( bManage && pCur->pData )
			DeleteObj( pCur->pData );

		pCur->pData = pObj;
	}

	TYPE* Get() const
	{
		uint32 uiCurID				= ThreadObject::GetCurrentThreadID();
		TreadLocalStorageItem* pCur = pHead;

		while( pCur && pCur->uiThreadID != uiCurID )
			pCur = pCur->pNext;
		

		if( pCur )
			return pCur->pData;
		else
		{
			if( bManage )
			{
				pCur				= new TreadLocalStorageItem;
				pCur->uiThreadID	= uiCurID;

				cs.Enter();
				pCur->pNext			= pHead;
				pHead				= pCur;
				cs.Leave();

				pCur->pData = NewObj();

				return pCur->pData;
			}
			else
				return NULL;
		}
	}

	operator TYPE*() const
	{
		return Get();
	}

	const TYPE* operator=( TYPE* pObj )
	{
		Set( pObj );
		return pObj;
	}

	uint32 GetAll( Array<TYPE*>& toHere ) const
	{
		toHere.RemoveAll();

		cs.Enter();
		TreadLocalStorageItem* pCur = pHead;
		while( pCur )
		{
			toHere.AddTail( pCur->pData );
			pCur = pCur->pNext;
		}

		cs.Leave();
		return toHere.GetCount();
	}

};


typedef SignalObject CachedSignalObject;
void ResourceCache<CachedSignalObject, 4, false>::FirstInit(CachedSignalObject* pData)
{
	new((void*)pData) SignalObject;
}
void ResourceCache<CachedSignalObject, 4, false>::LastDeInit(CachedSignalObject* pData)
{
	pData->SignalObject::~SignalObject();
}
void ResourceCache<CachedSignalObject, 4, false>::Init(CachedSignalObject* pData)
{
	pData->WaitForSignal(0);
}
class SignalCache : public ResourceCache<CachedSignalObject, 4, false>
{
public:
	SignalCache() : INHERIT_RESOURCE_CACHE(CachedSignalObject,4,false) { };
};






template <class ThreadMessage>
class MessageThread
{
public:
	MessageThread(bool bStartThread = true)
	{
		m_bMessageThreadIsRunning = false;
		if( bStartThread )
			VERIFY(StartMessageThread());
	}
	~MessageThread()
	{
		if( MessageThreadIsRunning() )
			StopMessageThread();
	}


	bool StartMessageThread()
	{
		if( m_bMessageThreadIsRunning )
			return false;
		m_bTerminate = false;
		if( m_MessageThread.Start(&MessageThreadProc, priority_normal, this, "MessageThread") )
			m_bMessageThreadIsRunning = true;
		return true;
	}
	void StopMessageThread()
	{
		m_bTerminate = true;
		m_MessageSignal.SetSignal();
		m_MessageThread.WaitForDeath();
		m_bMessageThreadIsRunning = false;
	}
	bool MessageThreadIsRunning()
	{
		return m_bMessageThreadIsRunning;
	}
	void PostMessage( ThreadMessage* pMessage )
	{
		m_MessageCS.Enter();
		m_Messages.AddTail(pMessage);
		m_MessageCS.Leave();
		m_MessageSignal.SetSignal();
	}

	virtual void ProcessMessage( ThreadMessage* pMessage ) = 0;// { ASSERT(!"implement me"); }

protected:
	ThreadObject m_MessageThread;
	SignalObject m_MessageSignal;
	CriticalSectionObject m_MessageCS;
	Array<ThreadMessage*> m_Messages;
	bool m_bMessageThreadIsRunning, m_bTerminate;

	static int32 __stdcall MessageThreadProc(void* pThis)
	{
		return ((MessageThread*)pThis)->MessageThreadFunc();
	}
	int32 MessageThreadFunc()
	{
		while( m_MessageSignal.WaitForSignal() == signal_set )
		{
			ThreadMessage* pMessage;
			do
			{
				m_MessageCS.Enter();
				if( m_Messages.GetCount() )
				{
					pMessage = m_Messages[0];
					m_Messages.RemoveAt(0);
				} else {
					pMessage = NULL;
				}
				m_MessageCS.Leave();

				if( pMessage )
				{
					ProcessMessage(pMessage);
					delete pMessage;
				}
			} while( pMessage );

			if( m_bTerminate )
				break;
		}
		return 0;
	}
};


#endif//_THREAD_H_
