// 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