Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

971 lines
29 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: threads.cxx
//
// Contents: Classes that implement thread pool for link tracking.
//
// Classes: CGlobalThreadSet -- all threads in all pools
// CThreadPool -- spare threads in a pool
// CTask -- object with data associated with thread
//
//
// Functions: StartTaskThread -- function called by CreateThread when
// creating the thread for CTask
//
//
// History: 1-Mar-95 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#define NO_INCLUDE_UNION 1
#include "shellprv.h"
#pragma hdrstop
#include "bldtrack.h"
#ifdef ENABLE_TRACK
extern "C" HRESULT TimeoutExpired( DWORD );
#include "threads.hxx"
//--------------------------------------------------------------------------
// Debugging stuff.
//--------------------------------------------------------------------------
#ifdef DBGTHREADS
extern CRITICAL_SECTION g_cs;
extern CGlobalThreadSet g_ts;
int __cdecl myprintf(const char *pszArg, ...)
{
DWORD dw;
EnterCriticalSection(&g_cs);
va_list va;
char buf[256];
va_start (va, pszArg);
int r =vsprintf(buf, pszArg, va);
va_end(va);
LeaveCriticalSection(&g_cs);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &dw, NULL);
return r;
}
VOID _ThdAssert(char *psz, char *pszFile, int line)
{
myprintf("%08X, Assertion %s failed in %s at line %d\n",
GetCurrentThreadId(), psz, pszFile, line);
g_ts.SuspendAll();
Sleep(INFINITE);
}
DWORD
_ThdWaitForSingleObject(char *psz, HANDLE h, DWORD dwTimeout)
{
ThdAssert(dwTimeout == INFINITE || dwTimeout == 0);
while (TRUE)
{
DWORD dw = WaitForSingleObject(h, 5000);
if (dw == WAIT_TIMEOUT && dwTimeout != 0)
{
myprintf("%08X, WaitForSingleObject timed out @ %s, "
"threads=%d, pools=%d\n",
GetCurrentThreadId(), psz, &g_cThreads, &g_cPools);
continue;
}
else
{
return dw;
}
}
}
DWORD
_ThdWaitForMultipleObjects(char *psz,
DWORD c,
CONST HANDLE *lph,
BOOL fWaitAll,
DWORD dwTimeout)
{
ThdAssert(dwTimeout == INFINITE);
while (TRUE)
{
DWORD dw = WaitForMultipleObjects(c, lph, fWaitAll, 5000);
if (dw == WAIT_TIMEOUT)
{
myprintf("%08X, WaitForMultipleObjects timed out @ %s, "
"threads=%d, pools=%d\n",
GetCurrentThreadId(), psz, &g_cThreads, &g_cPools);
continue;
}
else
{
return dw;
}
}
}
VOID
CGlobalThreadSet::SuspendAll()
{
ResumeThread(GetCurrentThread());
CTask *pCur = _pHeadTask;
while (pCur)
{
SuspendThread(pCur->GetHandle());
pCur = pCur->GetNext();
}
}
#endif
//+-------------------------------------------------------------------
//
// Member: CGlobalThreadSet::CGlobalThreadSet
//
// Synopsis: Initialize the object that keeps a reference to every
// CTask object.
//
// Arguments: [phr] -- set to E_OUTOFMEMORY if failed to init.
//
// Notes: All waits on the GTS_LIST mutex also include the
// CoUninitialize abort event (used by KillAllTasks)
// so that we can force all threads to stop creating
// more threads so we can wait for all the extant threads
// to exit and then unload the dll.
//
//--------------------------------------------------------------------
CGlobalThreadSet::CGlobalThreadSet(HRESULT *phr)
{
_pHeadTask = NULL;
_ahMultiWait[GTS_LIST] = CreateMutex(NULL, FALSE, NULL);
_ahMultiWait[GTS_COUNINITIALIZE_ABORT_EVENT] = CreateEvent(NULL,
TRUE,
FALSE,
NULL);
if (_ahMultiWait[GTS_COUNINITIALIZE_ABORT_EVENT] == NULL ||
_ahMultiWait[GTS_LIST] == NULL)
{
*phr = E_OUTOFMEMORY;
}
}
//+-------------------------------------------------------------------
//
// Member: CGlobalThreadSet::~CGlobalThreadSet
//
// Synopsis: Free resources used by this object.
//
// Notes: Object may not have constructed properly so we cleanup
// carefully.
//
//--------------------------------------------------------------------
CGlobalThreadSet::~CGlobalThreadSet()
{
if (_ahMultiWait[GTS_LIST] != NULL)
ThdVerify(CloseHandle(_ahMultiWait[GTS_LIST]));
if (_ahMultiWait[GTS_COUNINITIALIZE_ABORT_EVENT] != NULL)
ThdVerify(CloseHandle(_ahMultiWait[GTS_COUNINITIALIZE_ABORT_EVENT]));
}
//+-------------------------------------------------------------------
//
// Member: CGlobalThreadSet::CreateAndRecordThread
//
// Synopsis: Create a thread and insert into the global thread list.
//
// Arguments: [ppTask] -- where to put the pointer to the new CTask
// [pPool] -- the pool that will be ref counted from
// the new task.
//
//
// Returns: MK_E_EXCEEDEDDEADLINE if the abort event is set.
// E_OUTOFMEMORY
//
// Notes: Performs the insertion into the global list under the
// protection of the _ahMultiWait[GTS_LIST] mutex
//
//--------------------------------------------------------------------
HRESULT
CGlobalThreadSet::CreateAndRecordThread(CTask **ppTask, CThreadPool *pPool)
{
HRESULT hr = E_OUTOFMEMORY;
DWORD index = ThdWaitForMultipleObjects("CreateAndRecordThread:GTS_LIST",
2,
_ahMultiWait,
FALSE,
INFINITE); // GTS_LIST, GTS_COUNINITIALIZE_ABORT_EVENT
if (index != WAIT_OBJECT_0 + GTS_LIST)
{
ThdAssert(index == WAIT_OBJECT_0 + GTS_COUNINITIALIZE_ABORT_EVENT);
//assert(not the thread calling bind to object)
ThdPrint("%08X, CreateAndRecordThread returning MK_E_EXCEEDEDDEADLINE\n",
GetCurrentThreadId());
return(MK_E_EXCEEDEDDEADLINE);
}
*ppTask = new CTask(&hr);
if (hr == S_OK)
{
(*ppTask)->SetNext(_pHeadTask);
(*ppTask)->SetPool(pPool);
_pHeadTask = *ppTask;
}
else
{
delete *ppTask;
*ppTask = NULL;
}
ThdPrint("%08X, CreateAndRecordThread created pTask=%08X\n",
GetCurrentThreadId(),
*ppTask);
ReleaseMutex(_ahMultiWait[GTS_LIST]);
return(hr);
}
//+-------------------------------------------------------------------
//
// Member: CGlobalThreadSet::KillAllTasks
//
// Synopsis: Ensure all threads are killed so we can safely unload the
// DLL that the thread code is executing in.
//
// Acquire the global thread list and walk it, aborting
// each task in the list and waiting until the thread
// handle is signalled.
//
// Notes: We acquire the global thread list mutex and once acquired
// we set the abort event which will prevent any further
// thread creations because the event is waited on whenever
// the global thread list mutex is waited on. This means
// that we can reliably kill all threads and KNOW they are
// really killed because their handles are signalled.
//
//--------------------------------------------------------------------
VOID
CGlobalThreadSet::KillAllTasks()
{
DWORD ret = ThdWaitForSingleObject("KillTasks:GTS_LIST",
_ahMultiWait[GTS_LIST],
INFINITE);
ThdAssert (ret == WAIT_OBJECT_0 + GTS_LIST);
ThdPrint("%08X, KillAllTasks setting COUNINITIALIZE_ABORT\n",
GetCurrentThreadId());
SetEvent(_ahMultiWait[GTS_COUNINITIALIZE_ABORT_EVENT]); // force threads awake
CTask *pCur = _pHeadTask;
while (pCur)
{
CTask *pNext = pCur->GetNext();
pCur->AbortThread(); // waits until the thread is signalled
delete pCur;
pCur = pNext;
}
ReleaseMutex(_ahMultiWait[GTS_LIST]);
}
//+-------------------------------------------------------------------
//
// Member: CGlobalThreadSet::KillTasks
//
// Synopsis: Kill a selected set of tasks and remove them from
// the global thread set. Wait on the thread handles
// to wait for their complete cessation of activity.
//
// Arguments: [ppTasks] -- an array of CTask pointers
// [cTasks] -- the count of CTask pointers
// [pThisTask] -- this thread's CTask object ... make
// sure that we don't wait for THIS threads
// handle otherwise we'll wait forever.
//
//
// Notes: Used by the pool to remove all the threads when it
// has been detected that they are all spare.
//
//--------------------------------------------------------------------
VOID
CGlobalThreadSet::KillTasks(CTask **ppTasks, int cTasks, CTask *pThisTask)
{
DWORD ret = ThdWaitForMultipleObjects("KillTasks:GTS_LIST",
2,
_ahMultiWait,
FALSE,
INFINITE); // fWaitAll=FALSE
if (ret == WAIT_OBJECT_0 + GTS_LIST)
{
int i;
// signal each per-thread wait for work mutex and
// set pThread->fAbort = TRUE
ThdPrint("%08X, KillTasks aborting %d threads\n",
GetCurrentThreadId(),
cTasks);
// spare thread list entries should be removed from the global list
for (i=0; i<cTasks; i++)
{
if (ppTasks[i] != pThisTask) // we don't want to wait on our
// own thread handle being signalled.
ppTasks[i]->AbortThread();
RemoveTask(ppTasks[i]);
delete ppTasks[i];
}
ReleaseMutex(_ahMultiWait[GTS_LIST]);
}
else
{
ThdAssert(ret == WAIT_OBJECT_0 + GTS_COUNINITIALIZE_ABORT_EVENT);
}
ThdPrint("%08X, KillTask: Thread Exit\n", GetCurrentThreadId());
ThdInterlockedDecrement(&g_cThreads);
ExitThread(0);
}
//+-------------------------------------------------------------------
//
// Member: CGlobalThreadSet::RemoveTask
//
// Synopsis: Remove the specified task from the linked list of the
// global thread set.
//
// Arguments: [pTask] -- the pointer to search for in the linked list.
//
// Algorithm: start pointing at the head
// while pointing at a valid task
// if its the one we want
// if its not at the head
// we must set previous to point to next
// else
// we must set head to point to next
//
//--------------------------------------------------------------------
VOID
CGlobalThreadSet::RemoveTask(CTask *pTask)
{
CTask *pCur = _pHeadTask;
CTask *pPrev = NULL;
while (pCur != NULL)
{
if (pCur == pTask)
{
if (pPrev != NULL)
{
pPrev->SetNext(pCur->GetNext());
}
else
{ // first time through
_pHeadTask = pCur->GetNext();
}
break;
}
pPrev = pCur;
pCur = pCur->GetNext();
}
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::CThreadPool
//
// Synopsis: Create the bare thread pool.
//
// Arguments: [cMaxThreads] -- the maximum number of threads allowed
// to be created
//
// [refAllThreads] -- reference to the CGlobalThreadSet
// that should keep track of all the
// created CTasks (threads)
//
// [dwTickCountDeadline] -- time at which the pool should
// force completion
//
// [phr] -- set to S_OK if successful, otherwise not
// initialized.
//
// Notes: _hEventComplete is initialized not signalled and
// is signalled when all threads in the pool are spare and
// will be killed, OR if one of the threads is successful
// in performing the command (i.e. it finds the object.)
//
// _hEventEnableSecondTierThreads is enabled by the
// user of the thread pool when he/she has created all
// tier 1 threads. (this feature could be redesigned to
// save the event.)
//
// _hSemNSpare is intended to be signalled if the thread
// pool contains at least one spare thread or if there
// is room for more threads in the pool
//
// _csSpare is a critical section which protects the counters
// state of the pool
//
// _cPoolThreads is a count of the number of threads so far
// created in the pool.
//
// _cRefs keeps the pool alive if there are any threads
// referring to it.
//
//--------------------------------------------------------------------
CThreadPool::CThreadPool( int cMaxThreads,
CGlobalThreadSet &refAllThreads,
DWORD dwTickCountDeadline,
HRESULT *phr) :
_refAllThreads(refAllThreads),
_dwTickCountDeadline(dwTickCountDeadline)
{
_hEventComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
_fEventComplete = FALSE;
_hEventEnableSecondTierThreads = CreateEvent(NULL, TRUE, FALSE, NULL);
_hSemNSpare = CreateSemaphore(NULL, 1, cMaxThreads, NULL);
InitializeCriticalSection( &_csSpare );
_pUiCommand = NULL;
// an improvement would be to use linked-lists!
_ppSpareTasks = (CTask**) LocalAlloc(LMEM_FIXED, SIZEOF(CTask *) * cMaxThreads);
_fTerminatePool = FALSE;
_fHadSuccess = FALSE;
_cPoolThreads = 0;
_cMaxThreads = cMaxThreads;
_cRefs = 1;
_cSpare = 0;
_fAllDying = FALSE;
ThdInterlockedIncrement(&g_cPools);
if (_hEventComplete && _hEventEnableSecondTierThreads &&
_hSemNSpare && _ppSpareTasks)
*phr = S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::~CThreadPool
//
// Synopsis: Release resources associated with pool.
//
//--------------------------------------------------------------------
CThreadPool::~CThreadPool()
{
//ThdAssert(WAIT_OBJECT_0 == WaitForSingleObject(_hEventComplete, 0));
if (_hEventComplete)
ThdVerify(CloseHandle(_hEventComplete));
if (_hEventEnableSecondTierThreads)
ThdVerify(CloseHandle(_hEventEnableSecondTierThreads));
if (_hSemNSpare)
ThdVerify(CloseHandle(_hSemNSpare));
DeleteCriticalSection(&_csSpare);
LocalFree(_ppSpareTasks);
ThdInterlockedDecrement(&g_cPools);
ThdAssert(_cRefs == 0);
ThdAssert((_cSpare == 0 && _cPoolThreads == 0) || _cSpare == _cPoolThreads);
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::AssignCommandToThread
//
// Synopsis: Finds or creates a thread to execute the passed in
// command. Takes ownership of the command.
//
// Arguments: [pCommand] -- the command to pass to a thread.
//
// [tier] -- the tier of the thread, if -1 then this is
// the UI command that will be notified by a call to
//
//
//
// Returns: MK_E_EXCEEDEDDEADLINE if pool is being closed down.
// E_OUTOFMEMORY
//
// S_OK if command has been transferred to a thread
//
// Algorithm: if this is a tier 2 command then wait until
// EnableSecondTierThreads has been called.
// wait on the nspare semaphore which when acquired means
// that either 1 or more threads are spare, or a thread
// can be created.
//
//--------------------------------------------------------------------
HRESULT
CThreadPool::AssignCommandToThread(CCommand *pCommand, int tier)
{
HRESULT hr = S_OK;
CTask *pTask;
if (_fTerminatePool || _fAllDying)
{
return(MK_E_EXCEEDEDDEADLINE);
}
if (tier > 1 ) // ensure that search step threads get created before
// we reach limit on # of threads.
{
ThdWaitForSingleObject("AssignCommandToThread::Second Tier",
_hEventEnableSecondTierThreads,
INFINITE); //wait on enable second tier threads
}
// this semaphore lets us in if there is a task in the spare list
// or if we still are allowed to create more threads
ThdWaitForSingleObject( "AssignCommandToThread::_hSemNSpare",
_hSemNSpare,
INFINITE );
EnterCriticalSection(&_csSpare);
if (_cPoolThreads < _cMaxThreads && _cSpare == 0)
{
// will come through here at most _cMaxThreads times
//
// wait for CoUninitialize abort or all threads mutex
//
hr = _refAllThreads.CreateAndRecordThread(&pTask, this);
if (hr == MK_E_EXCEEDEDDEADLINE)
{
_fTerminatePool = TRUE;
}
if (hr == S_OK)
{
_cPoolThreads++;
ThdPrint("%08X, AssignCommandToThread created a thread, "
"_cPoolThreads=%d\n",
GetCurrentThreadId(),
_cPoolThreads);
}
if (_cPoolThreads < _cMaxThreads)
ReleaseSemaphore(_hSemNSpare, 1, NULL);
}
else
{
ThdAssert(_cSpare != 0);
pTask = _ppSpareTasks[--_cSpare];
ThdPrint("%08X, AssignCommandToThread recycled a thread, _cSpare=%d\n",
GetCurrentThreadId(), _cSpare);
}
if (hr == S_OK)
{
if (tier == -1)
{
_pUiCommand = pCommand;
}
pTask->SetCommand(pCommand);
pTask->WakeThread(); //wake thread by releasing wait for work mutex
}
LeaveCriticalSection( &_csSpare );
return(hr);
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::WaitForSuccessOrCompletion
//
// Synopsis: Wait until either there has been a successful command or
// all threads have become spare (i.e. all commands failed)
// At the end of the wait, tell all threads in pool to
// stop creating work and therefore become spare and kill the
// pool.
//
// Algorithm: Wait on _hEventComplete
//
//--------------------------------------------------------------------
VOID
CThreadPool::WaitForSuccessOrCompletion()
{
DWORD i;
DebugMsg(DM_TRACE, TEXT("In WaitForSuccessOrCompletion"));
if (_cPoolThreads != 0)
{
ThdWaitForSingleObject("WaitForSuccessOrCompletion::_hEventComplete",
_hEventComplete,
INFINITE);
}
// signal all threads to exit
//
// Three cases:
// 1) if the wait timed out, then all the search threads
// will exit (because _fTerminatePool is set TRUE), leaving the Ui thread.
// This flag is checked by downlevel searches too. When all but the Ui thread
// have exitted, the pool will inform the Ui to close by a call
// from CThreadPool::PoolThread to CCancelWindowCommand::CancelFromPool.
// 2) if the search was successful by another thread, the pool will
// call from CThreadPool::SetCompletionStatus to CCancelWindowCommand::
// CancelFromPool. This will cause the dialog to disappear.
// 3) if the search was cancelled by the user using the browse dialog then
// CCancelWindow::message IDCANCEL calls CThreadPool::StopSearchFromUI which
// causes the pool to terminate all search threads, and when the last
// thread (the ui thread) exits after GetFileNameFromBrowse returns and
// EndDialog is called, the UI thread itself dies too. This causes the
// whole pool to go away.
//
_fTerminatePool = TRUE;
DebugMsg(DM_TRACE, TEXT("Out WaitForSuccessOrCompletion\n"));
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::SetCompletionStatus
//
// Synopsis: Transfer ownership of the (successful) command to the pool and
// wake up any
//
// Arguments: [ppCommand] -- pointer to the buffer containing the pointer
// to the successful command.
//
// Notes: The successful command can only be set once.
// If the command is transferred from the caller to the pool
// then *ppCommand is set to NULL, otherwise *ppCommand
// is left alone.
//
//--------------------------------------------------------------------
VOID
CThreadPool::SetCompletionStatus(CCommand *pCommand)
{
ThdPrint("%08X, SetCompletionStatus(pCommand = %08X\n",
GetCurrentThreadId(), pCommand);
if (_pUiCommand != NULL && pCommand != _pUiCommand)
{
ThdPrint("%08X, SetCompletionStatus calls _pUiCommand->CancelFromPool()\n",
GetCurrentThreadId());
_pUiCommand->CancelFromPool();
}
SetEvent(_hEventComplete);
_fEventComplete = TRUE;
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::PoolThread
//
// Synopsis: Add this thread back to the pool as a spare thread,
// ready to do work. If all threads are now spare, then
// kill off the pool and remove all threads from the
// referenced global thread set.
//
// Arguments: [pTask] -- pointer to the task of this thread.
//
// Algorithm: Stuff pTask into the array of spare threads and
// then add one to the semaphore to release a thread
// to read it in AssignCommandToThread. Of course, do
// this table manipulation in the critical section.
//
// If all threads are spare then there are no more threads
// that can create work and therefore the pool has done
// its job and should be terminated.
//
// Notes:
//
//--------------------------------------------------------------------
VOID
CThreadPool::PoolThread(CTask *pTask)
{
ThdAssert(!_fAllDying);
ThdAssert(pTask != NULL);
if (MK_E_EXCEEDEDDEADLINE == TimeoutExpired(_dwTickCountDeadline))
{
_fTerminatePool = TRUE;
}
EnterCriticalSection(&_csSpare);
_ppSpareTasks[_cSpare++] = pTask;
ReleaseSemaphore(_hSemNSpare, 1, NULL);
ThdPrint("%08X, PoolThread pools a thread, _cSpare=%d\n",
GetCurrentThreadId(),
_cSpare);
if (_cSpare == _cPoolThreads)
{
// obviously if we have all spare threads then there can be
// no more work to do and no one to generate more work
ThdPrint("%08X, PoolThread detects all threads spare, "
"_cPoolThreads=_cSpare=%d\n",
GetCurrentThreadId(),
_cSpare);
SetEvent(_hEventComplete); // set "all threads done searching event"
_fAllDying = TRUE;
LeaveCriticalSection(&_csSpare);
_refAllThreads.KillTasks(_ppSpareTasks, _cSpare, pTask);
// at this point 'this' has been deleted
// and this and all other threads in the pool should have terminated
ThdAssert(FALSE);
}
else
if (_cSpare == _cPoolThreads-1 && _pUiCommand != NULL)
{
// if there is one running thread and we have UI operational
// then the one thread is the UI thread: we should cancel
// UI since the search is completed (either due to success OR
// to all other threads having searched everywhere they should've)
ThdPrint("%08X, PoolThread calls _pUiCommand->CancelFromPool()\n",
GetCurrentThreadId());
_pUiCommand->CancelFromPool();
}
LeaveCriticalSection(&_csSpare);
}
//+-------------------------------------------------------------------
//
// Member: CThreadPool::TerminatePool
//
// Synopsis: Causes no more work to be assigned to threads and thus
// all threads will become idle, ready for pool
// termination wwhen the final thread becomes idle.
//
// Notes: Called by UI command to stop the search.
//
//--------------------------------------------------------------------
VOID
CThreadPool::TerminatePool()
{
_fTerminatePool = TRUE;
}
//+-------------------------------------------------------------------
//
// Function: StartTaskThread
//
// Synopsis: Called by CreateThread as the thread routine.
//
// Arguments: [pvTask] -- this pointer for the CTask object that
// is associated with this thread.
//
// Returns: Never returns.
//
//--------------------------------------------------------------------
DWORD WINAPI
StartTaskThread(VOID *pvTask)
{
((CTask*)pvTask)->DoTask();
ThdAssert(0);
return(0);
}
//+-------------------------------------------------------------------
//
// Member: CTask::CTask
//
// Synopsis: Constructor for object that represents the thread which
// has its handle stored in _hThread.
//
// Arguments: [phr] -- set to S_OK if successful, otherwise untouched.
//
// Notes: Initialize members. Careful to create thread last!
//
//--------------------------------------------------------------------
CTask::CTask(HRESULT *phr)
{
DWORD dwThread;
_pPool = NULL;
_pCommand = NULL;
_pNext = NULL;
_fAbort = FALSE;
_hSemWaitForWork = CreateSemaphore(NULL, 0, 2, NULL);
if (_hSemWaitForWork != NULL)
{
_hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)StartTaskThread,
this,
0,
&dwThread);
if (_hThread != NULL)
{
ThdInterlockedIncrement(&g_cThreads);
*phr = S_OK;
}
}
else
{
_hThread = NULL;
}
}
//+-------------------------------------------------------------------
//
// Member: CTask::~CTask
//
// Synopsis: Destructor... thread is signalled, just delete the data.
//
//--------------------------------------------------------------------
CTask::~CTask()
{
if (_hThread != NULL)
ThdVerify(CloseHandle(_hThread));
if (_hSemWaitForWork != NULL)
ThdVerify(CloseHandle(_hSemWaitForWork));
if (_pPool != NULL)
_pPool->Release();
delete _pCommand;
}
//+-------------------------------------------------------------------
//
// Member: CTask::DoTask
//
// Synopsis: Main loop for any thread that is in a pool.
// Waits for something to do.
// Will call ExitThread if told to abort (_fAbort)
//
// Notes: This is the only place that threads exit, except
// for the last thread in a pool which can exit
// in PoolThread calling KillTasks
//
//--------------------------------------------------------------------
VOID
CTask::DoTask()
{
while (TRUE)
{
ThdWaitForSingleObject("DoTask", _hSemWaitForWork, INFINITE);
if (_fAbort)
{
ThdPrint("%08X, DoTask: Thread Exit\n", GetCurrentThreadId());
ThdInterlockedDecrement(&g_cThreads);
ExitThread(0);
}
_pCommand->DoCommand();
_pPool->PoolThread(this);
}
}
//+-------------------------------------------------------------------
//
// Member: CTask::AbortThread
//
// Synopsis: Tell the thread to abort and wait until it has.
//
//--------------------------------------------------------------------
VOID
CTask::AbortThread()
{
_fAbort = TRUE;
ReleaseSemaphore(_hSemWaitForWork, 1, NULL);
DWORD index=ThdWaitForSingleObject("AbortThread", _hThread, INFINITE);
ThdAssert(index == WAIT_OBJECT_0);
}
//+-------------------------------------------------------------------
//
// Member: CCommand::CCommand
//
// Synopsis: Common constructor to all commands.
//
// Arguments: [ptszParam] -- a handy string parameter that can be used
// by the derived classes.
// [phr] -- set on error, untouched otherwise.
//
//--------------------------------------------------------------------
CCommand::CCommand(const TCHAR *ptszParam, HRESULT *phr)
{
if (ptszParam != NULL)
{
_ptszParam = (TCHAR*)LocalAlloc(LMEM_FIXED, SIZEOF(TCHAR) * (lstrlen(ptszParam)+1));
if (_ptszParam != NULL)
{
lstrcpy(_ptszParam, ptszParam);
}
else
{
*phr = E_OUTOFMEMORY;
}
}
else
{
_ptszParam = NULL;
}
}
#endif