|
|
/**********************************************************************/ /** Microsoft Windows/NT **/ /** Copyright(c) Microsoft Corporation, 1997 - 1999 **/ /**********************************************************************/
/*
queryobj.cpp Implementation for nodes in the MMC
FILE HISTORY: */
#include "stdafx.h"
#include "queryobj.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
/////////////////////////////////////////////////////////////////////
//
// CBackgroundThread
//
/////////////////////////////////////////////////////////////////////
DEBUG_DECLARE_INSTANCE_COUNTER(CBackgroundThread);
CBackgroundThread::CBackgroundThread() { DEBUG_INCREMENT_INSTANCE_COUNTER(CBackgroundThread);
m_bAutoDelete = TRUE; ::InitializeCriticalSection(&m_cs); }
CBackgroundThread::~CBackgroundThread() { DEBUG_DECREMENT_INSTANCE_COUNTER(CBackgroundThread);
// Trace0("CBackgroundThread::~CBackgroundThread()\n");
::DeleteCriticalSection(&m_cs); m_spQuery.Release(); }
void CBackgroundThread::SetQueryObj(ITFSQueryObject *pQuery) { Assert(pQuery != NULL); m_spQuery.Set(pQuery); }
BOOL CBackgroundThread::Start() { // NOTE::: ericdav 10/23/97
// the thread is initially suspended so we can duplicate the handle
// if the query object exits very quickly, the background thread object
// may be destroyed before we can duplicate the handle. Right after
// we duplicate the handle, it is started.
return CreateThread(CREATE_SUSPENDED); }
int CBackgroundThread::Run() { DWORD dwRet; DWORD dwData; BOOL fAbort = FALSE; Assert(m_spQuery); // Trace0("CBackgroundThread::Run() started\n");
for (;;) { try { if (m_spQuery->Execute() != hrOK) break; } catch(...) { // Trace1("%x Caught an exception while executing CQuerObj!\n",
// GetCurrentThreadId());
fAbort = TRUE; }
//$ Review: kennt
// Should we sleep a little while at this point? especially
// since the thread has given us some data to process.
// Check to see if the abort flag is set
if (fAbort || FHrOK(m_spQuery->FCheckForAbort())) { break; } }
// Notify the query object that we are exiting
if (fAbort || FHrOK(m_spQuery->FCheckForAbort())) m_spQuery->OnEventAbort(); else m_spQuery->OnThreadExit();
m_spQuery->DoCleanup(); Trace2("handle=%X id=%X CBackgroundThread::Run() terminated\n", m_hThread, m_nThreadID); return 0; }
/*---------------------------------------------------------------------------
CQueryObject implementation ---------------------------------------------------------------------------*/ DEBUG_DECLARE_INSTANCE_COUNTER(CQueryObject);
/*!--------------------------------------------------------------------------
CQueryObject::CQueryObject - Author: KennT ---------------------------------------------------------------------------*/ CQueryObject::CQueryObject() { DEBUG_INCREMENT_INSTANCE_COUNTER(CQueryObject);
m_cRef = 1; m_hEventAbort = NULL; ::InitializeCriticalSection(&m_cs); }
/*!--------------------------------------------------------------------------
CQueryObject::~CQueryObject - Author: KennT ---------------------------------------------------------------------------*/ CQueryObject::~CQueryObject() { DEBUG_DECREMENT_INSTANCE_COUNTER(CQueryObject);
Assert(m_cRef == 0); ::DeleteCriticalSection(&m_cs); ::CloseHandle(m_hEventAbort); m_hEventAbort = 0; // Trace1("%X CQueryObject::~CQueryObject()\n", GetCurrentThreadId());
}
IMPLEMENT_ADDREF_RELEASE(CQueryObject)
STDMETHODIMP CQueryObject::QueryInterface(REFIID riid, LPVOID *ppv) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Is the pointer bad?
if (ppv == NULL) return E_INVALIDARG;
// Place NULL in *ppv in case of failure
*ppv = NULL;
// This is the non-delegating IUnknown implementation
if (riid == IID_IUnknown) *ppv = (LPVOID) this; else if (riid == IID_ITFSQueryObject) *ppv = (ITFSQueryObject *) this;
// If we're going to return an interface, AddRef it first
if (*ppv) { ((LPUNKNOWN) *ppv)->AddRef(); return hrOK; } else return E_NOINTERFACE; }
/*!--------------------------------------------------------------------------
CQueryObject::Init - Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP CQueryObject::Init(ITFSThreadHandler *pHandler, HWND hwndHidden, UINT uMsgBase) { Assert(m_spHandler == NULL); m_spHandler.Set(pHandler);
m_hHiddenWnd = hwndHidden; m_uMsgBase = uMsgBase; m_hEventAbort = ::CreateEvent(NULL, TRUE /*bManualReset*/, FALSE /*signalled*/, NULL); if (m_hEventAbort == NULL) return HRESULT_FROM_WIN32(GetLastError()); else return hrOK; } /*!--------------------------------------------------------------------------
CQueryObject::SetAbortEvent - Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP CQueryObject::SetAbortEvent() { // Trace1("%X Signalling CQueryObject abort event.\n", GetCurrentThreadId());
Assert(m_hEventAbort); ::SetEvent(m_hEventAbort); OnEventAbort(); // flush out the message queue in case something is wait to be processed
MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); }
return hrOK; }
/*!--------------------------------------------------------------------------
CQueryObject::FCheckForAbort - Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP CQueryObject::FCheckForAbort() { // Assert(m_hEventAbort);
// we may not be running as a background thread, but somebody may have
// created this object to do somework... In which case this isn't valid,
// and just return ok
if (!m_hEventAbort) return hrOK;
DWORD dwRet = WaitForSingleObjectEx(m_hEventAbort, 0, FALSE); #ifdef DEBUG
// if (dwRet == WAIT_OBJECT_0)
// Trace1("%X CQueryObject() detects an abort event!\n", GetCurrentThreadId());
#endif
return dwRet == WAIT_OBJECT_0 ? hrOK : hrFalse; }
/*---------------------------------------------------------------------------
CNodeQueryObject implementation ---------------------------------------------------------------------------*/
CNodeQueryObject::~CNodeQueryObject() { // Trace2("%X CNodeQueryObject::~CNodeQueryObject has %d objects\n",
// GetCurrentThreadId(), m_dataQueue.GetCount());
Assert(m_dataQueue.IsEmpty()); }
/*!--------------------------------------------------------------------------
CNodeQueryObject::AddToQueue - Author: KennT ---------------------------------------------------------------------------*/ BOOL CNodeQueryObject::AddToQueue(ITFSNode *pNode) { BOOL bSleep = FALSE;
Lock(); //::Sleep(1000);
LPQUEUEDATA pQData = new QUEUEDATA;
pQData->Type = QDATA_PNODE; pQData->Data = reinterpret_cast<LPARAM>(pNode);
BOOL bRes = NULL != m_dataQueue.AddTail(pQData); pNode->AddRef(); if (IsQueueFull()) { bSleep = TRUE; } Unlock();
// We have too much data, we've posted a notification to the node
// so we can go to sleep here.
// Note the danger here! The code calling has to be aware that a
// context switch will occur here (as well as not locking the data
// structures).
if (bSleep) { PostHaveData((LPARAM) (CNodeQueryObject *) this); ::Sleep(0); }
return bRes; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::AddToQueue - Author: KennT ---------------------------------------------------------------------------*/ BOOL CNodeQueryObject::AddToQueue(LPARAM Data, LPARAM Type) { BOOL bSleep = FALSE;
Lock(); //::Sleep(1000);
LPQUEUEDATA pQData = new QUEUEDATA;
pQData->Data = Data; pQData->Type = Type;
BOOL bRes = NULL != m_dataQueue.AddTail(pQData); if (IsQueueFull()) { bSleep = TRUE; } Unlock();
// We have too much data, we've posted a notification to the node
// so we can go to sleep here.
// Note the danger here! The code calling has to be aware that a
// context switch will occur here (as well as not locking the data
// structures).
if (bSleep) { PostHaveData((LPARAM) (CNodeQueryObject *) this); ::Sleep(0); }
return bRes; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::RemoveFromQueue - Author: KennT ---------------------------------------------------------------------------*/ LPQUEUEDATA CNodeQueryObject::RemoveFromQueue() { Lock(); LPQUEUEDATA pQD = m_dataQueue.IsEmpty() ? NULL : m_dataQueue.RemoveHead(); Unlock(); return pQD; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::IsQueueEmpty - Author: KennT ---------------------------------------------------------------------------*/ BOOL CNodeQueryObject::IsQueueEmpty() { Lock(); BOOL bRes = m_dataQueue.IsEmpty(); Unlock(); return bRes; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::IsQueueFull - Author: KennT ---------------------------------------------------------------------------*/ BOOL CNodeQueryObject::IsQueueFull() { Lock(); BOOL bRes = m_dataQueue.GetCount() >= m_nQueueCountMax; Unlock(); return bRes; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::OnThreadExit - Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP CNodeQueryObject::OnThreadExit() { BOOL fSomethingInQueue = FALSE; Lock(); fSomethingInQueue = (m_dataQueue.GetCount() > 0); Unlock();
// If there's anything in the queue, post
if (fSomethingInQueue) { PostHaveData((LPARAM) (CNodeQueryObject *) this); ::Sleep(0); } return hrOK; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::OnEventAbort - Author: KennT ---------------------------------------------------------------------------*/ STDMETHODIMP CNodeQueryObject::OnEventAbort() { // Trace2("%X CNodeQueryObject::OnEventAbort Q has %d nodes.\n", GetCurrentThreadId(), m_dataQueue.GetCount());
Lock(); while (!m_dataQueue.IsEmpty()) { LPQUEUEDATA pQD = m_dataQueue.RemoveHead(); if (pQD->Type == QDATA_PNODE) { SPITFSNode spNode; spNode = reinterpret_cast<ITFSNode *>(pQD->Data); } else { // give the query object a chance to clean up this data
OnEventAbort(pQD->Data, pQD->Type); }
delete pQD; }
Unlock(); return hrOK; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::OnCleanup DO NOT override this function. It provides a last cleanup mechanism for the query object. If you need notification that a thread is exiting, then override the OnThreadExit call. Author: EricDav ---------------------------------------------------------------------------*/ STDMETHODIMP CNodeQueryObject::DoCleanup() { PostMessageToComponentData(WM_HIDDENWND_INDEX_EXITING, (LPARAM) (CNodeQueryObject *) this);
m_spQuery.Release();
return hrOK; }
/*!--------------------------------------------------------------------------
CNodeQueryObject::PostMessageToComponentData Posts a message to the hidden window to get back on the main MMC thread. Author: KennT ---------------------------------------------------------------------------*/ BOOL CNodeQueryObject::PostMessageToComponentData(UINT uIndex, LPARAM lParam) { // Assert(m_spHandler);
// Assert(m_hHiddenWnd != NULL);
// Assert(::IsWindow(m_hHiddenWnd));
//$ Review: kennt, if the hidden window is bogus, should we still post
// to it? This could happen if our ComponentData went away but we were
// still in our loop, posting away (we haven't had a chance to get the
// abort signal).
// maybe something like
if (!m_hHiddenWnd) return 0; if (!::IsWindow(m_hHiddenWnd)) { // Trace2("%X The Hidden window is GONE, tried to send %08x.\n",
// GetCurrentThreadId(), m_uMsgBase+uIndex);
m_hHiddenWnd = NULL; return 0; } //Trace2("%X CBackgroundThread::PostMessageToComponentData(%08x)\n", GetCurrentThreadId(), m_uMsgBase+uIndex);
if (!m_spHandler) { // Trace0("PostMessageToCompData - m_spHandler == NULL, NOT posting a message\n");
return 0; }
return ::PostMessage(m_hHiddenWnd, m_uMsgBase + uIndex, (WPARAM)(ITFSThreadHandler *)m_spHandler, lParam); }
/*---------------------------------------------------------------------------
CNodeTimerQueryObject implementation ---------------------------------------------------------------------------*/ HRESULT CNodeTimerQueryObject::Execute() {
while (WaitForSingleObjectEx(m_hEventAbort, GetTimerInterval(), FALSE) != WAIT_OBJECT_0) { // we timed out. Post a message to the ComponentData...
AddToQueue(NULL, QDATA_TIMER); }
// Trace0("CNodeTimerQueryObject::Execute - got abort event, exiting.\n");
return hrFalse; }
|