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.

950 lines
26 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1993.
  5. //
  6. // File: runobj.cxx
  7. //
  8. // Contents: Run instance object class implementations.
  9. //
  10. // Classes: CRun and CRunList
  11. //
  12. // History: 14-Mar-96 EricB Created.
  13. // 10-Nov-96 AnirudhS Fixed CRunList::AddSorted to discard the
  14. // appropriate element if the list is at its maximum size.
  15. // Fixed CRunList::MakeSysTimeArray to call CoTaskMemAlloc
  16. // once instead of calling CoTaskMemRealloc in a loop.
  17. //
  18. //----------------------------------------------------------------------------
  19. #include "..\pch\headers.hxx"
  20. #pragma hdrstop
  21. #include <job_cls.hxx>
  22. #include <misc.hxx>
  23. #include <debug.hxx>
  24. #include "..\svc_core\svc_core.hxx"
  25. #if !defined(_CHICAGO_)
  26. #include <userenv.h> // UnloadUserProfile
  27. #endif // !defined(_CHICAGO_)
  28. PFNSetThreadExecutionState pfnSetThreadExecutionState;
  29. DWORD g_WakeCountSlot = 0xFFFFFFFF;
  30. //+----------------------------------------------------------------------------
  31. //
  32. // Run instance object class
  33. //
  34. //-----------------------------------------------------------------------------
  35. //+----------------------------------------------------------------------------
  36. //
  37. // Method: CRun::CRun
  38. //
  39. // Synopsis: ctor for time-sorted lists.
  40. //
  41. //-----------------------------------------------------------------------------
  42. CRun::CRun(LPFILETIME pft, LPFILETIME pftDeadline, FILETIME ftKill,
  43. DWORD MaxRunTime, DWORD rgFlags, WORD wIdleWait,
  44. BOOL fKeepAfterRunning) :
  45. m_ft(*pft),
  46. m_ftDeadline(*pftDeadline),
  47. m_ftKill(ftKill),
  48. m_hJob(NULL),
  49. m_ptszJobName(NULL),
  50. m_dwProcessId(0),
  51. #if !defined(_CHICAGO_)
  52. m_hUserToken(NULL),
  53. m_ptszDesktop(NULL),
  54. m_ptszStation(NULL),
  55. m_hProfile(NULL),
  56. #endif // !defined(_CHICAGO_)
  57. m_rgFlags(rgFlags),
  58. m_dwMaxRunTime(MaxRunTime),
  59. m_wIdleWait(wIdleWait),
  60. m_fKeepInList(fKeepAfterRunning),
  61. m_fStarted(FALSE)
  62. {
  63. schDebugOut((DEB_TRACE, "CRun::CRun(0x%x)\n", this));
  64. }
  65. //+----------------------------------------------------------------------------
  66. //
  67. // Method: CRun::CRun
  68. //
  69. // Synopsis: ctor for non-time-sorted lists.
  70. //
  71. //-----------------------------------------------------------------------------
  72. CRun::CRun(DWORD MaxRunTime, DWORD rgFlags, FILETIME ftDeadline,
  73. BOOL fKeepAfterRunning) :
  74. m_ftDeadline(ftDeadline),
  75. m_ftKill(MAX_FILETIME),
  76. m_hJob(NULL),
  77. m_ptszJobName(NULL),
  78. m_dwProcessId(0),
  79. #if !defined(_CHICAGO_)
  80. m_ptszDesktop(NULL),
  81. m_ptszStation(NULL),
  82. m_hUserToken(NULL),
  83. m_hProfile(NULL),
  84. #endif // !defined(_CHICAGO_)
  85. m_rgFlags(rgFlags),
  86. m_dwMaxRunTime(MaxRunTime),
  87. m_wIdleWait(0),
  88. m_fKeepInList(fKeepAfterRunning),
  89. m_fStarted(FALSE)
  90. {
  91. //schDebugOut((DEB_TRACE, "CRun::CRun(0x%x)\n", this));
  92. //
  93. // This ctor is used for non-sorted lists. Set the time elements to
  94. // non-zero values to distinguish these elements from the head.
  95. //
  96. // CODEWORK - Don't use 0,0 to mark the head. Remove IsNull() method.
  97. // Instead use a NULL Next pointer to mark the last list element.
  98. //
  99. m_ft.dwLowDateTime = 1;
  100. m_ft.dwHighDateTime = 1;
  101. }
  102. //+----------------------------------------------------------------------------
  103. //
  104. // Method: CRun::CRun
  105. //
  106. // Synopsis: ctor for idle-time-sorted lists.
  107. //
  108. //-----------------------------------------------------------------------------
  109. CRun::CRun(DWORD MaxRunTime, DWORD rgFlags, WORD wIdleWait,
  110. FILETIME ftDeadline, BOOL fKeepAfterRunning) :
  111. m_ftDeadline(ftDeadline),
  112. m_ftKill(MAX_FILETIME),
  113. m_hJob(NULL),
  114. m_ptszJobName(NULL),
  115. m_dwProcessId(0),
  116. #if !defined(_CHICAGO_)
  117. m_ptszDesktop(NULL),
  118. m_ptszStation(NULL),
  119. m_hUserToken(NULL),
  120. m_hProfile(NULL),
  121. #endif // !defined(_CHICAGO_)
  122. m_rgFlags(rgFlags),
  123. m_dwMaxRunTime(MaxRunTime),
  124. m_wIdleWait(wIdleWait),
  125. m_fKeepInList(fKeepAfterRunning),
  126. m_fStarted(FALSE)
  127. {
  128. TRACE3(CRun,CRun);
  129. //
  130. // Set the time elements to non-zero values to distinguish
  131. // these elements from the head.
  132. // CODEWORK - as above, don't do this.
  133. //
  134. m_ft.dwLowDateTime = 1;
  135. m_ft.dwHighDateTime = 1;
  136. }
  137. //+----------------------------------------------------------------------------
  138. //
  139. // Method: CRun::CRun
  140. //
  141. // Synopsis: copy ctor.
  142. //
  143. // Notes: This ctor should not be used to copy running objects, i.e.
  144. // objects that have valid process, user token, or profile
  145. // handles.
  146. //
  147. //-----------------------------------------------------------------------------
  148. CRun::CRun(CRun * pRun) :
  149. m_ft(pRun->m_ft),
  150. m_ftDeadline(pRun->m_ftDeadline),
  151. m_ftKill(pRun->m_ftKill),
  152. m_hJob(NULL),
  153. m_ptszJobName(NULL),
  154. m_dwProcessId(pRun->m_dwProcessId),
  155. #if !defined(_CHICAGO_)
  156. m_ptszDesktop(NULL),
  157. m_ptszStation(NULL),
  158. m_hUserToken(NULL),
  159. m_hProfile(NULL),
  160. #endif // !defined(_CHICAGO_)
  161. m_rgFlags(pRun->m_rgFlags),
  162. m_dwMaxRunTime(pRun->m_dwMaxRunTime),
  163. m_wIdleWait(pRun->m_wIdleWait),
  164. m_fKeepInList(pRun->m_fKeepInList),
  165. m_fStarted(pRun->m_fStarted)
  166. {
  167. TRACE3(CRun,CRun(Copy));
  168. SetName(pRun->m_ptszJobName);
  169. schAssert(!pRun->m_hJob);
  170. #if !defined(_CHICAGO_)
  171. schAssert(!pRun->m_hUserToken);
  172. schAssert(!pRun->m_hProfile);
  173. #endif // !defined(_CHICAGO_)
  174. }
  175. //+---------------------------------------------------------------------------
  176. //
  177. // Method: CRun::CRun
  178. //
  179. // Synopsis: ctor
  180. //
  181. //----------------------------------------------------------------------------
  182. CRun::CRun(void) :
  183. m_ftDeadline(MAX_FILETIME),
  184. m_ftKill(MAX_FILETIME),
  185. m_hJob(NULL),
  186. m_ptszJobName(NULL),
  187. m_dwProcessId(0),
  188. #if !defined(_CHICAGO_)
  189. m_ptszDesktop(NULL),
  190. m_ptszStation(NULL),
  191. m_hUserToken(NULL),
  192. m_hProfile(NULL),
  193. #endif // !defined(_CHICAGO_)
  194. m_rgFlags(0),
  195. m_dwMaxRunTime(RUN_TIME_NO_END),
  196. m_wIdleWait(0),
  197. m_fKeepInList(FALSE),
  198. m_fStarted(FALSE)
  199. {
  200. //schDebugOut((DEB_TRACE, "CRun::CRun(0x%x)\n", this));
  201. //
  202. // The null arg ctor is used only by CRunList for its head element
  203. // member. The zero time value for this element marks it as the head
  204. // when traversing the doubly linked list.
  205. //
  206. m_ft.dwLowDateTime = 0;
  207. m_ft.dwHighDateTime = 0;
  208. }
  209. //+---------------------------------------------------------------------------
  210. //
  211. // Method: CRun::~CRun
  212. //
  213. // Synopsis: dtor
  214. //
  215. //----------------------------------------------------------------------------
  216. CRun::~CRun()
  217. {
  218. BOOL fOk;
  219. //schDebugOut((DEB_TRACE, "CRun::~CRun(0x%x)\n", this));
  220. if (m_hJob)
  221. {
  222. CloseHandle(m_hJob);
  223. }
  224. if (m_ptszJobName)
  225. {
  226. delete m_ptszJobName;
  227. }
  228. #if !defined(_CHICAGO_)
  229. if (m_hProfile)
  230. {
  231. fOk = UnloadUserProfile(m_hUserToken, m_hProfile);
  232. if (!fOk)
  233. {
  234. ERR_OUT("~CRun: UnloadUserProfile",
  235. HRESULT_FROM_WIN32(GetLastError()));
  236. }
  237. }
  238. if (m_hUserToken)
  239. {
  240. fOk = CloseHandle(m_hUserToken);
  241. if (!fOk)
  242. {
  243. ERR_OUT("~CRun: CloseHandle",
  244. HRESULT_FROM_WIN32(GetLastError()));
  245. }
  246. }
  247. if ( m_ptszDesktop )
  248. {
  249. delete m_ptszDesktop;
  250. }
  251. if ( m_ptszStation )
  252. {
  253. delete m_ptszStation;
  254. }
  255. #endif // !defined(_CHICAGO_)
  256. }
  257. //+---------------------------------------------------------------------------
  258. //
  259. // Method: CRun::SetName
  260. //
  261. // Synopsis: Set the job name property. This is the folder-relative name.
  262. //
  263. //----------------------------------------------------------------------------
  264. HRESULT
  265. CRun::SetName(LPCTSTR ptszName)
  266. {
  267. if (m_ptszJobName)
  268. {
  269. delete m_ptszJobName;
  270. m_ptszJobName = NULL;
  271. }
  272. if (!ptszName)
  273. {
  274. return S_OK;
  275. }
  276. m_ptszJobName = new TCHAR[lstrlen(ptszName) + 1];
  277. if (m_ptszJobName == NULL)
  278. {
  279. return(E_OUTOFMEMORY);
  280. }
  281. lstrcpy(m_ptszJobName, ptszName);
  282. return(S_OK);
  283. }
  284. #if !defined(_CHICAGO_)
  285. //+---------------------------------------------------------------------------
  286. //
  287. // Method: CRun::SetDesktop
  288. //
  289. // Synopsis: Set the Desktop name property. This is the windows station \ desktop.
  290. //
  291. //----------------------------------------------------------------------------
  292. HRESULT
  293. CRun::SetDesktop( LPCTSTR ptszDesktop )
  294. {
  295. if (ptszDesktop)
  296. {
  297. delete m_ptszDesktop;
  298. m_ptszDesktop = NULL;
  299. }
  300. if (!ptszDesktop)
  301. {
  302. return S_OK;
  303. }
  304. m_ptszDesktop = new TCHAR[lstrlen(ptszDesktop) + 1];
  305. if (m_ptszDesktop == NULL)
  306. {
  307. return(E_OUTOFMEMORY);
  308. }
  309. lstrcpy(m_ptszDesktop, ptszDesktop);
  310. return(S_OK);
  311. }
  312. //+---------------------------------------------------------------------------
  313. //
  314. // Method: CRun::SetStation
  315. //
  316. // Synopsis: Set the Desktop name property. This is the windows station \ desktop.
  317. //
  318. //----------------------------------------------------------------------------
  319. HRESULT
  320. CRun::SetStation( LPCTSTR ptszStation )
  321. {
  322. if (ptszStation)
  323. {
  324. delete m_ptszStation;
  325. m_ptszStation = NULL;
  326. }
  327. if (!ptszStation)
  328. {
  329. return S_OK;
  330. }
  331. m_ptszStation = new TCHAR[lstrlen(ptszStation) + 1];
  332. if (m_ptszStation == NULL)
  333. {
  334. return(E_OUTOFMEMORY);
  335. }
  336. lstrcpy(m_ptszStation, ptszStation);
  337. return(S_OK);
  338. }
  339. #endif // !defined(_CHICAGO_)
  340. //+---------------------------------------------------------------------------
  341. //
  342. // Method: CRun::AdjustKillTimeByMaxRunTime
  343. //
  344. // Synopsis: If the job has a max run time, advance its kill time to "now"
  345. // plus the max run time.
  346. //
  347. //----------------------------------------------------------------------------
  348. void
  349. CRun::AdjustKillTimeByMaxRunTime(FILETIME ftNow)
  350. {
  351. if (m_dwMaxRunTime != INFINITE)
  352. {
  353. AdvanceKillTime(FTfrom64(
  354. FTto64(ftNow) +
  355. (DWORDLONG) m_dwMaxRunTime *
  356. FILETIMES_PER_MILLISECOND));
  357. }
  358. }
  359. //+----------------------------------------------------------------------------
  360. //
  361. // Run object list class
  362. //
  363. //-----------------------------------------------------------------------------
  364. //+----------------------------------------------------------------------------
  365. //
  366. // Member: CRunList::FreeList
  367. //
  368. // Synopsis: Frees the linked list elements, skipping the head.
  369. //
  370. //-----------------------------------------------------------------------------
  371. void
  372. CRunList::FreeList(void)
  373. {
  374. //
  375. // Skip the head, it is a placeholder in the circular list with a time
  376. // value of zero. The zero time value is used as a marker so that we can
  377. // tell when we have traversed the entire list.
  378. //
  379. CRun * pCur = m_RunHead.Next();
  380. while (!pCur->IsNull())
  381. {
  382. CRun * pNext = pCur->Next();
  383. pCur->UnLink();
  384. delete pCur;
  385. pCur = pNext;
  386. }
  387. }
  388. //+----------------------------------------------------------------------------
  389. //
  390. // Member: CRunList::AddSorted
  391. //
  392. // Synopsis: Add to the list in time sorted order.
  393. //
  394. // Arguments: [ftRun] -
  395. // [ftDeadline] -
  396. // [ftKillTime] -
  397. // [ptszJobName] -
  398. // [dwJobFlags] -
  399. // [dwMaxRunTime] -
  400. // [wIdleWait] -
  401. // [pCount] - On entry and on exit, points to the number of
  402. // elements in the list.
  403. // [cLimit] - Limit on the number of elements in the list.
  404. //
  405. // Returns: S_OK - new run added to the list.
  406. // S_FALSE - new run not added to the list because the list has
  407. // already reached its size limit and the new job's run time
  408. // is later than the last run time in the list.
  409. //
  410. //-----------------------------------------------------------------------------
  411. HRESULT
  412. CTimeRunList::AddSorted(FILETIME ftRun, FILETIME ftDeadline, FILETIME ftKillTime,
  413. LPTSTR ptszJobName,
  414. DWORD dwJobFlags, DWORD dwMaxRunTime, WORD wIdleWait,
  415. WORD * pCount, WORD cLimit)
  416. {
  417. schAssert(*pCount <= cLimit);
  418. //
  419. // The list is monotonically increasing in time. Traverse the list in
  420. // reverse order since the most common case will be to put the new
  421. // element at the end. That is, except in the case of overlapping
  422. // duration intervals, the run times for the same trigger will be
  423. // discovered in monotonically increasing order.
  424. //
  425. // For merging in the run times from separate triggers or jobs, the runs
  426. // will not be in any predictable order. In this case, it doesn't matter
  427. // from which end the search starts.
  428. //
  429. // CODEWORK Use IsNull() instead of GetTime(). Make this loop a for loop.
  430. //
  431. FILETIME ftCur;
  432. CRun * pRun = m_RunHead.Prev();
  433. pRun->GetTime(&ftCur);
  434. //
  435. // Note that the head element is merely a marker (since this is a doubly
  436. // linked, circular list) and has its time value set to zero. Thus if we
  437. // reach a zero FILETIME, we have reached the head and thus know that
  438. // there is no list element with a later time, so insert at the tail.
  439. //
  440. while (ftCur.dwLowDateTime || ftCur.dwHighDateTime)
  441. {
  442. if (CompareFileTime(&ftCur, &ftRun) == 0)
  443. {
  444. //
  445. // Duplicate found, check for job name match. If here as a result
  446. // of a call to ITask::GetRunTimes, then both will be null. We
  447. // want duplicates eliminated in this case. Otherwise, compare
  448. // names.
  449. //
  450. if ((pRun->GetName() == NULL && ptszJobName == NULL) ||
  451. (pRun->GetName() != NULL && ptszJobName != NULL &&
  452. lstrcmpi(pRun->GetName(), ptszJobName) == 0))
  453. {
  454. // keep the one already in the list but set the kill time
  455. // to the earlier of the two,
  456. // set the idle wait time to the lesser (less restrictive)
  457. // of the two
  458. // and set the start deadline to the later (less
  459. // restrictive) of the two.
  460. //
  461. pRun->ReduceWaitTo(wIdleWait);
  462. pRun->AdvanceKillTime(ftKillTime);
  463. pRun->RelaxDeadline(ftDeadline);
  464. // (There is no reason for the MaxRunTime to be different)
  465. schAssert(pRun->GetMaxRunTime() == dwMaxRunTime);
  466. return S_OK;
  467. }
  468. }
  469. if (CompareFileTime(&ftCur, &ftRun) < 0)
  470. {
  471. //
  472. // The new run is later than the current, so we are at the
  473. // insertion point.
  474. //
  475. break;
  476. }
  477. pRun = pRun->Prev();
  478. pRun->GetTime(&ftCur);
  479. }
  480. //
  481. // If the list is already at its maximum size, discard either the
  482. // last element or the one we were about to insert, whichever is
  483. // later.
  484. //
  485. if (*pCount >= cLimit)
  486. {
  487. CRun * pLast = m_RunHead.Prev();
  488. if (pLast == pRun)
  489. {
  490. //
  491. // We were about to insert after the last element.
  492. //
  493. return S_FALSE;
  494. }
  495. else
  496. {
  497. //
  498. // Discard the last element before inserting the new one.
  499. //
  500. pLast->UnLink();
  501. delete pLast;
  502. (*pCount)--;
  503. }
  504. }
  505. //
  506. // Create the new element and insert after the current one.
  507. //
  508. CRun * pNewRun = new CRun(&ftRun, &ftDeadline, ftKillTime, dwMaxRunTime,
  509. dwJobFlags, wIdleWait, FALSE);
  510. if (!pNewRun)
  511. {
  512. ERR_OUT("RunList: Add", E_OUTOFMEMORY);
  513. return E_OUTOFMEMORY;
  514. }
  515. HRESULT hr = pNewRun->SetName(ptszJobName);
  516. if (FAILED(hr))
  517. {
  518. ERR_OUT("CRunList::AddSorted SetName", hr);
  519. delete pNewRun;
  520. return hr;
  521. }
  522. pNewRun->SetNext(pRun->Next());
  523. pNewRun->Next()->SetPrev(pNewRun);
  524. pRun->SetNext(pNewRun);
  525. pNewRun->SetPrev(pRun);
  526. //
  527. // Increment the count.
  528. //
  529. (*pCount)++;
  530. return S_OK;
  531. }
  532. //+----------------------------------------------------------------------------
  533. //
  534. // Member: CIdleRunList::AddSortedByIdleWait
  535. //
  536. // Synopsis: Add to the list in time sorted order.
  537. //
  538. //-----------------------------------------------------------------------------
  539. void
  540. CIdleRunList::AddSortedByIdleWait(CRun * pAdd)
  541. {
  542. //
  543. // If the system needs to stay awake to run this task, increment the
  544. // thread's wake count. (We know that this is always called by the
  545. // worker thread.)
  546. //
  547. if (pAdd->IsFlagSet(TASK_FLAG_SYSTEM_REQUIRED))
  548. {
  549. WrapSetThreadExecutionState(TRUE, "AddSortedByIdleWait");
  550. }
  551. if (m_RunHead.Next()->IsNull())
  552. {
  553. // List is empty, so add this as the first element.
  554. //
  555. pAdd->LinkAfter(&m_RunHead);
  556. return;
  557. }
  558. WORD wAddWait = pAdd->GetWait();
  559. schAssert(wAddWait > 0); // We should never put a job in the idle wait
  560. // list if its idle wait time is 0
  561. //
  562. // Walk the list, comparing idle wait times.
  563. //
  564. CRun * pCur = m_RunHead.Next();
  565. while (!pCur->IsNull())
  566. {
  567. if (wAddWait < pCur->GetWait())
  568. {
  569. pAdd->LinkBefore(pCur);
  570. return;
  571. }
  572. pCur = pCur->Next();
  573. }
  574. //
  575. // Add to the end of the list.
  576. //
  577. pAdd->LinkBefore(pCur);
  578. }
  579. //+----------------------------------------------------------------------------
  580. //
  581. // Member: CIdleRunList::GetFirstWait
  582. //
  583. // Synopsis: Finds the lowest idle wait time of the jobs in the list that
  584. // haven't already been started in this idle period.
  585. // Returns 0xffff if there is no such job.
  586. //
  587. //-----------------------------------------------------------------------------
  588. WORD
  589. CIdleRunList::GetFirstWait()
  590. {
  591. for (CRun * pRun = m_RunHead.Next();
  592. !pRun->IsNull();
  593. pRun = pRun->Next())
  594. {
  595. if (!pRun->m_fStarted)
  596. {
  597. // (We should never have inserted a run with zero wait time)
  598. schAssert(pRun->GetWait() != 0);
  599. return (pRun->GetWait());
  600. }
  601. }
  602. return 0xffff;
  603. }
  604. //+----------------------------------------------------------------------------
  605. //
  606. // Member: CIdleRunList::MarkNoneStarted
  607. //
  608. // Synopsis: Marks all jobs in the idle list as not having been started in
  609. // the current idle period.
  610. //
  611. //-----------------------------------------------------------------------------
  612. void
  613. CIdleRunList::MarkNoneStarted()
  614. {
  615. schDebugOut((DEB_IDLE, "Marking idle jobs as not started\n"));
  616. for (CRun * pRun = GetFirstJob();
  617. !pRun->IsNull();
  618. pRun = pRun->Next())
  619. {
  620. pRun->m_fStarted = FALSE;
  621. }
  622. }
  623. //+----------------------------------------------------------------------------
  624. //
  625. // Member: CRunList::AddCopy
  626. //
  627. // Synopsis: Add a copy of the object to the list.
  628. //
  629. //-----------------------------------------------------------------------------
  630. HRESULT
  631. CRunList::AddCopy(CRun * pOriginal)
  632. {
  633. CRun * pCopy = new CRun(pOriginal);
  634. if (pCopy == NULL)
  635. {
  636. return E_OUTOFMEMORY;
  637. }
  638. pCopy->LinkAfter(&m_RunHead);
  639. return S_OK;
  640. }
  641. //+----------------------------------------------------------------------------
  642. //
  643. // Member: CTimeRunList::Pop
  644. //
  645. // Synopsis: Removes the first (earliest) time element from the list and
  646. // returns it.
  647. //
  648. //-----------------------------------------------------------------------------
  649. CRun *
  650. CTimeRunList::Pop(void)
  651. {
  652. CRun * pPop = m_RunHead.Next();
  653. if (pPop->IsNull())
  654. {
  655. // List is empty, so return a flag return code.
  656. //
  657. return NULL;
  658. }
  659. pPop->UnLink();
  660. return pPop;
  661. }
  662. //+----------------------------------------------------------------------------
  663. //
  664. // Member: CTimeRunList::PeekHeadTime
  665. //
  666. // Synopsis: Returns the filetime value for the element at the head of the
  667. // list.
  668. //
  669. //-----------------------------------------------------------------------------
  670. HRESULT
  671. CTimeRunList::PeekHeadTime(LPFILETIME pft)
  672. {
  673. if (m_RunHead.Next()->IsNull())
  674. {
  675. // List is empty, so return a flag return code.
  676. //
  677. return S_FALSE;
  678. }
  679. m_RunHead.Next()->GetTime(pft);
  680. return S_OK;
  681. }
  682. //+----------------------------------------------------------------------------
  683. //
  684. // Function: CTimeRunList::MakeSysTimeArray
  685. //
  686. // Synopsis: returns the run list times as an array of SYSTEMTIME structs.
  687. //
  688. // Arguments: [prgst] - a pointer to the returned array of filetime structs
  689. // is stored here. This function allocates the array
  690. // using CoTaskMemAlloc. It must be freed by the caller.
  691. // [pCount] - On entry, points to an upper limit on the number of
  692. // array elements to return. On exit, points to the
  693. // actual number returned.
  694. //
  695. // Returns: E_OUTOFMEMORY, S_OK
  696. //
  697. //-----------------------------------------------------------------------------
  698. HRESULT
  699. CTimeRunList::MakeSysTimeArray(LPSYSTEMTIME * prgst, WORD * pCount)
  700. {
  701. WORD cLimit = *pCount;
  702. *pCount = 0;
  703. *prgst = (LPSYSTEMTIME) CoTaskMemAlloc(cLimit * sizeof(SYSTEMTIME));
  704. if (*prgst == NULL)
  705. {
  706. return E_OUTOFMEMORY;
  707. }
  708. //
  709. // Skip the head, it is a placeholder in the circular list with a time
  710. // value of zero.
  711. //
  712. for (CRun * pCur = m_RunHead.Next();
  713. (*pCount < cLimit) && (!pCur->IsNull());
  714. (*pCount)++, pCur = pCur->Next())
  715. {
  716. pCur->GetSysTime( &(*prgst)[*pCount] );
  717. }
  718. return S_OK;
  719. }
  720. //+----------------------------------------------------------------------------
  721. //
  722. // Member: CIdleRunList::FreeList
  723. //
  724. // Synopsis: Same as CRunList::FreeList except it decrements the thread's
  725. // wake count for each system-required run in the list. (We
  726. // know this method is only called by the worker thread.)
  727. //
  728. //-----------------------------------------------------------------------------
  729. void
  730. CIdleRunList::FreeList()
  731. {
  732. CRun * pCur = m_RunHead.Next();
  733. while (!pCur->IsNull())
  734. {
  735. CRun * pNext = pCur->Next();
  736. pCur->UnLink();
  737. if (pCur->IsFlagSet(TASK_FLAG_SYSTEM_REQUIRED))
  738. {
  739. WrapSetThreadExecutionState(FALSE, "CIdleRunList::FreeList");
  740. }
  741. delete pCur;
  742. pCur = pNext;
  743. }
  744. }
  745. //+----------------------------------------------------------------------------
  746. //
  747. // Member: CIdleRunList::FreeExpiredOrRegenerated
  748. //
  749. // Synopsis: This method is called when rebuilding the idle wait list from
  750. // the data in the tasks folder.
  751. // Removes runs that have m_fKeepInList set. (These correspond
  752. // to jobs with idle triggers.) Also purges expired runs.
  753. // Runs that don't have m_fKeepInList set correspond to runs that
  754. // have been triggered due to some other event, and are waiting
  755. // for an idle period; these are not removed here.
  756. //
  757. //-----------------------------------------------------------------------------
  758. void
  759. CIdleRunList::FreeExpiredOrRegenerated()
  760. {
  761. // BUGBUG ftNow should be a parameter
  762. FILETIME ftNow = GetLocalTimeAsFileTime();
  763. CRun * pNext;
  764. for (CRun *pRun = m_RunHead.Next();
  765. !pRun->IsNull();
  766. pRun = pNext)
  767. {
  768. pNext = pRun->Next();
  769. if (pRun->IsIdleTriggered() ||
  770. CompareFileTime(pRun->GetDeadline(), &ftNow) < 0)
  771. {
  772. pRun->UnLink();
  773. //
  774. // If the system needed to stay awake to run this task, decrement
  775. // the thread's wake count. (We know that this is always called
  776. // by the worker thread.)
  777. //
  778. if (pRun->IsFlagSet(TASK_FLAG_SYSTEM_REQUIRED))
  779. {
  780. WrapSetThreadExecutionState(FALSE,
  781. "CIdleRunList::FreeExpiredOrRegenerated");
  782. }
  783. delete pRun;
  784. }
  785. }
  786. }
  787. //+----------------------------------------------------------------------------
  788. //
  789. // Function: WrapSetThreadExecutionStateFn
  790. //
  791. // Synopsis: Wrapper for dynamically loaded function
  792. //
  793. //-----------------------------------------------------------------------------
  794. void WINAPI
  795. WrapSetThreadExecutionStateFn(
  796. BOOL fSystemRequired
  797. #if DBG
  798. , LPCSTR pszDbgMsg // parameter for debug output message
  799. #endif
  800. )
  801. {
  802. DWORD dwCount = (DWORD) (ULONG_PTR)TlsGetValue(g_WakeCountSlot);
  803. if (fSystemRequired)
  804. {
  805. //
  806. // Increment this thread's keep-awake count. If it was zero,
  807. // set the system-required state.
  808. //
  809. schDebugOut((DEB_USER5, "INCREMENTING keep-awake count to %ld: %s\n",
  810. dwCount + 1, pszDbgMsg));
  811. schAssert(dwCount != (DWORD) -1);
  812. dwCount++;
  813. if (dwCount == 1)
  814. {
  815. if (pfnSetThreadExecutionState != NULL)
  816. {
  817. schDebugOut((DEB_USER5, "SETTING sys-required state\n"));
  818. (pfnSetThreadExecutionState)(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
  819. }
  820. }
  821. }
  822. else
  823. {
  824. //
  825. // Decrement this thread's keep-awake count. If it becomes zero,
  826. // reset the system-required state.
  827. //
  828. schDebugOut((DEB_USER5, "DECREMENTING keep-awake count to %ld: %s\n",
  829. dwCount - 1, pszDbgMsg));
  830. schAssert(dwCount != 0);
  831. dwCount--;
  832. if (dwCount == 0)
  833. {
  834. if (pfnSetThreadExecutionState != NULL)
  835. {
  836. schDebugOut((DEB_USER5, "RESETTING sys-required state\n"));
  837. (pfnSetThreadExecutionState)(ES_CONTINUOUS);
  838. }
  839. }
  840. }
  841. if (!TlsSetValue(g_WakeCountSlot, UlongToPtr(dwCount)))
  842. {
  843. ERR_OUT("TlsSetValue", GetLastError());
  844. }
  845. }
  846. //+----------------------------------------------------------------------------
  847. //
  848. // Function: InitThreadWakeCount
  849. //
  850. // Synopsis: Initialize this thread's keep-awake count.
  851. //
  852. //-----------------------------------------------------------------------------
  853. void
  854. InitThreadWakeCount()
  855. {
  856. schDebugOut((DEB_USER5, "INITIALIZING keep-awake count to 0\n"));
  857. if (!TlsSetValue(g_WakeCountSlot, 0))
  858. {
  859. ERR_OUT("TlsSetValue", GetLastError());
  860. }
  861. }