/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: thread.cpp Abstract: This module contains implementation of MSP thread management. Author: Mu Han (muhan) 1-11-1998 --*/ #include "stdafx.h" #include #include "rndcommc.h" #include "rndutil.h" #include "thread.h" #include "rendp.h" CRendThread g_RendThread; extern "C" DWORD WINAPI gfThreadProc(LPVOID p) { return ((CRendThread *)p)->ThreadProc(); } CRendThread::~CRendThread() { // all code moved from here to CRendThread::Shutdown } // // Since this class is instantiated above as a global object, and // _Module.Term() is called in DLL_PROCESS_DETACH before this // global object is destroyed, we must release all our COM references // in DLL_PROCESS_DETACH before the _Module.Term(). This is because // the _Module.Term() deletes a critical section that must be // acquired whenever a COM object is released. Therefore we have this // shutdown method, which we call explicitly in the DLL_PROCESS_DETACH // handling code before we call _Module.Term(). // void CRendThread::Shutdown(void) { CLock Lock(m_lock); if (m_hThread) { Stop(); } for (DWORD i = 0; i < m_Directories.size(); i ++) { m_Directories[i]->Release(); } if (m_hEvents[EVENT_TIMER]) { CloseHandle(m_hEvents[EVENT_TIMER]); m_hEvents[EVENT_TIMER] = NULL; } if (m_hEvents[EVENT_STOP]) { CloseHandle(m_hEvents[EVENT_STOP]); m_hEvents[EVENT_STOP] = NULL; } } HRESULT CRendThread::Start() /*++ Routine Description: Create the thread. Arguments: Return Value: HRESULT. --*/ { HRESULT hr = E_FAIL; while (TRUE) // break if fail, for clean up purpose. { if ((m_hEvents[EVENT_STOP] = ::CreateEvent( NULL, FALSE, // flag for manual-reset event FALSE, // initial state is not set. NULL // No name. )) == NULL) { LOG((MSP_ERROR, ("Can't create the signal event"))); hr = HRESULT_FROM_ERROR_CODE(GetLastError()); break; } if ((m_hEvents[EVENT_TIMER] = ::CreateWaitableTimer( NULL, // lpTimerAttributes FALSE, // bManualReset NULL // lpTimerName )) == NULL) { LOG((MSP_ERROR, ("Can't create timer. Error: %d"), GetLastError())); hr = HRESULT_FROM_ERROR_CODE(GetLastError()); break; } DWORD dwThreadID; m_hThread = ::CreateThread(NULL, 0, gfThreadProc, this, 0, &dwThreadID); if (m_hThread == NULL) { LOG((MSP_ERROR, ("Can't create thread. Error: %d"), GetLastError())); hr = HRESULT_FROM_ERROR_CODE(GetLastError()); break; } return S_OK; } if (m_hEvents[EVENT_TIMER]) { CloseHandle(m_hEvents[EVENT_TIMER]); m_hEvents[EVENT_TIMER] = NULL; } if (m_hEvents[EVENT_STOP]) { CloseHandle(m_hEvents[EVENT_STOP]); m_hEvents[EVENT_STOP] = NULL; } return hr; } HRESULT CRendThread::Stop() /*++ Routine Description: Stop the thread. Arguments: Return Value: HRESULT. --*/ { if (!StopThread()) { LOG((MSP_ERROR, ("can't stop the thread. %d"), GetLastError())); return HRESULT_FROM_ERROR_CODE(GetLastError()); } // Wait until the thread stops if (::WaitForSingleObject(m_hThread, INFINITE) != WAIT_OBJECT_0) { LOG((MSP_ERROR, ("waiting for the thread to stop, %d"), GetLastError())); return HRESULT_FROM_ERROR_CODE(GetLastError()); } else { ::CloseHandle(m_hThread); m_hThread = NULL; } return S_OK; } HRESULT CRendThread::AddDirectory(ITDirectory *pITDirectory) /*++ Routine Description: Add a new directory to the list. The directory will be notified to update its objects when the timer goes out. Arguments: pITDirectory - A pointer to a ITDirectory Interface. Return Value: --*/ { ITDynamicDirectory * pDir; HRESULT hr = pITDirectory->QueryInterface( IID_ITDynamicDirectory, (void **)&pDir ); if (FAILED(hr)) { return hr; } CLock Lock(m_lock); if (m_hThread == NULL) { hr = Start(); if (FAILED(hr)) { pDir->Release(); return hr; } } for (DWORD i = 0; i < m_Directories.size(); i ++) { if (m_Directories[i] == pDir) { // // It was already in the list, so don't keep a second reference // to it. // pDir->Release(); return S_OK; } } if (!m_Directories.add(pDir)) { pDir->Release(); return E_OUTOFMEMORY; } // // We have successfully added the directory to the list and // kept a reference to it. It is released on Remove or on destruction of // the thread class. // return S_OK; } HRESULT CRendThread::RemoveDirectory(ITDirectory *pITDirectory) /*++ Routine Description: Remove a directory from the list. Arguments: pITDirectory - A pointer to a ITDirectory Interface. Return Value: --*/ { CComPtr pDir; HRESULT hr = pITDirectory->QueryInterface( IID_ITDynamicDirectory, (void **)&pDir ); if (FAILED(hr)) { return hr; } CLock Lock(m_lock); if (m_hThread == NULL) { hr = Start(); if (FAILED(hr)) { return hr; } } for (DWORD i = 0; i < m_Directories.size(); i ++) { if (m_Directories[i] == pDir) { // // We kept a reference to the directory when we added it for // autorefresh. Release it now. // m_Directories[i]->Release(); // // Copy the last array element to the removed element and shrink // the array by one. Can do this because order does not matter. // m_Directories[i] = m_Directories[m_Directories.size() - 1]; m_Directories.shrink(); return S_OK; } } return S_OK; } VOID CRendThread::UpdateDirectories() /*++ Routine Description: Notify all the directories to update the objects. Arguments: Return Value: --*/ { if (m_lock.TryLock()) { for (DWORD i = 0; i < m_Directories.size(); i ++) { m_Directories[i]->Update(TIMER_PERIOD); } m_lock.Unlock(); } } HRESULT CRendThread::ThreadProc() /*++ Routine Description: the main loop of this thread. Arguments: Return Value: HRESULT. --*/ { HRESULT hr; LARGE_INTEGER liDueTime; const long UNIT_IN_SECOND = (long)1e7; // initialize update timer due time, negative mean relative. liDueTime.QuadPart = Int32x32To64(-(long)TIMER_PERIOD, UNIT_IN_SECOND); if (!SetWaitableTimer( m_hEvents[EVENT_TIMER], // hTimer &liDueTime, // DueTime in 100 nanonsecond units (long)(TIMER_PERIOD * 1e3), // miliseconds NULL, // pfnCompletionRoutine NULL, // lpArgToCompletionRoutine FALSE // fResume )) { LOG((MSP_ERROR, ("Can't enable timer. Error: %d"), GetLastError())); return HRESULT_FROM_ERROR_CODE(GetLastError()); } if (FAILED(hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED))) { LOG((MSP_ERROR, ("CRendThread:ConinitialzeEx failed:%x"), hr)); return hr; } LOG((MSP_TRACE, ("thread proc started"))); BOOL bExitFlag = FALSE; while (!bExitFlag) { DWORD dwResult = ::WaitForMultipleObjects( NUM_EVENTS, // wait for all the events. m_hEvents, FALSE, // return if any of them is set INFINITE // wait forever. ); switch (dwResult) { case WAIT_OBJECT_0 + EVENT_STOP: bExitFlag = TRUE; break; case WAIT_OBJECT_0 + EVENT_TIMER: UpdateDirectories(); break; } } ::CoUninitialize(); return S_OK; }