#ifndef _xbSYNC_
#define _xbSYNC_
/*<<<***************************************************************************
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: xbSync
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<05/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _xbHEAP_
#	include "xbHeap.h"
#endif
/*#ifndef _xbSECURITY_
#	include "xbSecurity.h"
#endif
*/
#define xbWAIT_TIMEOUTDFLT	60000
//#define xbWAITMSG_MODE		0
#define xbWAITMSG_MODE		MWMO_ALERTABLE
#define xbWAITMSG_FILTER	QS_ALLINPUT
//#define xbWAITMSG_FILTER	QS_ALLEVENTS
//#define xbWAITSLEEP_ALERTABLE	FALSE
#define xbWAITSLEEP_ALERTABLE	TRUE

class xbEXPORT xbSecurityAttr;
class xbEXPORT xbMutex:public sIxbLock
{
public:
	xbMutex(const xbSecurityAttr &attr,xbCtorMode ctormode=xbCtor_CallCreate,LPCSTR pName = NULL)
	{
		m_timeout = xbWAIT_TIMEOUTDFLT;
		m_hMutex = NULL;
		if (ctormode==xbCtor_CallCreate) Create(&attr,pName);
	}
	xbMutex(xbCtorMode ctormode=xbCtor_CallCreate,LPCSTR pName = NULL)
	{
		m_timeout = xbWAIT_TIMEOUTDFLT;
		m_hMutex = NULL;
		if (ctormode==xbCtor_CallCreate) Create(NULL,pName);
	}
	virtual ~xbMutex()
	{
		eProtect(Destroy());
	}
	void SetTimeOut(ulong ti)
	{
		m_timeout = ti;
	}
	ulong GetTimeOut() const
	{
		return m_timeout;
	}

	virtual void				Lock() const;
	virtual void				Unlock() const;

	//CAUTION: Functions not thread safe
	//Create return false if mutex already exists.
	bool Create(const xbSecurityAttr *pAttr = NULL, LPCSTR pName = NULL);
	void Destroy();

	bool IsValid() const {return m_hMutex != NULL;}
	bool IsEmpty() const {return m_hMutex == NULL;}
	HANDLE	GetHandle() const
	{
		if (m_hMutex == NULL) xbThrowEsys("xbMutex::GetHandle() : mutex not created");
		return m_hMutex;
	}
	void Attach(HANDLE hMutex) 
	{
		if (m_hMutex != NULL) xbThrowEsys("xbMutex::Attach() : mutex already created");
		m_hMutex = hMutex;
	}
	HANDLE Detach()
	{
		if (m_hMutex == NULL) xbThrowEsys("xbMutex::Detach() : mutex not created");
		HANDLE hMutex = m_hMutex;
		m_hMutex = NULL;
		return hMutex;
	}
protected:
	HANDLE	m_hMutex;
	ulong	m_timeout;
private:
	xbDclNoNewForObject(xbMutex);
	xbDclNoCopyForObject(xbMutex);
};

enum xbEventMode
{
	xbEventAuto = 0,
	xbEventAutoSet = 1,
	xbEventManual = 2,
	xbEventManualSet = 3
};
class xbEXPORT xbEvent
{
public:
	xbEvent(const xbSecurityAttr &attr,xbCtorMode ctormode=xbCtor_CallCreate,LPCSTR pName = NULL,xbEventMode mode = xbEventAuto)
	{
		m_hEvent = NULL;
		if (ctormode==xbCtor_CallCreate) Create(&attr,pName,mode);
	}
	xbEvent(xbCtorMode ctormode=xbCtor_CallCreate,LPCSTR pName = NULL,xbEventMode mode = xbEventAuto)
	{
		m_hEvent = NULL;
		if (ctormode==xbCtor_CallCreate) Create(NULL,pName,mode);
	}
	virtual ~xbEvent()
	{
		eProtect(Destroy());
	}

	//CAUTION: Functions not thread safe
	//Create return false if event already exists.
	bool Create(const xbSecurityAttr *pAttr = NULL, LPCSTR pName = NULL,xbEventMode mode = xbEventAuto);
	void Destroy();

	inline bool IsValid() const {return m_hEvent != NULL;}
	inline bool IsEmpty() const {return m_hEvent == NULL;}
	HANDLE	GetHandle() const
	{
		if (m_hEvent == NULL) xbThrowEsys("xbEvent::GetHandle() : event not created");
		return m_hEvent;
	}
	void Attach(HANDLE hEvent) 
	{
		if (m_hEvent != NULL) xbThrowEsys("xbEvent::Attach() : event already created");
		m_hEvent = hEvent;
	}
	HANDLE Detach()
	{
		if (m_hEvent == NULL) xbThrowEsys("xbEvent::Detach() : event not created");
		HANDLE hEvent = m_hEvent;
		m_hEvent = NULL;
		return hEvent;
	}
	void	Set()
	{
		if (m_hEvent == NULL) xbThrowEsys("xbEvent::Set() : event not created");
		if (!::SetEvent(m_hEvent))
			 xbThrowEsys(xbHR_LAST,"xbEvent::Set() failure");
	}
	void	Unset()
	{
		if (m_hEvent == NULL) xbThrowEsys("xbEvent::Unset() : event not created");
		if (!::ResetEvent(m_hEvent))
			 xbThrowEsys(xbHR_LAST,"xbEvent::Unset() failure");
	}

	/*Warning: 
	pour les events autoreset, un seul thread est réveillé, alors que pour les manual, ts les threads attendant sont réveillés
	le pulse de ms, remet le truc à l'état non signalé, cette implémentation remet le truc à signaler, pour donner une 
	chance aux threads n'étant pas sur le wait au moment du pulse de traiter l'event
	*/
	void	Pulse()
	{
		if (m_hEvent == NULL) xbThrowEsys("xbEvent::Pulse() : event not created");
		if (!::PulseEvent(m_hEvent))
			 xbThrowEsys(xbHR_LAST,"xbEvent::Pulse() failure");
		::SetEvent(m_hEvent);
	}
	void	PulseOnly()
	{
		if (m_hEvent == NULL) xbThrowEsys("xbEvent::PulseOnly() : event not created");
		if (!::PulseEvent(m_hEvent))
			 xbThrowEsys(xbHR_LAST,"xbEvent::PulseOnly() failure");
	}
	bool	wait(ulong timeOut,bool *pbCancel,bool bProcessMsg = true);
	bool	wait(xbEvent &ev2,ulong timeOut,bool *pbCancel,bool bProcessMsg = true);
protected:
	HANDLE	m_hEvent;
private:
	xbDclNoNewForObject(xbEvent);
	xbDclNoCopyForObject(xbEvent);
};

class xbEXPORT xbEventEx:public xbEvent
{
public:
	xbEventEx(const xbSecurityAttr &attr,xbCtorMode ctormode=xbCtor_CallCreate,LPCSTR pName = NULL,xbEventMode mode = xbEventAuto)
		:xbEvent(attr,ctormode,pName,mode)
	{
		m_timeout = xbWAIT_TIMEOUTDFLT;
	}
	xbEventEx(xbCtorMode ctormode=xbCtor_CallCreate,LPCSTR pName = NULL,xbEventMode mode = xbEventAuto)
		:xbEvent(ctormode,pName,mode)
	{
		m_timeout = xbWAIT_TIMEOUTDFLT;
	}
	void SetTimeOut(ulong ti)
	{
		m_timeout = ti;
	}
	ulong GetTimeOut() const
	{
		return m_timeout;
	}

	bool	Wait(bool *pbCancel,bool bProcessMsg = true);
protected:
	ulong	m_timeout;
private:
	xbDclNoNewForObject(xbEventEx);
	xbDclNoCopyForObject(xbEventEx);
};

//vWait return :
// 1 to n -> event 1 to N signaled
// -1 : time out
// -2 : io completion
int		xbEXPORT xbWait(long timeout,HANDLE *pEv,int cEv,bool bProcessMsg = true);
inline int xbWait(long timeout,HANDLE hEv,bool bProcessMsg = true)
{
	return xbWait(timeout,&hEv,1,bProcessMsg);
}
inline int xbWait(long timeout,const xbEvent &Ev,bool bProcessMsg = true)
{
	HANDLE hEv(Ev.GetHandle());
	return xbWait(timeout,&hEv,1,bProcessMsg);
}
inline int xbWait(long timeout,const xbEvent &Ev,const xbEvent &Ev2,bool bProcessMsg = true)
{
	HANDLE hEv[2] = {Ev.GetHandle(),Ev2.GetHandle()};
	return xbWait(timeout,hEv,2,bProcessMsg);
}
inline int xbWait(long timeout,const xbEvent &Ev,const xbEvent &Ev2,const xbEvent &Ev3,bool bProcessMsg = true)
{
	HANDLE hEv[3] = {Ev.GetHandle(),Ev2.GetHandle(),Ev3.GetHandle()};
	return xbWait(timeout,hEv,3,bProcessMsg);
}

void	xbEXPORT xbSleep(ulong timeout);

inline void	xbMsgSleep(ulong timeout)
{
	xbSleep(timeout);
}

#define xbTHREADEVENT_KILL		0x80000000
#define xbTHREADEVENT_STOP		0x40000000
#define xbTHREADEVENT_STOPTASK	0x20000000

class xbThreadEventPtr;
class xbEXPORT xbThreadEventObject:public xbEvent
{
protected:
	xbThreadEventObject()
	{
		m_flags = 0;
		m_cRef = 1;
	}
	virtual ~xbThreadEventObject()
	{
		m_flags = xbTHREADEVENT_KILL;
		xbRdbgAssert(m_cRef == 0);
	}
	xbHeapNewImpl(xbThreadEventObject);
public:

	//CAUTION: Functions not thread safe
	inline bool ProcessToStop() const {return (m_flags & (xbTHREADEVENT_KILL|xbTHREADEVENT_STOP))!=0;}
	inline bool ProcessOrTaskToStop() const {return (m_flags & (xbTHREADEVENT_KILL|xbTHREADEVENT_STOP|xbTHREADEVENT_STOPTASK))!=0;}
	inline bool TaskToStop() const {return (m_flags & (xbTHREADEVENT_STOPTASK))!=0;}
	inline bool ProcessToKill() const {return (m_flags & xbTHREADEVENT_KILL)!=0;}
	inline bool ProcessToContinue() const {return (m_flags & (xbTHREADEVENT_KILL|xbTHREADEVENT_STOP))==0;}
	inline ulong ProcessEvents() const {return m_flags;}

	inline void StopProcess()
	{
		m_flags |= xbTHREADEVENT_STOP;
		Set();
	}
	inline void StopTaskProcess()
	{
		m_flags |= xbTHREADEVENT_STOPTASK;
		Set();
	}
	inline void KillProcess()
	{
		m_flags |= xbTHREADEVENT_KILL;
		Set();
	}
	inline void ResumeProcess()
	{
		m_flags &= ~xbTHREADEVENT_STOP;
	}
	inline void ResumeTaskProcess()
	{
		m_flags &= ~xbTHREADEVENT_STOPTASK;
	}
	inline void Signal(ulong evMask)
	{
		if (evMask)
			m_flags |= evMask;
		Set();
	}
protected:
	bool Create(const xbSecurityAttr *pAttr = NULL, LPCSTR pName = NULL,xbEventMode mode = xbEventAuto)
	{
		m_flags = 0;
		return xbEvent::Create(pAttr,pName,mode);
	}
	void Destroy()
	{
		m_flags |= xbTHREADEVENT_KILL;
		xbEvent::Destroy();
	}
	ulong	m_flags;
	long	m_cRef;

	void addRef()
	{
		::xbInterlockIncrement32(&m_cRef);
	}
	void release()
	{
		if (::xbInterlockDecrement32(&m_cRef) != 0)
			return;
	
		eProtectTrace(delete this;);
	}
private:
	xbDclNoCopyForObject(xbThreadEventObject);

	friend xbThreadEventPtr;
};

class xbEXPORT xbThreadEventPtr
{
public:
	xbThreadEventPtr(xbCtorMode ctormode=xbCtor_CallCreate)
	{
		m_pObj = NULL;
		if (ctormode==xbCtor_CallCreate)
			Create();
	}
	xbThreadEventPtr(xbThreadEventObject *pObj)
	{
		if (pObj)
			pObj->addRef();
		m_pObj = pObj;
	}
	xbThreadEventPtr(const xbThreadEventPtr &ptr)
	{
		if (ptr.m_pObj)
			ptr.m_pObj->addRef();
		m_pObj = ptr.m_pObj;
	}
	~xbThreadEventPtr()
	{
		eProtectTrace(Empty());
	}
	void Create()
	{
		if (m_pObj)
			return;
		m_pObj = xbDnew xbThreadEventObject();
	}
	void Empty()
	{
		if (!m_pObj)
			return;
		m_pObj->release();
		m_pObj = NULL;
	}
	bool IsEmpty() const {return m_pObj == NULL;}
	bool IsValid() const {return m_pObj != NULL;}
	xbThreadEventPtr &operator=(xbThreadEventObject *pObj)
	{
		if (m_pObj)
			Empty();

		if (pObj)
			pObj->addRef();
		m_pObj = pObj;
		return *this;
	}
	xbThreadEventPtr &operator=(const xbThreadEventPtr &ptr)
	{
		if (m_pObj)
			Empty();

		if (ptr.m_pObj)
			ptr.m_pObj->addRef();
		m_pObj = ptr.m_pObj;
		return *this;
	}
	xbThreadEventObject *operator->() const
	{
		xbRdbgAssert(m_pObj);
		return m_pObj;
	}
	operator xbThreadEventObject*() const
	{
		xbRdbgAssert(m_pObj);
		return (xbThreadEventObject*)m_pObj;
	}
	xbEvent& operator*() const
	{
		xbRdbgAssert(m_pObj);
		return *m_pObj;
	}
	inline bool ProcessOrTaskToStop() const {return m_pObj->ProcessOrTaskToStop();}
	inline bool TaskToStop() const {return m_pObj->TaskToStop();}
	inline bool ProcessToStop() const {return m_pObj->ProcessToStop();}
	inline bool ProcessToKill() const {return m_pObj->ProcessToKill();}
	inline bool ProcessToContinue() const {return m_pObj->ProcessToContinue();}
	inline ulong ProcessEvents() const {return m_pObj->ProcessEvents();}
	inline void StopTaskProcess()
	{
		m_pObj->StopTaskProcess();
	}
	inline void StopProcess()
	{
		m_pObj->StopProcess();
	}
	inline void KillProcess()
	{
		m_pObj->KillProcess();
	}
	inline void ResumeProcess()
	{
		m_pObj->ResumeProcess();
	}
	inline void ResumeTaskProcess()
	{
		m_pObj->ResumeTaskProcess();
	}
	inline void Signal(ulong evMask)
	{
		m_pObj->Signal(evMask);
	}
protected:
	xbThreadEventObject	*m_pObj;
private:
	xbDclNoNewForObject(xbThreadEventPtr);
//	xbDclNoCopyForObject(xbThreadEventPtr);
};


#endif
