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.

610 lines
16 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. TSS.CPP
  5. Abstract:
  6. This file implements the classes used by the Timer Subsystem.
  7. History:
  8. 26-Nov-96 raymcc Draft
  9. 28-Dec-96 a-richm Alpha PDK Release
  10. 12-Apr-97 a-levn Extensive changes
  11. --*/
  12. #include "precomp.h"
  13. #include "tss.h"
  14. #include <cominit.h>
  15. #include <stdio.h>
  16. #include <wbemutil.h>
  17. CInstructionQueue::CInstructionQueue()
  18. : m_pQueue(NULL), m_csQueue(), m_bBreak(FALSE)
  19. {
  20. // Create the event which will be signaled whenever a new instruction
  21. // is added to the head of the queue
  22. // ==================================================================
  23. m_hNewHead = CreateEvent(NULL,
  24. FALSE, // automatic reset
  25. FALSE, // non-signalled
  26. NULL);
  27. if (NULL == m_hNewHead)
  28. throw CX_MemoryException(); // checked in esssink.cpp
  29. }
  30. CInstructionQueue::~CInstructionQueue()
  31. {
  32. CInCritSec ics(&m_csQueue); // work inside critical section
  33. while(m_pQueue)
  34. {
  35. CQueueEl* pCurrent = m_pQueue;
  36. m_pQueue = m_pQueue->m_pNext;
  37. delete pCurrent;
  38. }
  39. CloseHandle(m_hNewHead);
  40. }
  41. void CInstructionQueue::TouchHead()
  42. {
  43. SetEvent(m_hNewHead);
  44. }
  45. HRESULT CInstructionQueue::Enqueue(CWbemTime When,
  46. ADDREF CTimerInstruction* pInst)
  47. {
  48. CInCritSec ics(&m_csQueue); // work inside critical section
  49. // Create the link-list element for the object
  50. // ===========================================
  51. CQueueEl* pNew = new CQueueEl(pInst, When);
  52. if(!pNew)
  53. return WBEM_E_OUT_OF_MEMORY;
  54. // Find the right place to insert this instruction
  55. // ===============================================
  56. CQueueEl* pCurrent = m_pQueue;
  57. CQueueEl* pLast = NULL;
  58. while(pCurrent && When >= pCurrent->m_When)
  59. {
  60. pLast = pCurrent;
  61. pCurrent = pCurrent->m_pNext;
  62. }
  63. // Insert it
  64. // =========
  65. if(pLast)
  66. {
  67. // Inserting in the middle
  68. // =======================
  69. pLast->m_pNext = pNew;
  70. pNew->m_pNext = pCurrent;
  71. }
  72. else
  73. {
  74. // Inserting at the head
  75. // =====================
  76. pNew->m_pNext = m_pQueue;
  77. m_pQueue = pNew;
  78. TouchHead();
  79. }
  80. return S_OK;
  81. }
  82. HRESULT CInstructionQueue::Dequeue(OUT RELEASE_ME CTimerInstruction*& pInst,
  83. OUT CWbemTime& When)
  84. {
  85. CInCritSec ics(&m_csQueue); // all work in critical section
  86. if(m_pQueue == NULL)
  87. return S_FALSE;
  88. pInst = m_pQueue->m_pInst;
  89. When = m_pQueue->m_When;
  90. // Null out the instruction in the queue so it would not be deleted
  91. // ================================================================
  92. m_pQueue->m_pInst = NULL;
  93. // Delete the head from the queue
  94. // ==============================
  95. CQueueEl* pNewHead = m_pQueue->m_pNext;
  96. delete m_pQueue;
  97. m_pQueue = pNewHead;
  98. return S_OK;
  99. }
  100. HRESULT CInstructionQueue::Remove(IN CInstructionTest* pPred,
  101. OUT RELEASE_ME CTimerInstruction** ppInst)
  102. {
  103. if(ppInst)
  104. *ppInst = NULL;
  105. CTimerInstruction* pToMark = NULL;
  106. BOOL bFound = FALSE;
  107. {
  108. CInCritSec ics(&m_csQueue); // all work in critical section
  109. CQueueEl* pCurrent = m_pQueue;
  110. CQueueEl* pLast = NULL;
  111. while(pCurrent)
  112. {
  113. if((*pPred)(pCurrent->m_pInst))
  114. {
  115. // Accepted. Remove
  116. // ================
  117. bFound = TRUE;
  118. CQueueEl* pNext;
  119. if(pLast)
  120. {
  121. // removing from the middle
  122. // ========================
  123. pLast->m_pNext = pCurrent->m_pNext;
  124. pNext = pLast->m_pNext;
  125. }
  126. else
  127. {
  128. // Removing from the head
  129. // ======================
  130. m_pQueue = pCurrent->m_pNext;
  131. pNext = m_pQueue;
  132. TouchHead();
  133. }
  134. if(pToMark)
  135. {
  136. // This is not entirely clean. This function was originally
  137. // written to remove one instruction, but then converted to
  138. // remove all matching ones. The **ppInst and pToMark
  139. // business is only applicable to the one instruction case.
  140. // It would be cleaner to split this function up into two,
  141. // but that's too risky at this point.
  142. // ========================================================
  143. pToMark->Release();
  144. }
  145. pToMark = pCurrent->m_pInst;
  146. pToMark->AddRef();
  147. delete pCurrent;
  148. pCurrent = pNext;
  149. }
  150. else
  151. {
  152. pLast = pCurrent;
  153. pCurrent = pCurrent->m_pNext;
  154. }
  155. }
  156. } // out of critical section
  157. // Preserve the instruction to be returned, if required
  158. // ====================================================
  159. if(ppInst != NULL)
  160. {
  161. // Release whatever may be in there
  162. // ================================
  163. if(*ppInst)
  164. (*ppInst)->Release();
  165. // Store the instruction being deleted there
  166. // =========================================
  167. *ppInst = pToMark;
  168. }
  169. else if(pToMark)
  170. {
  171. pToMark->MarkForRemoval();
  172. pToMark->Release();
  173. }
  174. if(!bFound) return S_FALSE;
  175. return S_OK;
  176. }
  177. HRESULT CInstructionQueue::Change(CTimerInstruction* pInst, CWbemTime When)
  178. {
  179. CInCritSec ics(&m_csQueue); // all work in critical section
  180. CIdentityTest Test(pInst);
  181. CTimerInstruction* pObtained;
  182. if(Remove(&Test, &pObtained) == S_OK)
  183. {
  184. // pObtained == pInst, of course
  185. // =============================
  186. // Got it. Enqueue with new time
  187. // =============================
  188. HRESULT hres = S_OK;
  189. if(When.IsFinite())
  190. hres = Enqueue(When, pInst);
  191. pObtained->Release();
  192. return hres;
  193. }
  194. else
  195. {
  196. // This instruction is no longer there
  197. return S_FALSE;
  198. }
  199. }
  200. BOOL CInstructionQueue::IsEmpty()
  201. {
  202. return (m_pQueue == NULL);
  203. }
  204. CWbemInterval CInstructionQueue::TimeToWait()
  205. {
  206. // ================================================
  207. // Assumes that we are inside the critical section!
  208. // ================================================
  209. if(m_pQueue == NULL)
  210. {
  211. return CWbemInterval::GetInfinity();
  212. }
  213. else
  214. {
  215. return CWbemTime::GetCurrentTime().RemainsUntil(m_pQueue->m_When);
  216. }
  217. }
  218. void CInstructionQueue::BreakWait()
  219. {
  220. m_bBreak = TRUE;
  221. SetEvent(m_hNewHead);
  222. }
  223. HRESULT CInstructionQueue::WaitAndPeek(
  224. OUT RELEASE_ME CTimerInstruction*& pInst, OUT CWbemTime& When)
  225. {
  226. EnterCriticalSection(&m_csQueue);
  227. CWbemInterval ToWait = TimeToWait();
  228. // Wait that long. The wait may be interrupted and shortened by
  229. // insertion of new instructions
  230. // ============================================================
  231. while(!ToWait.IsZero())
  232. {
  233. LeaveCriticalSection(&m_csQueue);
  234. // If ToWait is infinite, wait for 30 seconds instead
  235. // ==================================================
  236. DWORD dwMilli;
  237. if(ToWait.IsFinite())
  238. dwMilli = ToWait.GetMilliseconds();
  239. else
  240. dwMilli = 30000;
  241. DWORD dwRes = WbemWaitForSingleObject(m_hNewHead, dwMilli);
  242. if(m_bBreak)
  243. return S_FALSE;
  244. if (dwRes == -1 || (dwRes == WAIT_TIMEOUT && !ToWait.IsFinite()))
  245. {
  246. if (dwRes == -1)
  247. {
  248. ERRORTRACE((LOG_WBEMCORE, "WaitForMultipleObjects failed. LastError = %X.\n", GetLastError()));
  249. ::Sleep(0);
  250. }
  251. // We timed out on the 30 second wait --- time to quit for lack
  252. // of work
  253. // ============================================================
  254. return WBEM_S_TIMEDOUT;
  255. }
  256. EnterCriticalSection(&m_csQueue);
  257. ToWait = TimeToWait();
  258. }
  259. // still in critical section
  260. pInst = m_pQueue->m_pInst;
  261. When = m_pQueue->m_When;
  262. pInst->AddRef();
  263. LeaveCriticalSection(&m_csQueue);
  264. return S_OK;
  265. }
  266. long CInstructionQueue::GetNumInstructions()
  267. {
  268. EnterCriticalSection(&m_csQueue);
  269. long lCount = 0;
  270. CQueueEl* pCurrent = m_pQueue;
  271. while(pCurrent)
  272. {
  273. lCount++;
  274. pCurrent = pCurrent->m_pNext;
  275. }
  276. LeaveCriticalSection(&m_csQueue);
  277. return lCount;
  278. }
  279. CTimerGenerator::CTimerGenerator()
  280. : CHaltable(), m_fExitNow(FALSE), m_hSchedulerThread(NULL)
  281. {
  282. // throws because of CHaltable
  283. }
  284. void CTimerGenerator::EnsureRunning()
  285. {
  286. CInCritSec ics(&m_cs);
  287. if(m_hSchedulerThread)
  288. return;
  289. // Create scheduler thread.
  290. // ========================
  291. NotifyStartingThread();
  292. DWORD dwThreadId;
  293. m_hSchedulerThread = CreateThread(
  294. NULL, // pointer to thread security attributes
  295. 0, // initial thread stack size, in bytes
  296. (LPTHREAD_START_ROUTINE)SchedulerThread, // pointer to thread function
  297. (CTimerGenerator*)this, // argument for new thread
  298. 0, // creation flags
  299. &dwThreadId // pointer to returned thread identifier
  300. );
  301. }
  302. HRESULT CTimerGenerator::Shutdown()
  303. {
  304. if(m_hSchedulerThread)
  305. {
  306. // Set the flag indicating that the scheduler should stop
  307. m_fExitNow = 1;
  308. // Resume the scheduler if halted.
  309. ResumeAll();
  310. // Wake up scheduler. It will stop immediately because of the flag.
  311. m_Queue.BreakWait();
  312. // Wait for scheduler thread to exit.
  313. WbemWaitForSingleObject(m_hSchedulerThread, INFINITE);
  314. CloseHandle(m_hSchedulerThread);
  315. m_hSchedulerThread = NULL;
  316. return S_OK;
  317. }
  318. else return S_FALSE;
  319. }
  320. CTimerGenerator::~CTimerGenerator()
  321. {
  322. Shutdown();
  323. }
  324. HRESULT CTimerGenerator::Set(ADDREF CTimerInstruction *pInst,
  325. CWbemTime NextFiring)
  326. {
  327. if (isValid() == false)
  328. return WBEM_E_OUT_OF_MEMORY;
  329. CInCritSec ics(&m_cs);
  330. //
  331. // 0 for NextFiring indicates that the instruction has not been fired or
  332. // scheduled before, and should therefore be asked when its first firing
  333. // time should be
  334. //
  335. if(NextFiring.IsZero())
  336. {
  337. NextFiring = pInst->GetFirstFiringTime();
  338. }
  339. //
  340. // Infinite firing time indicates that this istruction can never fire
  341. //
  342. if(!NextFiring.IsFinite())
  343. return S_FALSE;
  344. //
  345. // Real instruction --- enqueue
  346. //
  347. HRESULT hres = m_Queue.Enqueue(NextFiring, pInst);
  348. //
  349. // Ensure time generator thread is running, as it shuts down when there are
  350. // no instructions on the queue
  351. //
  352. EnsureRunning();
  353. return hres;
  354. }
  355. HRESULT CTimerGenerator::Remove(CInstructionTest* pPred)
  356. {
  357. CInCritSec ics(&m_cs);
  358. HRESULT hres = m_Queue.Remove(pPred);
  359. if(FAILED(hres)) return hres;
  360. return S_OK;
  361. }
  362. DWORD CTimerGenerator::SchedulerThread(LPVOID pArg)
  363. {
  364. InitializeCom();
  365. CTimerGenerator * pGen = (CTimerGenerator *) pArg;
  366. try
  367. {
  368. while(1)
  369. {
  370. // Wait until we are resumed. In non-paused state, returns immediately.
  371. // ====================================================================
  372. pGen->WaitForResumption();
  373. // Wait for the next instruction on the queue to mature
  374. // ====================================================
  375. CTimerInstruction* pInst;
  376. CWbemTime WhenToFire;
  377. HRESULT hres = pGen->m_Queue.WaitAndPeek(pInst, WhenToFire);
  378. if(hres == S_FALSE)
  379. {
  380. // End of the game: destructor called BreakDequeue
  381. // ===============================================
  382. break;
  383. }
  384. else if(hres == WBEM_S_TIMEDOUT)
  385. {
  386. // The thread is exiting for lack of work
  387. // ======================================
  388. CInCritSec ics(&pGen->m_cs);
  389. // Check if there is any work
  390. // ==========================
  391. if(pGen->m_Queue.IsEmpty())
  392. {
  393. // That's it --- exit
  394. // ==================
  395. CloseHandle( pGen->m_hSchedulerThread );
  396. pGen->m_hSchedulerThread = NULL;
  397. break;
  398. }
  399. else
  400. {
  401. // Work was added before we entered CS
  402. // ===================================
  403. continue;
  404. }
  405. }
  406. // Make sure we haven't been halted while sitting here
  407. // ===================================================
  408. if(pGen->IsHalted())
  409. {
  410. // try again later.
  411. pInst->Release();
  412. continue;
  413. }
  414. // Figure out how many times this instruction has "fired"
  415. // ======================================================
  416. long lMissedFiringCount = 0;
  417. CWbemTime NextFiring = pInst->GetNextFiringTime(WhenToFire,
  418. &lMissedFiringCount);
  419. // Notify accordingly
  420. // ==================
  421. pInst->Fire(lMissedFiringCount+1, NextFiring);
  422. // Requeue the instruction
  423. // =======================
  424. if(pGen->m_Queue.Change(pInst, NextFiring) != S_OK)
  425. {
  426. //Error!!!
  427. }
  428. pInst->Release();
  429. }
  430. }
  431. catch( CX_MemoryException )
  432. {
  433. }
  434. pGen->NotifyStoppingThread();
  435. CoUninitialize();
  436. return 0;
  437. }
  438. class CFreeUnusedLibrariesInstruction : public CTimerInstruction
  439. {
  440. protected:
  441. long m_lRef;
  442. CWbemInterval m_Delay;
  443. public:
  444. CFreeUnusedLibrariesInstruction() : m_lRef(0)
  445. {
  446. m_Delay.SetMilliseconds(660000);
  447. }
  448. virtual void AddRef() { InterlockedIncrement(&m_lRef);}
  449. virtual void Release() {if(0 == InterlockedDecrement(&m_lRef)) delete this;}
  450. virtual int GetInstructionType() {return INSTTYPE_FREE_LIB;}
  451. public:
  452. virtual CWbemTime GetNextFiringTime(CWbemTime LastFiringTime,
  453. OUT long* plFiringCount) const
  454. {
  455. *plFiringCount = 1;
  456. return CWbemTime::GetInfinity();
  457. }
  458. virtual CWbemTime GetFirstFiringTime() const
  459. {
  460. return CWbemTime::GetCurrentTime() + m_Delay;
  461. }
  462. virtual HRESULT Fire(long lNumTimes, CWbemTime NextFiringTime)
  463. {
  464. DEBUGTRACE((LOG_WBEMCORE, "Calling CoFreeUnusedLibraries...\n"));
  465. CoFreeUnusedLibraries();
  466. return S_OK;
  467. }
  468. };
  469. void CTimerGenerator::ScheduleFreeUnusedLibraries()
  470. {
  471. // Inform our EXE that now and in 11 minutes would be a good time to call
  472. // CoFreeUnusedLibraries
  473. // ======================================================================
  474. HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, __TEXT("WINMGMT_PROVIDER_CANSHUTDOWN"));
  475. if (hEvent)
  476. {
  477. SetEvent(hEvent);
  478. CloseHandle(hEvent);
  479. }
  480. }