mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
690 lines
17 KiB
690 lines
17 KiB
//
|
|
// MODULE: ThreadPool.CPP
|
|
//
|
|
// PURPOSE: Fully implement classes for high level of pool thread activity
|
|
//
|
|
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
|
|
//
|
|
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
|
|
//
|
|
// AUTHOR: Joe Mabel, based on earlier (8-2-96) work by Roman Mach
|
|
//
|
|
// ORIGINAL DATE: 9/23/98
|
|
//
|
|
// NOTES:
|
|
// 1.
|
|
//
|
|
// Version Date By Comments
|
|
//--------------------------------------------------------------------
|
|
// V0.1 - RM Original
|
|
// V3.0 9/23/98 JM better encapsulation & some chages to algorithm
|
|
//
|
|
|
|
#pragma warning(disable:4786)
|
|
|
|
#include "stdafx.h"
|
|
#include "ThreadPool.h"
|
|
#include "event.h"
|
|
#include "apgtscls.h"
|
|
#include "baseexception.h"
|
|
#include "CharConv.h"
|
|
#include "apgtsMFC.h"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// CThreadPool::CThreadControl
|
|
// POOL/WORKING THREAD
|
|
//////////////////////////////////////////////////////////////////////
|
|
CThreadPool::CThreadControl::CThreadControl(CSniffConnector* pSniffConnector) :
|
|
m_hThread (NULL),
|
|
m_hevDone (NULL),
|
|
m_hMutex (NULL),
|
|
m_bExit (false),
|
|
m_pPoolQueue (NULL),
|
|
m_timeCreated(0),
|
|
m_time (0),
|
|
m_bWorking (false),
|
|
m_bFailed (false),
|
|
m_strBrowser( _T("") ),
|
|
m_strClientIP( _T("") ),
|
|
m_pContext( NULL ),
|
|
m_pSniffConnector(pSniffConnector)
|
|
{
|
|
time(&m_timeCreated);
|
|
}
|
|
|
|
CThreadPool::CThreadControl::~CThreadControl()
|
|
{
|
|
if (m_hThread)
|
|
::CloseHandle(m_hThread);
|
|
if (m_hevDone)
|
|
::CloseHandle(m_hevDone);
|
|
if (m_hMutex)
|
|
::CloseHandle(m_hMutex);
|
|
}
|
|
|
|
// create a "pool" thread to handle user requests, one request at a time per thread
|
|
// returns error code; 0 if OK
|
|
// NOTE: This function throws exceptions so the caller should be catching exceptions
|
|
// rather than checking return values.
|
|
DWORD CThreadPool::CThreadControl::Initialize(CPoolQueue * pPoolQueue)
|
|
{
|
|
DWORD dwThreadID;
|
|
|
|
CString strErr;
|
|
CString strErrNum;
|
|
|
|
m_pPoolQueue = pPoolQueue;
|
|
|
|
m_hevDone= NULL;
|
|
m_hMutex= NULL;
|
|
m_hThread= NULL;
|
|
try
|
|
{
|
|
m_hevDone = ::CreateEvent(NULL, true /* manual reset*/, false /* init non-signaled*/, NULL);
|
|
if (!m_hevDone)
|
|
{
|
|
strErrNum.Format(_T("%d"), ::GetLastError());
|
|
strErr = _T( "Failure creating hevDone(GetLastError=" );
|
|
strErr += strErrNum;
|
|
strErr += _T( ")" );
|
|
|
|
throw CGeneralException( __FILE__, __LINE__,
|
|
strErr ,
|
|
EV_GTS_ERROR_THREAD );
|
|
}
|
|
|
|
m_hMutex = ::CreateMutex(NULL, false, NULL);
|
|
if (!m_hMutex)
|
|
{
|
|
strErrNum.Format(_T("%d"), ::GetLastError());
|
|
strErr = _T( "Failure creating hMutex (GetLastError=" );
|
|
strErr += strErrNum;
|
|
strErr += _T( ")" );
|
|
|
|
throw CGeneralException( __FILE__, __LINE__,
|
|
strErr ,
|
|
EV_GTS_ERROR_THREAD );
|
|
}
|
|
|
|
// create the thread
|
|
// Note although the destructor has a corresponding ::CloseHandle(m_hThread),
|
|
// it's probably not needed. However, it should be harmless: we don't tear down
|
|
// this object until after the thread has exited.
|
|
// That is because the thread goes out of existence on the implicit
|
|
// ::ExitThread() when PoolTask returns. See documentation of
|
|
// ::CreateThread for further details JM 10/22/98
|
|
m_hThread = ::CreateThread( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)PoolTask,
|
|
this,
|
|
0,
|
|
&dwThreadID);
|
|
|
|
if (!m_hThread)
|
|
{
|
|
strErrNum.Format(_T("%d"), ::GetLastError());
|
|
strErr = _T( "Failure creating hThread (GetLastError=" );
|
|
strErr += strErrNum;
|
|
strErr += _T( ")" );
|
|
|
|
throw CGeneralException( __FILE__, __LINE__,
|
|
strErr ,
|
|
EV_GTS_ERROR_THREAD );
|
|
}
|
|
}
|
|
catch (CGeneralException&)
|
|
{
|
|
// Clean up any open handles.
|
|
if (m_hevDone)
|
|
{
|
|
::CloseHandle(m_hevDone);
|
|
m_hevDone = NULL;
|
|
}
|
|
|
|
if (m_hMutex)
|
|
{
|
|
::CloseHandle(m_hMutex);
|
|
m_hMutex = NULL;
|
|
}
|
|
|
|
// Rethrow the exception.
|
|
throw;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CThreadPool::CThreadControl::Lock()
|
|
{
|
|
::WaitForSingleObject(m_hMutex, INFINITE);
|
|
}
|
|
|
|
void CThreadPool::CThreadControl::Unlock()
|
|
{
|
|
::ReleaseMutex(m_hMutex);
|
|
}
|
|
|
|
time_t CThreadPool::CThreadControl::GetTimeCreated() const
|
|
{
|
|
return m_timeCreated;
|
|
}
|
|
|
|
// OUTPUT status
|
|
void CThreadPool::CThreadControl::WorkingStatus(CPoolThreadStatus & status)
|
|
{
|
|
Lock();
|
|
status.m_timeCreated = m_timeCreated;
|
|
time_t timeNow;
|
|
status.m_bWorking = m_bWorking;
|
|
status.m_bFailed = m_bFailed;
|
|
time(&timeNow);
|
|
status.m_seconds = timeNow - (m_time ? m_time : m_timeCreated);
|
|
if (m_pContext)
|
|
status.m_strTopic = m_pContext->RetCurrentTopic();
|
|
status.m_strBrowser= m_strBrowser.Get();
|
|
status.m_strClientIP= m_strClientIP.Get();
|
|
Unlock();
|
|
}
|
|
|
|
// This should only be called as a result of an operator request to kill the thread.
|
|
// This is not the normal way to stop a thread.
|
|
// INPUT milliseconds - how long to wait for normal exit before a TerminateThread
|
|
// NOTE: Because this Kill function gets a lock, it is very important that no function
|
|
// ever hold this lock more than briefly.
|
|
void CThreadPool::CThreadControl::Kill(DWORD milliseconds)
|
|
{
|
|
Lock();
|
|
m_bExit = true;
|
|
Unlock();
|
|
WaitForThreadToFinish(milliseconds);
|
|
}
|
|
|
|
// After a pool task thread has been signaled to finish, this is how main thread waits for it
|
|
// to finish.
|
|
// returns true if terminates OK.
|
|
bool CThreadPool::CThreadControl::WaitForThreadToFinish(DWORD milliseconds)
|
|
{
|
|
bool bTermOK = true;
|
|
if (m_hevDone != NULL)
|
|
{
|
|
DWORD dwStatus = ::WaitForSingleObject(m_hevDone, milliseconds);
|
|
|
|
// terminate thread as last resort if it didn't exit properly
|
|
// this may cause memory leak, but shouldn't normally happen
|
|
// then close thread handle
|
|
if (dwStatus != WAIT_OBJECT_0)
|
|
{
|
|
// We ignore the return of ::TerminateThread(). If we got here at all, there
|
|
// was a problem witht th thread terminating. We don't care about distinguishing
|
|
// how severe a problem.
|
|
::TerminateThread(m_hThread,0);
|
|
bTermOK = false;
|
|
}
|
|
}
|
|
return bTermOK;
|
|
}
|
|
|
|
// To be called on PoolTask thread
|
|
// Return true if this initiates shutdown, false otherwise.
|
|
// This is what handles healthy HTTP requests (many errors already filtered out before we
|
|
// get here.)
|
|
bool CThreadPool::CThreadControl::ProcessRequest()
|
|
{
|
|
WORK_QUEUE_ITEM * pwqi;
|
|
bool bShutdown = false;
|
|
|
|
pwqi = m_pPoolQueue->GetWorkItem();
|
|
|
|
if ( !pwqi )
|
|
{
|
|
// no task. We shouldn't have been awakened.
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
_T(""),
|
|
_T(""),
|
|
EV_GTS_ERROR_NO_QUEUE_ITEM );
|
|
}
|
|
|
|
if (pwqi->pECB != NULL)
|
|
{
|
|
|
|
// a normal user request
|
|
|
|
// set privileges, etc. to those of a particular user
|
|
if (pwqi->hImpersonationToken)
|
|
::ImpersonateLoggedOnUser( pwqi->hImpersonationToken );
|
|
|
|
|
|
try
|
|
{
|
|
CString strBrowser;
|
|
CString strClientIP;
|
|
|
|
// Acquire the browser and IP address for status pages.
|
|
APGTS_nmspace::GetServerVariable( pwqi->pECB, "HTTP_USER_AGENT", strBrowser );
|
|
APGTS_nmspace::GetServerVariable( pwqi->pECB, "REMOTE_ADDR", strClientIP );
|
|
m_strBrowser.Set( strBrowser );
|
|
m_strClientIP.Set( strClientIP );
|
|
|
|
m_pContext = new APGTSContext( pwqi->pECB,
|
|
pwqi->pConf,
|
|
pwqi->pLog,
|
|
&pwqi->GTSStat,
|
|
m_pSniffConnector);
|
|
|
|
m_pContext->ProcessQuery();
|
|
|
|
// Release the context and set the point to null.
|
|
Lock();
|
|
delete m_pContext;
|
|
m_pContext= NULL;
|
|
Unlock();
|
|
|
|
// Clear the browser and IP address as this request is over.
|
|
m_strBrowser.Set( _T("") );
|
|
m_strClientIP.Set( _T("") );
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
// A memory allocation failure occurred during processing of query, log it.
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
_T(""), _T(""), EV_GTS_CANT_ALLOC );
|
|
}
|
|
catch (...)
|
|
{
|
|
// Catch any other exception thrown.
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
_T(""), _T(""),
|
|
EV_GTS_GEN_EXCEPTION );
|
|
}
|
|
|
|
::RevertToSelf();
|
|
|
|
// Terminate HTTP request
|
|
pwqi->pECB->ServerSupportFunction( HSE_REQ_DONE_WITH_SESSION,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
::CloseHandle( pwqi->hImpersonationToken );
|
|
}
|
|
|
|
|
|
if (pwqi->pECB)
|
|
delete pwqi->pECB;
|
|
else
|
|
// exit thread if null (we're shutting down)
|
|
bShutdown = true;
|
|
|
|
delete pwqi;
|
|
|
|
return bShutdown;
|
|
}
|
|
|
|
// To be called on PoolTask thread
|
|
bool CThreadPool::CThreadControl::Exit()
|
|
{
|
|
Lock();
|
|
bool bExit = m_bExit;
|
|
Unlock();
|
|
return bExit;
|
|
}
|
|
|
|
|
|
// To be called on PoolTask thread
|
|
// Main loop of a worker thread.
|
|
void CThreadPool::CThreadControl::PoolTaskLoop()
|
|
{
|
|
DWORD res;
|
|
bool bBad = false;
|
|
|
|
while ( !Exit() )
|
|
{
|
|
res = m_pPoolQueue->WaitForWork();
|
|
|
|
if ( res == WAIT_OBJECT_0 )
|
|
{
|
|
bBad = false;
|
|
|
|
Lock();
|
|
m_bWorking = true;
|
|
time(&m_time);
|
|
Unlock();
|
|
|
|
bool bExit = ProcessRequest();
|
|
Lock();
|
|
m_bExit = bExit;
|
|
Unlock();
|
|
m_pPoolQueue->DecrementWorkItems();
|
|
|
|
Lock();
|
|
m_bWorking = false;
|
|
time(&m_time);
|
|
Unlock();
|
|
}
|
|
else
|
|
{
|
|
// utterly unexpected event, like a WAIT_FAILED.
|
|
// There's no obvious way to recover from this sort of thing. Fortunately,
|
|
// we've never seen it happen. Obviously we want to log to the event log.
|
|
// Our variable bBad is a way of deciding that if this happens twice
|
|
// in a row, this thread will just exit and give up totally. ,
|
|
// If we ever see this in a real live system, it's
|
|
// time to give this issue some thought.
|
|
CString str;
|
|
|
|
str.Format(_T("%d/%d"), res, GetLastError());
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
str,
|
|
_T(""),
|
|
EV_GTS_ERROR_UNEXPECTED_WT );
|
|
|
|
if (bBad)
|
|
{
|
|
m_bFailed = true;
|
|
break; // out of while loop & implicitly out of thread.
|
|
}
|
|
else
|
|
bBad = true;
|
|
}
|
|
}
|
|
|
|
// signal shutdown code that we are finished
|
|
::SetEvent(m_hevDone);
|
|
}
|
|
|
|
// Main routine of a worker thread.
|
|
// INPUT lpParams
|
|
// Always returns 0.
|
|
/* static */ UINT WINAPI CThreadPool::CThreadControl::PoolTask( LPVOID lpParams )
|
|
{
|
|
CThreadControl * pThreadControl;
|
|
|
|
#ifdef LOCAL_TROUBLESHOOTER
|
|
if (RUNNING_FREE_THREADED())
|
|
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (RUNNING_APARTMENT_THREADED())
|
|
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
#endif
|
|
|
|
pThreadControl = (CThreadControl *)lpParams;
|
|
|
|
pThreadControl->PoolTaskLoop();
|
|
|
|
#ifdef LOCAL_TROUBLESHOOTER
|
|
if (RUNNING_FREE_THREADED() || RUNNING_APARTMENT_THREADED())
|
|
::CoUninitialize();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// CThreadPool
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CThreadPool::CThreadPool(CPoolQueue * pPoolQueue, CSniffConnector* pSniffConnector) :
|
|
m_dwErr(0),
|
|
m_ppThreadCtl(NULL),
|
|
m_dwWorkingThreadCount(0),
|
|
m_pPoolQueue(pPoolQueue),
|
|
m_pSniffConnector(pSniffConnector)
|
|
{
|
|
}
|
|
|
|
CThreadPool::~CThreadPool()
|
|
{
|
|
DestroyThreads();
|
|
|
|
if (m_ppThreadCtl)
|
|
{
|
|
for ( DWORD i = 0; i < m_dwWorkingThreadCount; i++ )
|
|
if (m_ppThreadCtl[i])
|
|
delete m_ppThreadCtl[i];
|
|
|
|
delete [] m_ppThreadCtl;
|
|
}
|
|
}
|
|
|
|
// get any error during construction
|
|
DWORD CThreadPool::GetStatus() const
|
|
{
|
|
return m_dwErr;
|
|
}
|
|
|
|
DWORD CThreadPool::GetWorkingThreadCount() const
|
|
{
|
|
return m_dwWorkingThreadCount;
|
|
}
|
|
|
|
//
|
|
// Call only from destructor
|
|
void CThreadPool::DestroyThreads()
|
|
{
|
|
int BadTerm = 0;
|
|
bool bFirst = true;
|
|
DWORD i;
|
|
|
|
// APGTSExtension should have already signaled the threads to quit.
|
|
// >>>(ignore for V3.0) Doing that in APGTSExtension is lousy encapsulation, but
|
|
// so far we don't see a clean way to do this.
|
|
// Wait for them all to terminate unless we had a problem.
|
|
// Because this is called from the dll's process detach, we can't
|
|
// signal on thread termination, just when threads have exited their
|
|
// infinite while loops
|
|
|
|
if (m_dwWorkingThreadCount && m_ppThreadCtl)
|
|
{
|
|
// We will wait longer for the first thread: 10 seconds for processing to finish.
|
|
// After that, we clip right along, since this has also been time for all the
|
|
// other threads to finish.
|
|
for ( i = 0; i < m_dwWorkingThreadCount; i++ )
|
|
{
|
|
if ( m_ppThreadCtl[i] )
|
|
{
|
|
if ( ! m_ppThreadCtl[i]->WaitForThreadToFinish((bFirst) ? 20000 : 100) )
|
|
++BadTerm;
|
|
|
|
bFirst = false;
|
|
}
|
|
}
|
|
|
|
if (BadTerm != 0)
|
|
{
|
|
CString str;
|
|
str.Format(_T("%d"), BadTerm);
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
str,
|
|
_T(""),
|
|
EV_GTS_USER_THRD_KILL );
|
|
}
|
|
}
|
|
}
|
|
|
|
// create the "pool" threads which handle user requests, one request at a time per thread
|
|
// if there are less than dwDesiredThreadCount existing threads, expand the thread pool
|
|
// to that size.
|
|
// (We cannot shrink the thread pool while we are running).
|
|
void CThreadPool::ExpandPool(DWORD dwDesiredThreadCount)
|
|
{
|
|
CString strErr;
|
|
|
|
if (dwDesiredThreadCount > m_dwWorkingThreadCount)
|
|
{
|
|
CThreadControl **ppThreadCtl = NULL;
|
|
const DWORD dwOldCount = m_dwWorkingThreadCount;
|
|
bool bExceptionThrown = false; // Flag used in cleanup.
|
|
|
|
// Attempt to allocate additional threads.
|
|
try
|
|
{
|
|
// Allocate new thread block.
|
|
ppThreadCtl = new CThreadControl* [dwDesiredThreadCount];
|
|
//[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
|
|
if(!ppThreadCtl)
|
|
{
|
|
throw bad_alloc();
|
|
}
|
|
|
|
DWORD i;
|
|
// Initialize before adding threads
|
|
for (i = 0; i < dwDesiredThreadCount; i++)
|
|
ppThreadCtl[i] = NULL;
|
|
|
|
// Transfer any existing threads.
|
|
for (i = 0; i < dwOldCount; i++)
|
|
ppThreadCtl[i] = m_ppThreadCtl[i];
|
|
|
|
// Allocate additional threads.
|
|
for (i = dwOldCount; i < dwDesiredThreadCount; i++)
|
|
{
|
|
ppThreadCtl[i] = new CThreadControl(m_pSniffConnector);
|
|
//[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
|
|
if(!ppThreadCtl[i])
|
|
{
|
|
throw bad_alloc();
|
|
}
|
|
|
|
// This function may throw exceptions of type CGeneralException.
|
|
m_dwErr = ppThreadCtl[i]->Initialize(m_pPoolQueue);
|
|
|
|
m_dwWorkingThreadCount++;
|
|
}
|
|
}
|
|
catch (CGeneralException& x)
|
|
{
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
x.GetErrorMsg(), _T("General exception"),
|
|
x.GetErrorCode() );
|
|
bExceptionThrown= true;
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
// Note memory failure in event log.
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
_T(""), _T(""), EV_GTS_CANT_ALLOC );
|
|
bExceptionThrown= true;
|
|
}
|
|
|
|
if ((bExceptionThrown) && (dwOldCount))
|
|
{
|
|
// Restore previous settings.
|
|
// Clean up any allocated memory and reset the working thread count.
|
|
for (DWORD i = dwOldCount; i < dwDesiredThreadCount; i++)
|
|
{
|
|
if (ppThreadCtl[i])
|
|
delete ppThreadCtl[i];
|
|
}
|
|
if (ppThreadCtl)
|
|
delete [] ppThreadCtl;
|
|
m_dwWorkingThreadCount= dwOldCount;
|
|
}
|
|
else if (ppThreadCtl)
|
|
{
|
|
// Move thread block to member variable.
|
|
CThreadControl **pp = m_ppThreadCtl;
|
|
m_ppThreadCtl = ppThreadCtl;
|
|
|
|
// Release any previous thread block.
|
|
if (pp)
|
|
delete[] pp;
|
|
}
|
|
else
|
|
{
|
|
// this is a very unlikely situation, but it would mean we have no pool
|
|
// threads. We don't want to terminate the program (it's possible that
|
|
// we want to run in support of status queries).
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
_T(""), _T(""), EV_GTS_ERROR_NOPOOLTHREADS );
|
|
}
|
|
}
|
|
}
|
|
|
|
// input i is thread index.
|
|
bool CThreadPool::ReinitializeThread(DWORD i)
|
|
{
|
|
if (i <m_dwWorkingThreadCount && m_ppThreadCtl && m_ppThreadCtl[i])
|
|
{
|
|
m_ppThreadCtl[i]->Kill(2000L); // 2 seconds to exit normally
|
|
|
|
try
|
|
{
|
|
delete m_ppThreadCtl[i];
|
|
m_ppThreadCtl[i] = new CThreadControl(m_pSniffConnector);
|
|
|
|
// This function may throw exceptions of type CGeneralException.
|
|
m_dwErr = m_ppThreadCtl[i]->Initialize(m_pPoolQueue);
|
|
}
|
|
catch (CGeneralException& x)
|
|
{
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
x.GetErrorMsg(), _T("General exception"),
|
|
x.GetErrorCode() );
|
|
|
|
// Initialization has failed, delete the newly allocated thread.
|
|
if (m_ppThreadCtl[i])
|
|
delete m_ppThreadCtl[i];
|
|
}
|
|
catch (bad_alloc&)
|
|
{
|
|
// A memory allocation failure occurred during processing of query, log it.
|
|
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
|
|
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
|
|
SrcLoc.GetSrcFileLineStr(),
|
|
_T(""), _T(""), EV_GTS_CANT_ALLOC );
|
|
|
|
// Set the thread to a known state.
|
|
m_ppThreadCtl[i]= NULL;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// Reinitialize any threads that have been "working" more than 10 seconds on a single request
|
|
void CThreadPool::ReinitializeStuckThreads()
|
|
{
|
|
if (!m_ppThreadCtl)
|
|
return;
|
|
|
|
for (DWORD i=0; i<m_dwWorkingThreadCount;i++)
|
|
{
|
|
if (m_ppThreadCtl[i])
|
|
{
|
|
CPoolThreadStatus status;
|
|
m_ppThreadCtl[i]->WorkingStatus(status);
|
|
if ( status.m_bFailed || (status.m_bWorking && status.m_seconds > 10) )
|
|
ReinitializeThread(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// input i is thread index.
|
|
bool CThreadPool::ThreadStatus(DWORD i, CPoolThreadStatus &status)
|
|
{
|
|
if (i <m_dwWorkingThreadCount && m_ppThreadCtl && m_ppThreadCtl[i])
|
|
{
|
|
m_ppThreadCtl[i]->WorkingStatus(status);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|