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

  1. #include "precomp.h"
  2. #include "KillTimer.h"
  3. #include <strsafe.h>
  4. HRESULT CKillerTimer::Initialize(CLifeControl* pControl)
  5. {
  6. HRESULT hr = WBEM_E_FAILED;
  7. // create events
  8. m_hShutdown = CreateEvent(NULL, false, false, NULL);
  9. m_hNewVictim = CreateEvent(NULL, false, false, NULL);
  10. // get some control into our lives
  11. m_pControl = pControl;
  12. if (m_hShutdown && m_hNewVictim && m_pControl)
  13. hr = WBEM_S_NO_ERROR;
  14. return hr;
  15. }
  16. CKillerTimer::CKillerTimer()
  17. : m_hTimerThread(NULL), m_hShutdown(NULL),
  18. m_hNewVictim(NULL), m_pControl(NULL)
  19. {
  20. }
  21. // shuts down timer thread
  22. // toggle thread dead event
  23. // wait for thread to exit
  24. bool CKillerTimer::KillTimer()
  25. {
  26. bool bRet = false;
  27. CInCritSec csStartup(&m_csStartup);
  28. // double check - might have gotten crossed up...
  29. if (m_hTimerThread != NULL)
  30. {
  31. if (SetEvent(m_hShutdown))
  32. {
  33. // you've got one minute to vacate...
  34. bRet = (WAIT_TIMEOUT != WaitForSingleObject(m_hTimerThread, 60000));
  35. CloseHandle(m_hTimerThread);
  36. m_hTimerThread = NULL;
  37. }
  38. }
  39. return bRet;
  40. }
  41. // kill all procs that are older than our expiration date
  42. // called from killer thread only
  43. void CKillerTimer::KillOffOldGuys(const FILETIME& now)
  44. {
  45. CInCritSec csKillers(&m_csKillers);
  46. CKiller* pKiller;
  47. int nSize = m_killers.Size();
  48. for (int i = 0;
  49. (i < nSize) &&
  50. (pKiller = ((CKiller*)m_killers[i])) &&
  51. pKiller->TimeExpired(now);
  52. i++)
  53. {
  54. m_killers[i] = NULL;
  55. pKiller->Die();
  56. // all done now
  57. delete pKiller;
  58. }
  59. // remove them NULLs
  60. m_killers.Compress();
  61. }
  62. // decide when to set the waitable timer again.
  63. // called from killer thread only
  64. void CKillerTimer::RecalcNextKillingSpree(FILETIME& then)
  65. {
  66. CInCritSec csKillers(&m_csKillers);
  67. if (m_killers.Size() > 0)
  68. // since these are assumed sorted, we can just grab the first one
  69. then = ((CKiller*)m_killers[0])->GetDeathDate();
  70. else
  71. then = FILETIME_MAX;
  72. }
  73. HRESULT CKillerTimer::StartTimer()
  74. {
  75. CInCritSec csStartup(&m_csStartup);
  76. HRESULT hr = WBEM_S_NO_ERROR;
  77. // double check - might have gotten crossed up...
  78. if (m_hTimerThread == NULL)
  79. {
  80. DWORD dwIDLikeIcare;
  81. m_hTimerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadStartRoutine,
  82. (LPVOID)this, 0, &dwIDLikeIcare);
  83. if (m_hTimerThread == NULL)
  84. hr = WBEM_E_FAILED;
  85. }
  86. return hr;
  87. }
  88. DWORD WINAPI CKillerTimer::ThreadStartRoutine(LPVOID lpParameter)
  89. {
  90. ((CKillerTimer*)lpParameter)->RunKillerThread();
  91. return 0;
  92. }
  93. void CKillerTimer::RunKillerThread()
  94. {
  95. HRESULT foo = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  96. HANDLE hTimer = CreateWaitableTimer(NULL, false, NULL);
  97. HANDLE hAutoShutdownTimer = CreateWaitableTimer(NULL, false, NULL);
  98. // toggled if while we are inside the startup CS
  99. // so we know to get out when we leave
  100. bool bInStartupCS = false;
  101. // those things that are worth waiting for
  102. enum { FirstEvent = WAIT_OBJECT_0,
  103. TimerEvent = FirstEvent,
  104. AutoShutdownEvent,
  105. NewVictimEvent,
  106. LastHandledEvent = NewVictimEvent,
  107. ShutDownEvent,
  108. TrailerEvent
  109. };
  110. // order counts. so does neatness.
  111. const DWORD nEvents = TrailerEvent -FirstEvent;
  112. HANDLE events[nEvents];
  113. events[TimerEvent -FirstEvent] = hTimer;
  114. events[AutoShutdownEvent -FirstEvent] = hAutoShutdownTimer;
  115. events[NewVictimEvent -FirstEvent] = m_hNewVictim;
  116. events[ShutDownEvent -FirstEvent] = m_hShutdown;
  117. // silliness about the FirstEvent <= whichEvent being always true
  118. // well it is unless somebody changes one of hte constants
  119. // whihc is *why* I made them constants in the first place...
  120. #pragma warning(disable:4296)
  121. DWORD whichEvent;
  122. for (whichEvent = WaitForMultipleObjects(nEvents, (const HANDLE*)&events, FALSE, INFINITE);
  123. (FirstEvent <= whichEvent) && (whichEvent <= LastHandledEvent);
  124. whichEvent = WaitForMultipleObjects(nEvents, (const HANDLE*)&events, FALSE, INFINITE))
  125. #pragma warning(default:4296)
  126. {
  127. // cancel auto-shutdown if scheduled;
  128. CancelWaitableTimer(hAutoShutdownTimer);
  129. switch (whichEvent)
  130. {
  131. case AutoShutdownEvent:
  132. {
  133. m_csStartup.Enter();
  134. // double check - might have gotten crossed up...
  135. if (m_hTimerThread != NULL)
  136. {
  137. {
  138. // see if there's anything in the queue
  139. // if it's enpty - we're gone
  140. // if anything slips in, it'll get caught at ScheduleAssassination time
  141. // and we'll start a new thread
  142. CInCritSec csKillers(&m_csKillers);
  143. if (m_killers.Size() == 0)
  144. {
  145. bInStartupCS = true;
  146. CloseHandle(m_hTimerThread);
  147. m_hTimerThread = NULL;
  148. // and we're outta here...
  149. SetEvent(m_hShutdown);
  150. }
  151. }
  152. }
  153. }
  154. break;
  155. case TimerEvent:
  156. {
  157. // the *official* "now" so we don't get confused
  158. // anything that occurs after *official* "now" must wait for next loop
  159. FILETIME now;
  160. GetSystemTimeAsFileTime(&now);
  161. KillOffOldGuys(now);
  162. // if we killed everybody off
  163. // schedule our own termination in sixty seconds
  164. {
  165. CInCritSec csKillers(&m_csKillers);
  166. if (m_killers.Size() == 0)
  167. {
  168. WAYCOOL_FILETIME then = WAYCOOL_FILETIME(now) +WAYCOOL_FILETIME::SecondsToTicks(60);
  169. SetWaitableTimer(hAutoShutdownTimer, (const union _LARGE_INTEGER *)&then, 0, NULL, NULL, true);
  170. }
  171. }
  172. }
  173. // no break; FALLTHROUGH to recalc
  174. case NewVictimEvent:
  175. {
  176. FILETIME then;
  177. RecalcNextKillingSpree(then);
  178. if (WAYCOOL_FILETIME(FILETIME_MAX) != WAYCOOL_FILETIME(then))
  179. if (!SetWaitableTimer(hTimer, (const union _LARGE_INTEGER *)&then, 0, NULL, NULL, true))
  180. {
  181. DWORD dwErr = GetLastError();
  182. }
  183. }
  184. break;
  185. }
  186. }
  187. // handle the other handles
  188. CancelWaitableTimer(hTimer);
  189. CloseHandle(hTimer);
  190. CancelWaitableTimer(hAutoShutdownTimer);
  191. CloseHandle(hAutoShutdownTimer);
  192. // last gasp at killing off anyone whose time has come
  193. FILETIME now;
  194. GetSystemTimeAsFileTime(&now);
  195. KillOffOldGuys(now);
  196. CoUninitialize();
  197. if (bInStartupCS)
  198. m_csStartup.Leave();
  199. }
  200. CKillerTimer::~CKillerTimer()
  201. {
  202. if (m_hTimerThread)
  203. if (!KillTimer())
  204. ERRORTRACE((LOG_ESS, "CKillerTimer: Unable to stop worker thread, continuing shutdown\n"));
  205. UnloadNOW();
  206. if (m_hShutdown) CloseHandle(m_hShutdown);
  207. if (m_hNewVictim) CloseHandle(m_hNewVictim);
  208. }
  209. // clear out array, does not trigger deaths
  210. void CKillerTimer::UnloadNOW(void)
  211. {
  212. CInCritSec csKillers(&m_csKillers);
  213. for (int i = 0; i < m_killers.Size(); i++)
  214. {
  215. delete (CKiller*)m_killers[i];
  216. m_killers[i] = NULL;
  217. }
  218. m_killers.Empty();
  219. }
  220. // insert pKiller into array where he belongs
  221. HRESULT CKillerTimer::ScheduleAssassination(CKiller* pKiller)
  222. {
  223. HRESULT hr = WBEM_E_FAILED;
  224. {
  225. CInCritSec csKillers(&m_csKillers);
  226. if (m_killers.Size())
  227. {
  228. // minor optimization: check to see if this time is greater than all known
  229. // this will ALWAYS be the case if all procs are created with the same timeout.
  230. if (((CKiller*)m_killers[m_killers.Size() -1])->CompareTime(pKiller->GetDeathDate()) < 0)
  231. {
  232. if (SUCCEEDED(m_killers.Add(pKiller)))
  233. hr = WBEM_S_NO_ERROR;
  234. else
  235. hr = WBEM_E_OUT_OF_MEMORY;
  236. }
  237. else
  238. {
  239. int nFirstGreater = 0;
  240. // WARNING: break in middle of loop.
  241. while (nFirstGreater < m_killers.Size())
  242. {
  243. if (((CKiller*)m_killers[nFirstGreater])->CompareTime(pKiller->GetDeathDate()) >= 0)
  244. {
  245. if (SUCCEEDED(m_killers.InsertAt(nFirstGreater, (void*)pKiller)))
  246. {
  247. hr = WBEM_S_NO_ERROR;
  248. break; // BREAKOUT!
  249. }
  250. else
  251. hr = WBEM_E_OUT_OF_MEMORY;
  252. }
  253. nFirstGreater++;
  254. } // endwhile
  255. } // else
  256. }
  257. else
  258. {
  259. // array is empty
  260. if (SUCCEEDED(m_killers.Add(pKiller)))
  261. hr = WBEM_S_NO_ERROR;
  262. else
  263. hr = WBEM_E_OUT_OF_MEMORY;
  264. }
  265. }
  266. // we'll set this last just to make sure,
  267. // timer thread may have died along the way
  268. if (SUCCEEDED(hr))
  269. {
  270. hr = StartTimer();
  271. if (!SetEvent(m_hNewVictim))
  272. hr = WBEM_E_FAILED;
  273. }
  274. else
  275. // NOTE: this assumes that all failure paths result in
  276. // pKiller *not* being added to list
  277. delete pKiller;
  278. return hr;
  279. }