windows-nt/Source/XPSP1/NT/net/tapi/skywalker/rend/thread.cpp
2020-09-26 16:20:57 +08:00

410 lines
8.3 KiB
C++

/*++
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 <objbase.h>
#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<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))
{
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;
}