359 lines
8.8 KiB
C++
359 lines
8.8 KiB
C++
|
// worker.cpp
|
||
|
//
|
||
|
// Implementation of the worker thread object
|
||
|
//
|
||
|
|
||
|
#include "priv.h"
|
||
|
|
||
|
// Do not build this file if on Win9X or NT4
|
||
|
#ifndef DOWNLEVEL_PLATFORM
|
||
|
|
||
|
#include "resource.h"
|
||
|
#include "worker.h"
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
//
|
||
|
//
|
||
|
// CWorkerThread class
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
// CWorkerThread constructor
|
||
|
|
||
|
CWorkerThread::CWorkerThread() : _cRef(1)
|
||
|
{
|
||
|
ASSERT(NULL == _hthreadWorker);
|
||
|
ASSERT(NULL == _hwndWorker);
|
||
|
ASSERT(FALSE == _fKillWorker);
|
||
|
ASSERT(NULL == _pwe);
|
||
|
ASSERT(0 == _cRefLockWorker);
|
||
|
|
||
|
InitializeCriticalSection(&_csWorker);
|
||
|
DllAddRef();
|
||
|
}
|
||
|
|
||
|
|
||
|
// CWorkerThread destructor
|
||
|
|
||
|
CWorkerThread::~CWorkerThread()
|
||
|
{
|
||
|
ASSERT(0 == _cRefLockWorker);
|
||
|
|
||
|
if (IsWindow(_hwndWorker))
|
||
|
{
|
||
|
DestroyWindow(_hwndWorker);
|
||
|
}
|
||
|
|
||
|
SetListenerWT(NULL);
|
||
|
DeleteCriticalSection(&_csWorker);
|
||
|
|
||
|
DllRelease();
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CWorkerThread::PostWorkerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
_LockWorker();
|
||
|
// Only if we are not being killed, and the worker window is valid we
|
||
|
// post the message, we don't want to post to NULL window (desktop)
|
||
|
if (!_fKillWorker && _hwndWorker)
|
||
|
bRet = PostMessage(_hwndWorker, uMsg, wParam, lParam);
|
||
|
_UnlockWorker();
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------
|
||
|
Purpose: IUnknown::QueryInterface
|
||
|
*/
|
||
|
STDMETHODIMP CWorkerThread::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
||
|
{
|
||
|
static const QITAB qit[] = {
|
||
|
QITABENT(CWorkerThread, IARPWorker),
|
||
|
{ 0 },
|
||
|
};
|
||
|
|
||
|
return QISearch(this, (LPCQITAB)qit, riid, ppvObj);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CWorkerThread::AddRef()
|
||
|
{
|
||
|
LONG cRef = InterlockedIncrement(&_cRef);
|
||
|
TraceAddRef(CWorkerThread, cRef);
|
||
|
return cRef;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CWorkerThread::Release()
|
||
|
{
|
||
|
LONG cRef = InterlockedDecrement(&_cRef);
|
||
|
TraceRelease(CWorkerThread, cRef);
|
||
|
if (cRef)
|
||
|
return cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Sets the event listener so the worker thread can fire
|
||
|
events incrementally.
|
||
|
*/
|
||
|
HRESULT CWorkerThread::SetListenerWT(IWorkerEvent * pwe)
|
||
|
{
|
||
|
_LockWorker();
|
||
|
{
|
||
|
// We have to protect _pwe because it can be accessed by this
|
||
|
// thread and the main thread.
|
||
|
|
||
|
// Don't AddRef the event listener or we'll have a circular
|
||
|
// reference.
|
||
|
_pwe = pwe;
|
||
|
}
|
||
|
_UnlockWorker();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Start the thread
|
||
|
*/
|
||
|
HRESULT CWorkerThread::StartWT(int iPriority)
|
||
|
{
|
||
|
DWORD thid; // Not used but we have to pass something in
|
||
|
|
||
|
// Create a hidden top-level window to post messages to from the
|
||
|
// worker thread
|
||
|
_hwndWorker = SHCreateWorkerWindow(_WorkerWndProcWrapper, NULL, 0, 0, NULL, this);
|
||
|
|
||
|
if (_hwndWorker)
|
||
|
{
|
||
|
AddRef(); // AddRef myself, the background thread is responsible of releasing
|
||
|
// this ref count
|
||
|
|
||
|
// Kick off the worker thread to do the slow enumeration.
|
||
|
_hthreadWorker = CreateThread(NULL, 0,
|
||
|
(LPTHREAD_START_ROUTINE)_ThreadStartProcWrapper,
|
||
|
(LPVOID)this, CREATE_SUSPENDED, &thid);
|
||
|
|
||
|
if (_hthreadWorker)
|
||
|
{
|
||
|
// Demote the priority so it doesn't interfere with the
|
||
|
// initial HTML databinding.
|
||
|
SetThreadPriority(_hthreadWorker, iPriority);
|
||
|
ResumeThread(_hthreadWorker);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Release my refcount in case of failure
|
||
|
Release();
|
||
|
|
||
|
// If we can't create the background thread, don't bother with the window
|
||
|
DestroyWindow(_hwndWorker);
|
||
|
_hwndWorker = NULL;
|
||
|
}
|
||
|
}
|
||
|
return (_hthreadWorker != NULL) ? S_OK : E_FAIL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Kills the worker thread if one is around
|
||
|
*/
|
||
|
|
||
|
HRESULT CWorkerThread::KillWT(void)
|
||
|
{
|
||
|
MSG msg;
|
||
|
|
||
|
// I should never call KillWT to kill myself
|
||
|
ASSERT(_hthreadWorker != GetCurrentThread());
|
||
|
|
||
|
TraceMsg(TF_TASKS, "[%x] Killing worker thread...", _dwThreadId);
|
||
|
|
||
|
// Tell the worker thread to stop when it can
|
||
|
// Do this inside the critical section because we don't want random messages
|
||
|
// get posted to the worker window after this.
|
||
|
_LockWorker();
|
||
|
_fKillWorker = TRUE;
|
||
|
_UnlockWorker();
|
||
|
|
||
|
// If we have no worker thread, nothing to do
|
||
|
if (_hthreadWorker)
|
||
|
{
|
||
|
// Now wait for the worker to stop
|
||
|
if (WaitForSingleObject(_hthreadWorker, 10000) == WAIT_TIMEOUT)
|
||
|
TraceMsg(TF_ERROR, "[%x] Worker thread termination wait timed out!", _dwThreadId);
|
||
|
else
|
||
|
TraceMsg(TF_TASKS, "[%x] Worker thread wait exited cleanly", _dwThreadId);
|
||
|
|
||
|
// Now that the thread is stopped, release our hold so all its memory can go away
|
||
|
CloseHandle(_hthreadWorker);
|
||
|
_hthreadWorker = NULL;
|
||
|
}
|
||
|
|
||
|
// Make sure that all messages to our worker HWND get processed
|
||
|
if (_hwndWorker)
|
||
|
{
|
||
|
while (PeekMessage(&msg, _hwndWorker, 0, 0, PM_REMOVE))
|
||
|
DispatchMessage(&msg);
|
||
|
|
||
|
DestroyWindow(_hwndWorker);
|
||
|
_hwndWorker = NULL;
|
||
|
}
|
||
|
SetListenerWT(NULL);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
// Private methods
|
||
|
|
||
|
|
||
|
|
||
|
void CWorkerThread::_LockWorker(void)
|
||
|
{
|
||
|
EnterCriticalSection(&_csWorker);
|
||
|
DEBUG_CODE( _cRefLockWorker++; )
|
||
|
}
|
||
|
|
||
|
void CWorkerThread::_UnlockWorker(void)
|
||
|
{
|
||
|
DEBUG_CODE( _cRefLockWorker--; )
|
||
|
LeaveCriticalSection(&_csWorker);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Static wndproc wrapper. Calls the real non-static WndProc.
|
||
|
*/
|
||
|
LRESULT
|
||
|
CALLBACK
|
||
|
CWorkerThread::_WorkerWndProcWrapper(
|
||
|
HWND hwnd,
|
||
|
UINT uMsg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
if (uMsg == WM_DESTROY)
|
||
|
SetWindowLongPtr(hwnd, 0, 0);
|
||
|
else
|
||
|
{
|
||
|
CWorkerThread * pWorker = (CWorkerThread*)GetWindowLongPtr(hwnd, 0);
|
||
|
if (pWorker)
|
||
|
return pWorker->_WorkerWndProc(hwnd, uMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Used to fire events back to Trident since they can't be fired from
|
||
|
the worker thread.
|
||
|
*/
|
||
|
LRESULT
|
||
|
CWorkerThread::_WorkerWndProc(
|
||
|
HWND hwnd,
|
||
|
UINT uMsg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case WORKERWIN_FIRE_ROW_READY:
|
||
|
// Posted by worker thread when async data is ready
|
||
|
_LockWorker();
|
||
|
{
|
||
|
if (_pwe)
|
||
|
{
|
||
|
TraceMsg(TF_TASKS, "[%x] Firing event for row #%d", _dwThreadId, wParam);
|
||
|
_pwe->FireOnDataReady((LONG)wParam);
|
||
|
}
|
||
|
}
|
||
|
_UnlockWorker();
|
||
|
return 0;
|
||
|
|
||
|
case WORKERWIN_FIRE_FINISHED:
|
||
|
// Posted by worker thread when thread is finished enumerating
|
||
|
// async data.
|
||
|
_LockWorker();
|
||
|
{
|
||
|
if (_pwe)
|
||
|
{
|
||
|
TraceMsg(TF_TASKS, "[%x] Firing finished", _dwThreadId);
|
||
|
_pwe->FireOnFinished();
|
||
|
}
|
||
|
}
|
||
|
_UnlockWorker();
|
||
|
return 0;
|
||
|
|
||
|
case WORKERWIN_FIRE_DATASETCHANGED:
|
||
|
// Posted by worker thread when matrix array finished enumerating
|
||
|
_LockWorker();
|
||
|
{
|
||
|
if (_pwe)
|
||
|
{
|
||
|
TraceMsg(TF_TASKS, "[%x] Firing DatasetChanged", _dwThreadId);
|
||
|
_pwe->FireOnDatasetChanged();
|
||
|
}
|
||
|
}
|
||
|
_UnlockWorker();
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Start and exit of worker thread to do slow app information
|
||
|
*/
|
||
|
DWORD
|
||
|
CALLBACK
|
||
|
CWorkerThread::_ThreadStartProcWrapper(
|
||
|
LPVOID lpParam // this pointer of object since wrapper is static
|
||
|
)
|
||
|
{
|
||
|
CWorkerThread * pwt = (CWorkerThread *)lpParam;
|
||
|
|
||
|
pwt->_dwThreadId = GetCurrentThreadId();
|
||
|
|
||
|
return pwt->_ThreadStartProc();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*-------------------------------------------------------------------------
|
||
|
Purpose: Contains the code run on the worker thread where we get the slow
|
||
|
information about applications
|
||
|
*/
|
||
|
DWORD
|
||
|
CWorkerThread::_ThreadStartProc()
|
||
|
{
|
||
|
// Don't bother killing the worker window here, let the main thread take care
|
||
|
// of the life time of the worker window.
|
||
|
|
||
|
// Signal that we don't have a worker thread anymore. Prevents race
|
||
|
// conditions.
|
||
|
_fKillWorker = FALSE;
|
||
|
|
||
|
TraceMsg(TF_TASKS, "[%x] Exiting worker thread", _dwThreadId);
|
||
|
|
||
|
// Release the ref count to "this" object at end of thread, be it CMtxArray or CDataSrc because we
|
||
|
// AddRef()ed before this thread started.
|
||
|
Release();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif //DOWNLEVEL_PLATFORM
|