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
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;
|
|
}
|
|
|
|
|