Figure 3: Classes MultiThreadState and MultiThread

/* ============================================================
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;
}