Leaked source code of windows server 2003
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.
 
 
 
 
 
 

336 lines
10 KiB

#include "precomp.h"
#include "KillTimer.h"
#include <strsafe.h>
HRESULT CKillerTimer::Initialize(CLifeControl* pControl)
{
HRESULT hr = WBEM_E_FAILED;
// create events
m_hShutdown = CreateEvent(NULL, false, false, NULL);
m_hNewVictim = CreateEvent(NULL, false, false, NULL);
// get some control into our lives
m_pControl = pControl;
if (m_hShutdown && m_hNewVictim && m_pControl)
hr = WBEM_S_NO_ERROR;
return hr;
}
CKillerTimer::CKillerTimer()
: m_hTimerThread(NULL), m_hShutdown(NULL),
m_hNewVictim(NULL), m_pControl(NULL)
{
}
// shuts down timer thread
// toggle thread dead event
// wait for thread to exit
bool CKillerTimer::KillTimer()
{
bool bRet = false;
CInCritSec csStartup(&m_csStartup);
// double check - might have gotten crossed up...
if (m_hTimerThread != NULL)
{
if (SetEvent(m_hShutdown))
{
// you've got one minute to vacate...
bRet = (WAIT_TIMEOUT != WaitForSingleObject(m_hTimerThread, 60000));
CloseHandle(m_hTimerThread);
m_hTimerThread = NULL;
}
}
return bRet;
}
// kill all procs that are older than our expiration date
// called from killer thread only
void CKillerTimer::KillOffOldGuys(const FILETIME& now)
{
CInCritSec csKillers(&m_csKillers);
CKiller* pKiller;
int nSize = m_killers.Size();
for (int i = 0;
(i < nSize) &&
(pKiller = ((CKiller*)m_killers[i])) &&
pKiller->TimeExpired(now);
i++)
{
m_killers[i] = NULL;
pKiller->Die();
// all done now
delete pKiller;
}
// remove them NULLs
m_killers.Compress();
}
// decide when to set the waitable timer again.
// called from killer thread only
void CKillerTimer::RecalcNextKillingSpree(FILETIME& then)
{
CInCritSec csKillers(&m_csKillers);
if (m_killers.Size() > 0)
// since these are assumed sorted, we can just grab the first one
then = ((CKiller*)m_killers[0])->GetDeathDate();
else
then = FILETIME_MAX;
}
HRESULT CKillerTimer::StartTimer()
{
CInCritSec csStartup(&m_csStartup);
HRESULT hr = WBEM_S_NO_ERROR;
// double check - might have gotten crossed up...
if (m_hTimerThread == NULL)
{
DWORD dwIDLikeIcare;
m_hTimerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadStartRoutine,
(LPVOID)this, 0, &dwIDLikeIcare);
if (m_hTimerThread == NULL)
hr = WBEM_E_FAILED;
}
return hr;
}
DWORD WINAPI CKillerTimer::ThreadStartRoutine(LPVOID lpParameter)
{
((CKillerTimer*)lpParameter)->RunKillerThread();
return 0;
}
void CKillerTimer::RunKillerThread()
{
HRESULT foo = CoInitializeEx(NULL, COINIT_MULTITHREADED);
HANDLE hTimer = CreateWaitableTimer(NULL, false, NULL);
HANDLE hAutoShutdownTimer = CreateWaitableTimer(NULL, false, NULL);
// toggled if while we are inside the startup CS
// so we know to get out when we leave
bool bInStartupCS = false;
// those things that are worth waiting for
enum { FirstEvent = WAIT_OBJECT_0,
TimerEvent = FirstEvent,
AutoShutdownEvent,
NewVictimEvent,
LastHandledEvent = NewVictimEvent,
ShutDownEvent,
TrailerEvent
};
// order counts. so does neatness.
const DWORD nEvents = TrailerEvent -FirstEvent;
HANDLE events[nEvents];
events[TimerEvent -FirstEvent] = hTimer;
events[AutoShutdownEvent -FirstEvent] = hAutoShutdownTimer;
events[NewVictimEvent -FirstEvent] = m_hNewVictim;
events[ShutDownEvent -FirstEvent] = m_hShutdown;
// silliness about the FirstEvent <= whichEvent being always true
// well it is unless somebody changes one of hte constants
// whihc is *why* I made them constants in the first place...
#pragma warning(disable:4296)
DWORD whichEvent;
for (whichEvent = WaitForMultipleObjects(nEvents, (const HANDLE*)&events, FALSE, INFINITE);
(FirstEvent <= whichEvent) && (whichEvent <= LastHandledEvent);
whichEvent = WaitForMultipleObjects(nEvents, (const HANDLE*)&events, FALSE, INFINITE))
#pragma warning(default:4296)
{
// cancel auto-shutdown if scheduled;
CancelWaitableTimer(hAutoShutdownTimer);
switch (whichEvent)
{
case AutoShutdownEvent:
{
m_csStartup.Enter();
// double check - might have gotten crossed up...
if (m_hTimerThread != NULL)
{
{
// see if there's anything in the queue
// if it's enpty - we're gone
// if anything slips in, it'll get caught at ScheduleAssassination time
// and we'll start a new thread
CInCritSec csKillers(&m_csKillers);
if (m_killers.Size() == 0)
{
bInStartupCS = true;
CloseHandle(m_hTimerThread);
m_hTimerThread = NULL;
// and we're outta here...
SetEvent(m_hShutdown);
}
}
}
}
break;
case TimerEvent:
{
// the *official* "now" so we don't get confused
// anything that occurs after *official* "now" must wait for next loop
FILETIME now;
GetSystemTimeAsFileTime(&now);
KillOffOldGuys(now);
// if we killed everybody off
// schedule our own termination in sixty seconds
{
CInCritSec csKillers(&m_csKillers);
if (m_killers.Size() == 0)
{
WAYCOOL_FILETIME then = WAYCOOL_FILETIME(now) +WAYCOOL_FILETIME::SecondsToTicks(60);
SetWaitableTimer(hAutoShutdownTimer, (const union _LARGE_INTEGER *)&then, 0, NULL, NULL, true);
}
}
}
// no break; FALLTHROUGH to recalc
case NewVictimEvent:
{
FILETIME then;
RecalcNextKillingSpree(then);
if (WAYCOOL_FILETIME(FILETIME_MAX) != WAYCOOL_FILETIME(then))
if (!SetWaitableTimer(hTimer, (const union _LARGE_INTEGER *)&then, 0, NULL, NULL, true))
{
DWORD dwErr = GetLastError();
}
}
break;
}
}
// handle the other handles
CancelWaitableTimer(hTimer);
CloseHandle(hTimer);
CancelWaitableTimer(hAutoShutdownTimer);
CloseHandle(hAutoShutdownTimer);
// last gasp at killing off anyone whose time has come
FILETIME now;
GetSystemTimeAsFileTime(&now);
KillOffOldGuys(now);
CoUninitialize();
if (bInStartupCS)
m_csStartup.Leave();
}
CKillerTimer::~CKillerTimer()
{
if (m_hTimerThread)
if (!KillTimer())
ERRORTRACE((LOG_ESS, "CKillerTimer: Unable to stop worker thread, continuing shutdown\n"));
UnloadNOW();
if (m_hShutdown) CloseHandle(m_hShutdown);
if (m_hNewVictim) CloseHandle(m_hNewVictim);
}
// clear out array, does not trigger deaths
void CKillerTimer::UnloadNOW(void)
{
CInCritSec csKillers(&m_csKillers);
for (int i = 0; i < m_killers.Size(); i++)
{
delete (CKiller*)m_killers[i];
m_killers[i] = NULL;
}
m_killers.Empty();
}
// insert pKiller into array where he belongs
HRESULT CKillerTimer::ScheduleAssassination(CKiller* pKiller)
{
HRESULT hr = WBEM_E_FAILED;
{
CInCritSec csKillers(&m_csKillers);
if (m_killers.Size())
{
// minor optimization: check to see if this time is greater than all known
// this will ALWAYS be the case if all procs are created with the same timeout.
if (((CKiller*)m_killers[m_killers.Size() -1])->CompareTime(pKiller->GetDeathDate()) < 0)
{
if (SUCCEEDED(m_killers.Add(pKiller)))
hr = WBEM_S_NO_ERROR;
else
hr = WBEM_E_OUT_OF_MEMORY;
}
else
{
int nFirstGreater = 0;
// WARNING: break in middle of loop.
while (nFirstGreater < m_killers.Size())
{
if (((CKiller*)m_killers[nFirstGreater])->CompareTime(pKiller->GetDeathDate()) >= 0)
{
if (SUCCEEDED(m_killers.InsertAt(nFirstGreater, (void*)pKiller)))
{
hr = WBEM_S_NO_ERROR;
break; // BREAKOUT!
}
else
hr = WBEM_E_OUT_OF_MEMORY;
}
nFirstGreater++;
} // endwhile
} // else
}
else
{
// array is empty
if (SUCCEEDED(m_killers.Add(pKiller)))
hr = WBEM_S_NO_ERROR;
else
hr = WBEM_E_OUT_OF_MEMORY;
}
}
// we'll set this last just to make sure,
// timer thread may have died along the way
if (SUCCEEDED(hr))
{
hr = StartTimer();
if (!SetEvent(m_hNewVictim))
hr = WBEM_E_FAILED;
}
else
// NOTE: this assumes that all failure paths result in
// pKiller *not* being added to list
delete pKiller;
return hr;
}