Source code of Windows XP (NT5)
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.

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