#ifndef __Thread_h__
#define __Thread_h__
//#ifdef _WIN32_WCE
#include "CeDebug.h"
//#endif
#if !defined(_WIN32_WCE) && !defined(_WIN32_DCOM)
# error DCOM must be defined when not in Windows CE
#endif
#include <objidl.h>
#if !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE)
#include <process.h>
#endif
#ifndef WAIT_OBJECT_1
#define WAIT_OBJECT_1 (WAIT_OBJECT_0 + 1)
#define WAIT_OBJECT_2 (WAIT_OBJECT_0 + 2)
#define WAIT_OBJECT_3 (WAIT_OBJECT_0 + 3)
#endif
#pragma warning(disable: 4018) // signed/unsigned
#ifndef _WIN32_WCE
class CeInProcMarshal
{
private:
LPSTREAM m_pStream;
IID m_iidMarshalled;
public:
CeInProcMarshal() { m_pStream = NULL; }
~CeInProcMarshal()
{
if (NULL != m_pStream)
{
IUnknown* pUnk = NULL;
TRACE(_T("CInProcMarshal: Interface Stream never retrieved, will release now!\n"));
if (S_OK == Get(m_iidMarshalled, (void**) &pUnk))
pUnk->Release();
}
}
HRESULT Set(REFIID riid, LPUNKNOWN pUnk)
{
if (NULL == pUnk)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(riid, pUnk, &m_pStream);
// if (! SUCCEEDED(hr))
// TRACE("CoMarshalInterThreadInterfaceInStream failed\n");
// keep a copy until I can figure out a way to get it from the stream
m_iidMarshalled = riid;
return hr;
}
HRESULT Get(REFIID riid, LPVOID * ppv)
{
if (NULL == m_pStream)
return S_FALSE;
if (m_iidMarshalled != riid)
return E_NOINTERFACE;
HRESULT hr = ::CoGetInterfaceAndReleaseStream(m_pStream, riid, ppv);
if (! SUCCEEDED(hr))
TRACE(_T("CoGetInterfaceAndReleaseStream failed\n"));
else
m_pStream = NULL;
return hr;
}
};
#endif // ! _WIN32_WCE
class CeCriticalSection
{
public:
CRITICAL_SECTION m_cs;
CeCriticalSection() { ::InitializeCriticalSection(&m_cs); }
~CeCriticalSection() { ::DeleteCriticalSection(&m_cs); }
operator CRITICAL_SECTION() { return m_cs; }
void Enter() { ::EnterCriticalSection(&m_cs); }
void Leave() { ::LeaveCriticalSection(&m_cs); }
void Lock() { Enter(); }
void Unlock() { Leave(); }
#if(_WIN32_WINNT >= 0x0401)
BOOL TryLock() { return ::TryEnterCriticalSection(&m_cs); }
BOOL TryEnter() { return ::TryEnterCriticalSection(&m_cs); }
#endif
};
class CeWaitableHandle
{
public:
HANDLE m_handle;
operator HANDLE()
{ return m_handle; }
HANDLE GetHandle() const
{ return m_handle; }
DWORD WaitFor(DWORD dwMilliseconds=INFINITE)
{ return ::WaitForSingleObject(m_handle, dwMilliseconds); }
DWORD MsgWaitFor(DWORD dwMilliseconds=INFINITE)
{
DWORD dwWait;
while (WAIT_OBJECT_0 + 1 == (dwWait = ::MsgWaitForMultipleObjects(1, &m_handle, FALSE, dwMilliseconds, QS_ALLINPUT)))
{
// message to process, process and continue
MSG msg;
// just to be careful, i've run into an unexplainable problem
// where the ::MsgWait... returned with this value, but the
// GetMessage() hung waiting for a message, so just check first
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
}
return dwWait;
}
BOOL Close()
{
if (NULL == m_handle)
return FALSE;
BOOL bRet = ::CloseHandle(m_handle);
m_handle = NULL;
return bRet;
}
};
//
// Manual reset event, requires Reset in order to unsignal the event
//
class CeManualEvent: public CeWaitableHandle
{
public:
CeManualEvent(BOOL bSet=FALSE)
{
m_handle = ::CreateEvent(NULL, TRUE, bSet, NULL);
}
CeManualEvent(LPCTSTR szName, BOOL bSet, LPSECURITY_ATTRIBUTES psa=NULL)
{
m_handle = ::CreateEvent(psa, TRUE, bSet, szName);
}
~CeManualEvent() { Close(); }
void Set() { ::SetEvent(m_handle); }
void Reset() { ::ResetEvent(m_handle); }
void Pulse() { ::PulseEvent(m_handle); }
BOOL IsSet() { return (WAIT_OBJECT_0 == WaitFor(0)); }
};
//
// Auto reset event, the event is reset once a SINGLE waiting thread
// is release from a wait state
//
class CeAutoEvent: public CeWaitableHandle
{
public:
CeAutoEvent(BOOL bSet=FALSE)
{
m_handle = ::CreateEvent(NULL, FALSE, bSet, NULL);
}
CeAutoEvent(LPCTSTR szName, BOOL bSet=FALSE, LPSECURITY_ATTRIBUTES psa=NULL)
{
m_handle = ::CreateEvent(psa, FALSE, bSet, szName);
}
~CeAutoEvent() { Close(); }
void Set() { ::SetEvent(m_handle); }
void Reset() { ::ResetEvent(m_handle); }
void Pulse() { ::PulseEvent(m_handle); }
};
#ifndef _WIN32_WCE
class CeSemaphore: public CeWaitableHandle
{
public:
CeSemaphore(LONG lMax)
{
m_handle = ::CreateSemaphore(NULL, 0, lMax, NULL);
}
~CeSemaphore() { Close(); }
void Release() { ::ReleaseSemaphore(m_handle, 1, NULL); }
LONG Release(LONG lIncr)
{
LONG lOldCnt;
::ReleaseSemaphore(m_handle, lIncr, &lOldCnt);
return lOldCnt;
}
};
#endif //_WIN32_WCE
class CeWaitObjects
{
private:
HANDLE* m_pHandleArray;
int m_nHandles;
public:
// empty wait object group
CeWaitObjects()
{
m_pHandleArray = NULL;
m_nHandles = 0;
}
// Simple two item wait
CeWaitObjects(CeWaitableHandle* pHandle1, CeWaitableHandle* pHandle2)
{
_ASSERTE(NULL != pHandle1);
_ASSERTE(NULL != pHandle2);
m_pHandleArray = new HANDLE[2];
m_nHandles = 2;
m_pHandleArray[0] = (HANDLE) *pHandle1;
m_pHandleArray[1] = (HANDLE) *pHandle2;
}
CeWaitObjects(CeWaitableHandle* pHandleArray, int nHandles)
{
m_pHandleArray = new HANDLE[nHandles];
m_nHandles = nHandles;
for (int ii = 0; ii < nHandles; ii++)
{
_ASSERTE(NULL != m_pHandleArray[ii]);
m_pHandleArray[ii] = pHandleArray[ii];
}
}
BOOL Add(HANDLE h)
{
HANDLE* pTmp = new HANDLE[m_nHandles+1];
if (! pTmp)
return FALSE;
pTmp[m_nHandles] = h;
if (m_pHandleArray)
{
memcpy(pTmp, m_pHandleArray, m_nHandles*sizeof(HANDLE));
delete[] m_pHandleArray;
}
m_pHandleArray = pTmp;
m_nHandles++;
return TRUE;
}
~CeWaitObjects()
{
if (m_pHandleArray)
delete [] m_pHandleArray;
}
DWORD WaitFor(DWORD dwTimeOut=INFINITE, BOOL bWaitForAll=FALSE, DWORD dwWakeMask=0)
{
// number of handles in the object handle array
DWORD dwResult;
if (dwWakeMask == 0)
dwResult = ::WaitForMultipleObjects(m_nHandles,
m_pHandleArray, bWaitForAll, dwTimeOut);
else
dwResult = ::MsgWaitForMultipleObjects(m_nHandles,
m_pHandleArray, bWaitForAll, dwTimeOut, dwWakeMask);
// if (WAIT_FAILED == dwResult)
// TRACE("Error: %u\n", GetLastError());
return dwResult;
}
DWORD MsgWaitFor(DWORD dwTimeOut=INFINITE, BOOL bWaitForAll=FALSE)
{
// wait until a NON-windows message event wakes up the object
DWORD dwWait;
while (WAIT_OBJECT_0 + m_nHandles == (dwWait = ::MsgWaitForMultipleObjects(m_nHandles, m_pHandleArray, bWaitForAll, dwTimeOut, QS_ALLINPUT)))
{
// message to process, process and continue
MSG msg;
// just to be careful, i've run into an unexplainable problem
// where the ::MsgWait... returned with this value, but the
// GetMessage() hung waiting for a message, so just check first
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
}
return dwWait;
}
};
#define WM_WORKER_DONE (WM_USER + 0x400)
// Note:
// This class does NOT work properly if ExitThread() is called from within the thread
// instead you must
class CeWorkerThread: public CeWaitableHandle
{
protected:
bool m_bRunning;
bool m_bAutoDelete;
bool m_bStopping;
public:
static DWORD WINAPI StartThread(LPVOID pParam);
HWND m_hWndMsg;
DWORD m_dwThreadId;
CeManualEvent m_eventStop;
DWORD m_dwStopTimeOut;
bool m_bInitCOM;
COINIT m_nCoInit;
CeWorkerThread(BOOL bAutoDelete=FALSE, HWND hWndMsg=NULL, DWORD dwStopTimeOut=INFINITE);
~CeWorkerThread();
// Methods used by derived classes
virtual void Delete();
virtual void Exit(DWORD dwExitCode);
BOOL IsStopSet() const { return m_bStopping; }
// Methods used by other threads
virtual BOOL Start(int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
virtual void Stop(BOOL bDispatchMsgs=FALSE);
DWORD WaitForComplete();
BOOL IsRunning() const;
virtual BOOL PostThreadMessage(UINT Msg, WPARAM wParam = 0, LPARAM lParam = 0) const;
virtual BOOL PostWindowMessage(UINT Msg, WPARAM wParam = 0, LPARAM lParam = 0) const;
virtual BOOL Kill(DWORD dwExit = 1);
DWORD Pause();
DWORD Suspend();
DWORD Resume();
DWORD GetExitCode();
// Method calls made by either
int GetPriority() const;
BOOL SetPriority(int nPriority);
DWORD GetId() const;
HANDLE GetHandle() const;
// just run the thread function directly, debug stuff
void RunInline();
// override this to make the thread do something (required) ...
virtual UINT ThreadProc() = 0;
// Some virtual calls that happen for the convenience of the
// derived class, this allows the thread to do some state
// management if that would aid it in some way
virtual void OnStop() {}
virtual void OnSuspend() {}
virtual void OnResume() {}
virtual void OnKill() {}
};
#ifdef __AFX_H__
//
// Add MFC support to the thread class
//
class CeMFCWorkerThread : public CeWorkerThread
{
public:
CeMFCWorkerThread()
{
m_pThread = NULL;
}
~CeMFCWorkerThread();
CWinThread* m_pThread;
// these methods are required in order for MFC to function properly,
// the rest of the methods defined for CWorkerThread work properly
// on the handle set up by the Start() method
virtual BOOL Start( int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
virtual void Delete();
virtual void Exit(DWORD dwExitCode);
virtual BOOL Close();
};
inline CeMFCWorkerThread::~CeMFCWorkerThread()
{
// we ALWAYS hang on to the pointer when we're not auto deleting
// so we need to get rid of it now, or else we leak CWinThread objects
if (NULL != m_pThread && ! m_bAutoDelete)
{
// if we hung on to the pointer, then we need to get rid of it manually
m_pThread->m_bAutoDelete = TRUE;
m_pThread->Delete();
m_pThread = NULL;
m_dwThreadId = 0;
m_handle = NULL;
}
}
inline BOOL CeMFCWorkerThread::Start(int nPriority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
// Note: MFC actually starts the thread AFTER calling it's own
// initalization and suspending the thread and returning this pointer
// See the MFC source code for details
m_pThread = AfxBeginThread((AFX_THREADPROC) StartThread,
this, nPriority, nStackSize, dwCreateFlags, lpSecurityAttrs);
if (NULL == m_pThread)
return FALSE;
// have the MFC thread take on the attributes of our thread
// implementation
m_pThread->m_bAutoDelete = m_bAutoDelete;
m_handle = m_pThread->m_hThread;
m_dwThreadId = m_pThread->m_nThreadID;
return TRUE;
}
inline void CeMFCWorkerThread::Delete()
{
if (m_bAutoDelete && m_pThread)
{
m_pThread->m_bAutoDelete = TRUE;
m_pThread->Delete();
// at this point just forget about the MFC thread,
// MFC is still using is after calling us, we'll let MFC handle
// this one also
m_pThread = NULL;
m_handle = NULL;
m_dwThreadId = 0;
}
CeWorkerThread::Delete();
}
inline void CeMFCWorkerThread::Exit(DWORD dwExitCode)
{
if (m_bAutoDelete)
{
// get rid of our pointer states, MFC will shortly
m_pThread = NULL;
m_handle = NULL;
m_dwThreadId = 0;
}
// matchs AfxBeginThread() when aborting the thread
AfxEndThread(dwExitCode);
}
inline BOOL CeMFCWorkerThread::Close()
{
// do nothing, the base class gets involved and tries to close
// the handle and MFC and this class lose
// After a Close() operation the handles are invalid but
// the thread still runs, the only problem comes from
// not being able to wait on completion or get status of any kind
// so, we'll just null our copy and let MFC take care
// of the other later
if (m_pThread)
{
if (! m_pThread->m_bAutoDelete)
// turn on auto delete since we can't deal with the
// thread anymore
m_pThread->m_bAutoDelete = TRUE;
m_pThread = NULL;
}
m_dwThreadId = 0;
m_handle = NULL;
return TRUE;
}
#endif // __AFX_H__
/////////////////////////////////////////////////////////////////////////////
// inline function definitions
inline DWORD WINAPI CeWorkerThread::StartThread(LPVOID pParam)
{
UINT iRet = 1; // assume fatal unless otherwise changed
__try
{
CeWorkerThread* pThis = (CeWorkerThread *) pParam;
if (0 == pThis->m_dwThreadId)
// paranoia, in case the creator didn't
pThis->m_dwThreadId = GetCurrentThreadId();
if (pThis->m_bInitCOM)
// requires DCOM or WinNT 4.0
::CoInitializeEx(NULL, pThis->m_nCoInit);
pThis->m_bRunning = true;
// This is the overridable virtual function that
// is the equivelent to the thread proc
iRet = pThis->ThreadProc();
// thread processing complete, do all we can to notify
// that we have completed processing
pThis->m_bRunning = false; // flag for our or others uses
pThis->PostWindowMessage(WM_WORKER_DONE, iRet, pThis->m_dwThreadId); // post to associated window
if (pThis->m_bInitCOM)
::CoUninitialize();
// note: this only deletes the thread if autodelete is set
pThis->Delete();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
::MessageBox(NULL, _T("Thread termainated abnormally by an exception"),
_T("Fatal"), MB_OK | MB_ICONSTOP);
}
return iRet;
}
inline void CeWorkerThread::Exit(DWORD dwExitCode)
{
// thread processing complete, do all we can to notify
// that we have completed processing
m_bRunning = false; // flag for our or others uses
PostWindowMessage(WM_WORKER_DONE); // post to associated window
if (m_bInitCOM)
::CoUninitialize();
// note: this only deletes the thread if autodelete is set
Delete();
#if defined(_MT) && !defined(_WIN32_WCE)
_endthreadex(dwExitCode);
#else
::ExitThread(dwExitCode);
#endif
}
inline CeWorkerThread::CeWorkerThread(BOOL bAutoDelete/*=FALSE*/, HWND hWndMsg/*=NULL*/, DWORD dwStopTimeOut/*=INFINITE*/)
{
m_handle = NULL;
m_hWndMsg = hWndMsg;
m_dwThreadId = 0;
m_bRunning = false;
m_bInitCOM = true;
m_bStopping = false;
m_nCoInit = (COINIT) COINIT_MULTITHREADED;
m_dwStopTimeOut = dwStopTimeOut;
m_bAutoDelete = (bAutoDelete != FALSE);
}
inline CeWorkerThread::~CeWorkerThread()
{
Stop();
Close();
}
inline void CeWorkerThread::RunInline()
{
StartThread(this);
}
inline BOOL CeWorkerThread::Start(int nPriority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
if (m_bRunning)
{
// TRACE("Start: Thread is already executing, create a new instance if this is what is required!\n");
// already running
return FALSE;
}
#if defined(_MT) && !defined(_WIN32_WCE)
m_handle = (HANDLE) _beginthreadex(lpSecurityAttrs, nStackSize,
(unsigned (__stdcall *) (void *)) StartThread,
(LPVOID) this, dwCreateFlags,
(unsigned int*) &m_dwThreadId);
#else
m_handle = ::CreateThread(lpSecurityAttrs, nStackSize, StartThread,
(LPVOID) this, dwCreateFlags, &m_dwThreadId);
#endif
if (m_handle)
::SetThreadPriority(m_handle, nPriority);
return (m_handle != NULL);
}
inline void CeWorkerThread::Stop(BOOL bDispatchMsgs/*=FALSE*/)
{
// set the stop event for the thread to see
m_eventStop.Set();
m_bStopping = true;
// anything to wait for? may have exited already
if (! m_bRunning || NULL == m_handle)
return;
// wait until complete, kill if we timeout,
// the default implementation waits for infinite time
// and the Kill() will never get called in that case
// notify the derived class that we've been asked to stop,
// this will allow the thread to kill any blocking functions
// so that it can process the stop request
OnStop();
// don't wait if we have autodelete set, since this
// will just cause a protection fault anyway in the waitfor
if (m_bAutoDelete)
// will delete object on return, thus you CANNOT wait on it
return;
if (bDispatchMsgs)
{
// for GUI threads, get and dispatch messages while waiting
// for the thread to terminate
if (WAIT_TIMEOUT == MsgWaitFor(m_dwStopTimeOut))
// wait for the time out, kill the thread
Kill();
}
else
{
if (WAIT_TIMEOUT == WaitFor(m_dwStopTimeOut))
// wait for the time out, kill the thread
Kill();
}
}
inline void CeWorkerThread::Delete()
{
if (m_bAutoDelete)
delete this;
}
inline DWORD CeWorkerThread::WaitForComplete()
{
_ASSERTE(! m_bAutoDelete);
return WaitFor(INFINITE);
}
inline BOOL CeWorkerThread::IsRunning() const
{
return m_bRunning;
}
inline int CeWorkerThread::GetPriority() const
{
return ::GetThreadPriority(m_handle);
}
inline BOOL CeWorkerThread::SetPriority(int nPriority)
{
return ::SetThreadPriority(m_handle, nPriority);
}
inline DWORD CeWorkerThread::GetId() const
{
return m_dwThreadId;
}
inline BOOL CeWorkerThread::PostThreadMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const
{
if (m_dwThreadId)
return ::PostThreadMessage(m_dwThreadId, Msg, wParam, lParam);
return FALSE;
}
inline BOOL CeWorkerThread::PostWindowMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const
{
if (m_hWndMsg)
return ::PostMessage(m_hWndMsg, Msg, wParam, lParam);
return FALSE;
}
inline BOOL CeWorkerThread::Kill(DWORD dwExit)
{
OnKill();
return ::TerminateThread(m_handle, dwExit);
}
inline DWORD CeWorkerThread::Pause()
{
return Suspend();
}
inline DWORD CeWorkerThread::Suspend()
{
OnSuspend();
return ::SuspendThread(m_handle);
}
inline DWORD CeWorkerThread::Resume()
{
OnResume();
return ::ResumeThread(m_handle);
}
inline DWORD CeWorkerThread::GetExitCode()
{
DWORD dwRet;
if (! ::GetExitCodeThread(m_handle, &dwRet))
return 0xffffffff;
if (STILL_ACTIVE == dwRet)
TRACE(_T("Thread Still Active\n"));
else
TRACE(_T("Thread Exit Code %lu\n"), dwRet);
return dwRet;
}
#endif // Thread_h__