#include "stock.h" #pragma hdrstop #include "runtask.h" #define SUPERCLASS // #define TF_RUNTASK TF_GENERAL #define TF_RUNTASK 0 // #define TF_RUNTASKV TF_CUSTOM1 // verbose version #define TF_RUNTASKV 0 // constructor CRunnableTask::CRunnableTask(DWORD dwFlags) { _lState = IRTIR_TASK_NOT_RUNNING; _dwFlags = dwFlags; ASSERT(NULL == _hDone); if (_dwFlags & RTF_SUPPORTKILLSUSPEND) { // we signal this on suspend or kill // Explicitly call the ANSI version so we don't need to worry // about whether we're being built UNICODE and have to switch // to a wrapper function... _hDone = CreateEventA(NULL, TRUE, FALSE, NULL); } #ifdef DEBUG _dwTaskID = GetTickCount(); TraceMsg(TF_RUNTASK, "CRunnableTask (%#lx): creating task", _dwTaskID); #endif _cRef = 1; } // destructor CRunnableTask::~CRunnableTask() { DEBUG_CODE( TraceMsg(TF_RUNTASK, "CRunnableTask (%#lx): deleting task", _dwTaskID); ) if (_hDone) CloseHandle(_hDone); } STDMETHODIMP CRunnableTask::QueryInterface( REFIID riid, LPVOID * ppvObj ) { if ( ppvObj == NULL ) { return E_INVALIDARG; } if ( riid == IID_IRunnableTask ) { *ppvObj = SAFECAST( this, IRunnableTask *); AddRef(); } else return E_NOINTERFACE; return NOERROR; } STDMETHODIMP_(ULONG) CRunnableTask::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_ (ULONG) CRunnableTask::Release() { AssertMsg( 0 != _cRef, TEXT("RefCount problem.") ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } /*---------------------------------------------------------- Purpose: IRunnableTask::Run method This does a lot of the state-related work, and then calls the derived-class's RunRT() method. */ STDMETHODIMP CRunnableTask::Run(void) { HRESULT hr = E_FAIL; // Are we already running? if (_lState == IRTIR_TASK_RUNNING) { // Yes; nothing to do hr = S_FALSE; } else if ( _lState == IRTIR_TASK_PENDING ) { hr = E_FAIL; } else if ( _lState == IRTIR_TASK_NOT_RUNNING ) { // Say we're running LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_RUNNING); if ( lRes == IRTIR_TASK_PENDING ) { _lState = IRTIR_TASK_FINISHED; return NOERROR; } if (_lState == IRTIR_TASK_RUNNING) { // Prepare to run DEBUG_CODE( TraceMsg(TF_RUNTASKV, "CRunnableTask (%#lx): initialize to run", _dwTaskID); ) hr = RunInitRT(); ASSERT(E_PENDING != hr); } if (SUCCEEDED(hr)) { if (_lState == IRTIR_TASK_RUNNING) { // Continue to do the work hr = InternalResumeRT(); } else if (_lState == IRTIR_TASK_SUSPENDED) { // it is possible that RunInitRT took a little longer to complete and our state changed // from running to suspended with _hDone signaled, which would cause us to not call // internal resume. We simulate internal resume here if (_hDone) ResetEvent(_hDone); hr = E_PENDING; } } if (FAILED(hr) && E_PENDING != hr) { DEBUG_CODE( TraceMsg(TF_WARNING, "CRunnableTask (%#lx): task failed to run: %#lx", _dwTaskID, hr); ) } // Are we finished? if (_lState != IRTIR_TASK_SUSPENDED || hr != E_PENDING) { // Yes _lState = IRTIR_TASK_FINISHED; } } return hr; } /*---------------------------------------------------------- Purpose: IRunnableTask::Kill method */ STDMETHODIMP CRunnableTask::Kill(BOOL fWait) { if ( !(_dwFlags & RTF_SUPPORTKILLSUSPEND) ) return E_NOTIMPL; if (_lState != IRTIR_TASK_RUNNING) return S_FALSE; DEBUG_CODE( TraceMsg(TF_RUNTASKV, "CRunnableTask (%#lx): killing task", _dwTaskID); ) LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_PENDING); if (lRes == IRTIR_TASK_FINISHED) { DEBUG_CODE( TraceMsg(TF_RUNTASKV, "CRunnableTask (%#lx): task already finished", _dwTaskID); ) _lState = lRes; } else if (_hDone) { // signal the event it is likely to be waiting on SetEvent(_hDone); } return KillRT(fWait); } /*---------------------------------------------------------- Purpose: IRunnableTask::Suspend method */ STDMETHODIMP CRunnableTask::Suspend( void ) { if ( !(_dwFlags & RTF_SUPPORTKILLSUSPEND) ) return E_NOTIMPL; if (_lState != IRTIR_TASK_RUNNING) return E_FAIL; DEBUG_CODE( TraceMsg(TF_RUNTASKV, "CRunnableTask (%#lx): suspending task", _dwTaskID); ) LONG lRes = InterlockedExchange(&_lState, IRTIR_TASK_SUSPENDED); if (IRTIR_TASK_FINISHED == lRes) { // we finished before we could suspend DEBUG_CODE( TraceMsg(TF_RUNTASKV, "CRunnableTask (%#lx): task already finished", _dwTaskID); ) _lState = lRes; return NOERROR; } if (_hDone) SetEvent(_hDone); return SuspendRT(); } /*---------------------------------------------------------- Purpose: IRunnableTask::Resume method */ STDMETHODIMP CRunnableTask::Resume(void) { if (_lState != IRTIR_TASK_SUSPENDED) return E_FAIL; DEBUG_CODE( TraceMsg(TF_RUNTASKV, "CRunnableTask (%#lx): resuming task", _dwTaskID); ) _lState = IRTIR_TASK_RUNNING; if (_hDone) ResetEvent(_hDone); return ResumeRT(); } /*---------------------------------------------------------- Purpose: IRunnableTask::IsRunning method */ STDMETHODIMP_( ULONG ) CRunnableTask:: IsRunning ( void ) { return _lState; }