/* ============================================================
MultiThread.h
============================================================ */
#ifndef MULTITHREAD_H
#define MULTITHREAD_H
#include <windows.h>
class ThreadableObject;
DWORD WINAPI MultiThreadImpl( LPVOID lpParam );
class MultiThreadState
{
public:
enum { UNINITIALIZED, INITIALIZED,
WORKING, SLEEPING, DONE,
SUSPENDED, RESUMED };
MultiThreadState();
~MultiThreadState();
void Reset();
private:
// not implemented
MultiThreadState( const MultiThreadState& copyMe );
MultiThreadState& operator=( const MultiThreadState& rhs );
friend class MultiThread;
friend DWORD WINAPI MultiThreadImpl( LPVOID lpParam );
// Closing handle twice is an error.
void CloseThreadHandle();
HANDLE m_hThread;
HANDLE m_hRunEvent;
HANDLE m_hWorkEvent;
HANDLE m_hDoneEvent;
// Controls access to following data.
HANDLE m_hMutex;
DWORD m_dwCycleTime;
bool m_bStopThread;
int m_eState;
DWORD m_dwSuspendCount;
ThreadableObject* m_pObject;
};
class MultiThread
{
public:
MultiThread();
~MultiThread();
bool Initialize(
ThreadableObject* ptrClass,
DWORD dwMilliSecs,
DWORD dwStackSize = 0 );
void Run();
void RequestStop();
bool IsStopping() const;
void DoWork();
DWORD CycleTime() const;
void CycleTime( DWORD dwCycleTime );
// Similar to Win32 functions, except they return the
// threads current suspend count, not the previous count.
DWORD Suspend();
DWORD Resume();
bool IsSuspended( DWORD* dwSuspendCount = NULL ) const;
// Same as Win32 functions GetExitCodeThread and
// SetThreadPriority.
BOOL ExitCode( LPDWORD lpExitCode );
BOOL Priority( int nPriority );
// Suspends caller until DoneEvent is signalled.
void WaitUntilDone();
// See enum in MultiThreadState for return values.
int ThreadState() const;
private:
// not implemented
MultiThread( const MultiThread& copyMe );
MultiThread& operator=( const MultiThread& rhs );
MultiThreadState m_td;
};
#endif
/* ==============================================
MultiThread.cpp
============================================== */
#include "multithread.h"
#include "threadableobject.h"
#include "mutexlock.h"
#include <assert.h>
// Error code for SuspendThread, ResumeThread.
static const DWORD SR_ERROR = 0xFFFFFFFF;
DWORD WINAPI MultiThreadImpl(LPVOID lpParam)
{
MultiThreadState* ptrState = (MultiThreadState*) lpParam;
// Synch on the Run Event
WaitForSingleObject( ptrState->m_hRunEvent, INFINITE );
bool bStopThread = false;
bool bWorkInProgress = true;
DWORD dwCycleTime = 0;
DWORD dwReturn = 0;
ThreadableObject *pObj = 0;
// Latch values once to start loop
{
MutexLock( ptrState->m_hMutex );
dwCycleTime = ptrState->m_dwCycleTime;
ptrState->m_eState = MultiThreadState::SLEEPING;
pObj = ptrState->m_pObject;
}
do
{
// Wait for a Work event, or a timeout.
WaitForSingleObject( ptrState->m_hWorkEvent,
dwCycleTime );
{
MutexLock( ptrState->m_hMutex );
ptrState->m_eState = MultiThreadState::WORKING;
}
bWorkInProgress = pObj->ThreadableTask( &dwReturn );
if ( bWorkInProgress )
{
// Latch values for next repitition.
MutexLock( ptrState->m_hMutex );
dwCycleTime = ptrState->m_dwCycleTime;
bStopThread = ptrState->m_bStopThread;
ptrState->m_eState = MultiThreadState::SLEEPING;
}
} while ( !bStopThread && bWorkInProgress );
// Thread finished.
{
MutexLock( ptrState->m_hMutex );
ptrState->m_bStopThread = true;
ptrState->m_eState = MultiThreadState::DONE;
}
SetEvent(ptrState->m_hDoneEvent);
return dwReturn;
}
MultiThreadState::MultiThreadState()
{
m_hThread = 0;
// lpEventAttributes, bManualReset, bInitialState, lpName
m_hRunEvent = CreateEvent( NULL, false, false, NULL );
assert( m_hRunEvent );
m_hWorkEvent = CreateEvent( NULL, false, false, NULL );
assert( m_hWorkEvent );
// Manual reset since multiple threads can wait on this
// event.
m_hDoneEvent = CreateEvent( NULL, true, false, NULL );
assert( m_hDoneEvent );
// Security Attr., bInitialOwner, lpName
m_hMutex = CreateMutex( NULL, false, NULL );
assert( m_hMutex );
// Should be consistent with code in Reset.
m_dwCycleTime = 0;
m_bStopThread = false;
m_eState = UNINITIALIZED;
m_dwSuspendCount = 0;
m_pObject = 0;
}
MultiThreadState::~MultiThreadState()
{
CloseThreadHandle();
CloseHandle( m_hRunEvent );
CloseHandle( m_hWorkEvent );
CloseHandle( m_hDoneEvent );
CloseHandle( m_hMutex );
}
void MultiThreadState::Reset()
{
CloseThreadHandle();
// This code should be consistent with
// code in the constructor.
ResetEvent( m_hRunEvent );
ResetEvent( m_hWorkEvent );
ResetEvent( m_hDoneEvent );
m_dwCycleTime = 0;
m_bStopThread = false;
m_eState = UNINITIALIZED;
m_dwSuspendCount = 0;
m_pObject = 0;
}
void MultiThreadState::CloseThreadHandle()
{
// Closing a HANDLE twice is an error.
// Allowing multiple calls to Initialize
// requires this.
if ( m_hThread != 0 )
{
CloseHandle( m_hThread );
m_hThread = 0;
}
}
MultiThread::MultiThread()
:m_td( MultiThreadState() )
{}
MultiThread::~MultiThread()
{
// Do what we can to end thread,
// but ThreadableTask must still cooperate!
if ( m_td.m_hThread != 0 )
{
while ( IsSuspended() )
{
Resume();
}
Run();
DoWork();
RequestStop();
WaitUntilDone();
}
}
bool MultiThread::Initialize
(
ThreadableObject* ptrClass,
DWORD dwMilliSecs,
DWORD dwStackSize
)
{
MutexLock( m_td.m_hMutex );
// User must cleanly exit previous thread, if any.
assert( m_td.m_eState == MultiThreadState::DONE ||
m_td.m_eState == MultiThreadState::UNINITIALIZED );
bool bRetVal = false;
// Initialize can be called multiple times.
m_td.Reset();
if ( m_td.m_hThread == 0 )
{
DWORD dwThreadId = 0;
m_td.m_hThread = CreateThread( NULL, dwStackSize,
&MultiThreadImpl, (LPVOID) &m_td,
0, &dwThreadId);
if ( m_td.m_hThread )
{
m_td.m_dwCycleTime = dwMilliSecs;
m_td.m_eState = MultiThreadState::INITIALIZED;
m_td.m_pObject = ptrClass;
bRetVal = true;
} else
{
// Explicitly set just in case.
m_td.m_hThread = 0;
}
}
return bRetVal;
}
void MultiThread::Run()
{
MutexLock( m_td.m_hMutex );
if ( m_td.m_eState == MultiThreadState::INITIALIZED )
{
SetEvent( m_td.m_hRunEvent );
}
}
void MultiThread::RequestStop()
{
MutexLock( m_td.m_hMutex );
m_td.m_bStopThread = true;
}
bool MultiThread::IsStopping() const
{
MutexLock( m_td.m_hMutex );
return m_td.m_bStopThread;
}
void MultiThread::DoWork()
{
MutexLock( m_td.m_hMutex );
if ( m_td.m_eState == MultiThreadState::SLEEPING )
{
SetEvent( m_td.m_hWorkEvent );
}
}
DWORD MultiThread::CycleTime() const
{
MutexLock( m_td.m_hMutex );
return m_td.m_dwCycleTime;
}
void MultiThread::CycleTime( DWORD dwCycleTime )
{
MutexLock( m_td.m_hMutex );
m_td.m_dwCycleTime = dwCycleTime;
}
DWORD MultiThread::Suspend()
{
MutexLock( m_td.m_hMutex );
if ( ::SuspendThread( m_td.m_hThread ) != SR_ERROR )
{
++m_td.m_dwSuspendCount;
if ( m_td.m_dwSuspendCount > 0 )
{
m_td.m_eState = MultiThreadState::SUSPENDED;
}
}
return m_td.m_dwSuspendCount;
}
DWORD MultiThread::Resume()
{
MutexLock( m_td.m_hMutex );
if ( ::ResumeThread( m_td.m_hThread ) != SR_ERROR )
{
--m_td.m_dwSuspendCount;
if ( m_td.m_dwSuspendCount == 0 )
{
m_td.m_eState = MultiThreadState::RESUMED;
}
}
return m_td.m_dwSuspendCount;
}
bool MultiThread::IsSuspended( DWORD* dwSuspendCount ) const
{
MutexLock( m_td.m_hMutex );
if ( dwSuspendCount != NULL )
{
*dwSuspendCount = m_td.m_dwSuspendCount;
}
return ( m_td.m_dwSuspendCount != 0 ? true : false );
}
BOOL MultiThread::ExitCode( LPDWORD lpExitCode )
{
return ::GetExitCodeThread(m_td.m_hThread, lpExitCode);
}
BOOL MultiThread::Priority( int nPriority )
{
return ::SetThreadPriority( m_td.m_hThread, nPriority );
}
void MultiThread::WaitUntilDone()
{
WaitForSingleObject( m_td.m_hDoneEvent, INFINITE );
}
int MultiThread::ThreadState() const
{
MutexLock( m_td.m_hMutex );
return m_td.m_eState;
}