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.

493 lines
14 KiB

  1. /***************************************************************************\
  2. *
  3. * File: Scheduler.cpp
  4. *
  5. * Description:
  6. * Scheduler.cpp maintains a collection of timers that are created and used
  7. * by the application for notifications.
  8. *
  9. *
  10. * History:
  11. * 1/18/2000: JStall: Created
  12. *
  13. * Copyright (C) 2000 by Microsoft Corporation. All rights reserved.
  14. *
  15. \***************************************************************************/
  16. #include "stdafx.h"
  17. #include "Motion.h"
  18. #include "Scheduler.h"
  19. #include "Action.h"
  20. #include "Context.h"
  21. /***************************************************************************\
  22. *****************************************************************************
  23. *
  24. * class Scheduler
  25. *
  26. *****************************************************************************
  27. \***************************************************************************/
  28. //---------------------------------------------------------------------------
  29. Scheduler::Scheduler()
  30. {
  31. #if DBG
  32. m_DEBUG_fLocked = FALSE;
  33. #endif // DBG
  34. }
  35. //---------------------------------------------------------------------------
  36. Scheduler::~Scheduler()
  37. {
  38. AssertMsg(m_fShutdown, "Scheduler must be manually shutdown before destruction");
  39. }
  40. /***************************************************************************\
  41. *
  42. * Scheduler::xwPreDestroy
  43. *
  44. * xwPreDestroy() prepares the Scheduler for destruction while it is still
  45. * valid to callback into the application.
  46. *
  47. \***************************************************************************/
  48. void
  49. Scheduler::xwPreDestroy()
  50. {
  51. m_fShutdown = TRUE;
  52. xwRemoveAllActions();
  53. }
  54. /***************************************************************************\
  55. *
  56. * Scheduler::AddAction
  57. *
  58. * AddAction() creates and adds a new Action, using the specified information.
  59. *
  60. \***************************************************************************/
  61. Action *
  62. Scheduler::AddAction(
  63. IN const GMA_ACTION * pma) // Action information
  64. {
  65. //
  66. // Check if shutting down and don't allow any new Actions to be created.
  67. //
  68. if (m_fShutdown) {
  69. return NULL;
  70. }
  71. Action * pact;
  72. Enter();
  73. //
  74. // Determine which list to add the action to and add it.
  75. //
  76. GList<Action> * plstParent = NULL;
  77. bool fPresent = IsPresentTime(pma->flDelay);
  78. if (fPresent) {
  79. plstParent = &m_lstacPresent;
  80. } else {
  81. plstParent = &m_lstacFuture;
  82. }
  83. DWORD dwCurTick = GetTickCount();
  84. pact = Action::Build(plstParent, pma, dwCurTick, fPresent);
  85. if (pact == NULL) {
  86. goto Exit;
  87. }
  88. plstParent->Add(pact);
  89. //
  90. // Returning out the Action, so we need to lock the HACTION that we are
  91. // giving back.
  92. //
  93. pact->Lock();
  94. Exit:
  95. Leave();
  96. return pact;
  97. }
  98. /***************************************************************************\
  99. *
  100. * Scheduler::xwRemoveAllActions
  101. *
  102. * xwRemoveAllActions() removes all Actions still "owned" by the Scheduler.
  103. *
  104. \***************************************************************************/
  105. void
  106. Scheduler::xwRemoveAllActions()
  107. {
  108. GArrayF<Action *> aracFire;
  109. //
  110. // NOTE: We can not fire any notifications while inside the Scheduler lock,
  111. // or the Scheduler could get messed up. Instead, we need to remember all
  112. // of the Actions to fire, and then fire them when we leave the lock.
  113. //
  114. Enter();
  115. int cItems = m_lstacPresent.GetSize() + m_lstacFuture.GetSize();
  116. aracFire.SetSize(cItems);
  117. int idxAdd = 0;
  118. while (!m_lstacPresent.IsEmpty()) {
  119. Action * pact = m_lstacPresent.UnlinkHead();
  120. VerifyMsg(pact->xwUnlock(), "Action should still be valid");
  121. pact->SetParent(NULL);
  122. aracFire[idxAdd++] = pact;
  123. }
  124. while (!m_lstacFuture.IsEmpty()) {
  125. Action * pact = m_lstacFuture.UnlinkHead();
  126. VerifyMsg(pact->xwUnlock(), "Action should still be valid");
  127. pact->SetParent(NULL);
  128. aracFire[idxAdd++] = pact;
  129. }
  130. AssertMsg(idxAdd == cItems, "Should have added all items");
  131. Leave();
  132. //
  133. // Don't fire from processing when removing the Actions. Instead, only
  134. // have the destructors fire when the Action finally gets cleaned up.
  135. //
  136. xwFireNL(aracFire, FALSE);
  137. }
  138. /***************************************************************************\
  139. *
  140. * Scheduler::xwProcessActionsNL
  141. *
  142. * xwProcessActionsNL() processes the Actions for one iteration, moving
  143. * between queues and firing notifications.
  144. *
  145. \***************************************************************************/
  146. DWORD
  147. Scheduler::xwProcessActionsNL()
  148. {
  149. DWORD dwCurTime = ::GetTickCount();
  150. //
  151. // NOTE: We need to leave the lock when calling back as part of the
  152. // Action::Fire() mechanism. To accomplish this, we store up all of the
  153. // Actions to callback during processing and callback after leaving the
  154. // lock.
  155. //
  156. // NOTE: We can not use a GList to store the actions to fire because they
  157. // are already stored in a list and the ListNode's would conflict. So,
  158. // we use an Array instead.
  159. //
  160. GArrayF<Action *> aracFire;
  161. Enter();
  162. Thread * pCurThread = GetThread();
  163. BOOL fFinishedPeriod, fFire;
  164. //
  165. // Go through and pre-process all future actions. If a future actions
  166. // time has come up, move it to the present actions list.
  167. //
  168. Action * pactCur = m_lstacFuture.GetHead();
  169. while (pactCur != NULL) {
  170. Action * pactNext = pactCur->GetNext();
  171. if (pactCur->GetThread() == pCurThread) {
  172. AssertMsg(!pactCur->IsPresent(), "Ensure action not yet present");
  173. pactCur->Process(dwCurTime, &fFinishedPeriod, &fFire);
  174. AssertMsg(! fFire, "Should not fire future Actions");
  175. if (fFinishedPeriod) {
  176. //
  177. // Action has reached the present
  178. //
  179. m_lstacFuture.Unlink(pactCur);
  180. pactCur->SetPresent(TRUE);
  181. pactCur->ResetPresent(dwCurTime);
  182. pactCur->SetParent(&m_lstacPresent);
  183. m_lstacPresent.Add(pactCur);
  184. }
  185. }
  186. pactCur = pactNext;
  187. }
  188. //
  189. // Go through and process all present actions
  190. //
  191. pactCur = m_lstacPresent.GetHead();
  192. while (pactCur != NULL) {
  193. Action * pactNext = pactCur->GetNext();
  194. if (pactCur->GetThread() == pCurThread) {
  195. pactCur->Process(dwCurTime, &fFinishedPeriod, &fFire);
  196. if (fFire) {
  197. //
  198. // The Action should be fired, so lock it and add it to the
  199. // delayed set of Actions to fire. It is important to lock
  200. // it if the Action is finished so that it doesn't get
  201. // destroyed.
  202. //
  203. pactCur->Lock();
  204. if (aracFire.Add(pactCur) < 0) {
  205. // TODO: Unable to add the Action. This is pretty bad.
  206. // Need to figure out how to handle this situation,
  207. // especially if fFinishedPeriod or the app may leak resources.
  208. }
  209. }
  210. if (fFinishedPeriod) {
  211. pactCur->SetParent(NULL);
  212. m_lstacPresent.Unlink(pactCur);
  213. pactCur->EndPeriod();
  214. //
  215. // The action has finished this round. If it is not periodic, it
  216. // will be destroyed during its callback. If it is periodic,
  217. // need to re-add it to the correct (present or future) list.
  218. //
  219. if (pactCur->IsComplete()) {
  220. pactCur->MarkDelete(TRUE);
  221. VerifyMsg(pactCur->xwUnlock(), "Should still have HANDLE lock");
  222. } else {
  223. GList<Action> * plstParent = NULL;
  224. float flWait = pactCur->GetStartDelay();
  225. BOOL fPresent = IsPresentTime(flWait);
  226. if (fPresent) {
  227. pactCur->ResetPresent(dwCurTime);
  228. plstParent = &m_lstacPresent;
  229. } else {
  230. pactCur->ResetFuture(dwCurTime, FALSE);
  231. plstParent = &m_lstacFuture;
  232. }
  233. pactCur->SetPresent(fPresent);
  234. pactCur->SetParent(plstParent);
  235. plstParent->Add(pactCur);
  236. }
  237. }
  238. }
  239. pactCur = pactNext;
  240. }
  241. //
  242. // Now that everything has been determined, determine how long until Actions
  243. // need to be processed again.
  244. //
  245. // NOTE: To keep Actions from overwhelming CPU and giving other tasks some
  246. // time to accumulate and process, we normally limit the granularity to
  247. // 10 ms. We actually should allow Actions to specify there own granularity
  248. // and provide a default, probably of 10 ms for continuous Actions.
  249. //
  250. // NOTE: Is is very important that this number is not too high, because it
  251. // will severly limit the framerate to 1000 / delay. After doing
  252. // significant profiling work, 10 ms was found to be ideal which gives an
  253. // upper bound of about 100 fps.
  254. //
  255. DWORD dwTimeOut = INFINITE;
  256. if (m_lstacPresent.IsEmpty()) {
  257. //
  258. // There are no present Actions, so check over the future Actions to
  259. // determine when the next one executes.
  260. //
  261. Action * pactCur = m_lstacFuture.GetHead();
  262. while (pactCur != NULL) {
  263. Action * pactNext = pactCur->GetNext();
  264. if (pactCur->GetThread() == pCurThread) {
  265. AssertMsg(!pactCur->IsPresent(), "Ensure action not yet present");
  266. DWORD dwNewTimeOut = pactCur->GetIdleTimeOut(dwCurTime);
  267. AssertMsg(dwTimeOut > 0, "If Action has no TimeOut, should already be present.");
  268. if (dwNewTimeOut < dwTimeOut) {
  269. dwTimeOut = dwNewTimeOut;
  270. }
  271. }
  272. pactCur = pactNext;
  273. }
  274. } else {
  275. //
  276. // There are present Actions, so query their PauseTimeOut().
  277. //
  278. Action * pactCur = m_lstacPresent.GetHead();
  279. while (pactCur != NULL) {
  280. Action * pactNext = pactCur->GetNext();
  281. DWORD dwNewTimeout = pactCur->GetPauseTimeOut();
  282. if (dwNewTimeout < dwTimeOut) {
  283. dwTimeOut = dwNewTimeout;
  284. if (dwTimeOut == 0) {
  285. break;
  286. }
  287. }
  288. pactCur = pactNext;
  289. }
  290. }
  291. Leave();
  292. xwFireNL(aracFire, TRUE);
  293. //
  294. // After actually execution the Actions, compute how much time to wait until
  295. // processing the next batch. We want to subtract the time we spent
  296. // processing the Actions, since if we setup timers on 50 ms intervals and
  297. // the processing takes 20 ms, we should only wait 30 ms.
  298. //
  299. // NOTE we need to do this AFTER calling xwFireNL(), since this fires the
  300. // actual notifications and does the processing. If we compute before this,
  301. // the majority of the work will not be included.
  302. //
  303. DWORD dwOldCurTime = dwCurTime;
  304. dwCurTime = ::GetTickCount(); // Update the current time
  305. DWORD dwProcessTime = ComputeTickDelta(dwCurTime, dwOldCurTime);
  306. if (dwProcessTime < dwTimeOut) {
  307. dwTimeOut -= dwProcessTime;
  308. } else {
  309. dwTimeOut = 0;
  310. }
  311. return dwTimeOut;
  312. }
  313. //---------------------------------------------------------------------------
  314. #if DBG
  315. void
  316. DEBUG_CheckValid(const GArrayF<Action *> & aracFire, int idxStart)
  317. {
  318. int cActions = aracFire.GetSize();
  319. for (int i = idxStart; i < cActions; i++) {
  320. DWORD * pdw = (DWORD *) aracFire[i];
  321. AssertMsg(*pdw != 0xfeeefeee, "Should still be valid");
  322. }
  323. }
  324. #endif // DBG
  325. /***************************************************************************\
  326. *
  327. * Scheduler::xwFireNL
  328. *
  329. * xwFireNL() fires notifications for the specified Actions, updating Action
  330. * state as it is fired.
  331. *
  332. \***************************************************************************/
  333. void
  334. Scheduler::xwFireNL(
  335. IN GArrayF<Action *> & aracFire, // Actions to notify
  336. IN BOOL fFire // "Fire" the notification (or just update)
  337. ) const
  338. {
  339. #if DBG
  340. //
  341. // Check that each Action is only in the list once.
  342. //
  343. {
  344. int cActions = aracFire.GetSize();
  345. for (int i = 0; i < cActions; i++) {
  346. aracFire[i]->DEBUG_MarkInFire(TRUE);
  347. for (int j = i + 1; j < cActions; j++) {
  348. AssertMsg(aracFire[i] != aracFire[j], "Should only be in once");
  349. }
  350. }
  351. DEBUG_CheckValid(aracFire, 0);
  352. }
  353. #endif // DBG
  354. //
  355. // Outside of the lock, so can fire the callbacks.
  356. //
  357. // NOTE: We may actually be locked by a different thread, but that's okay.
  358. //
  359. int cActions = aracFire.GetSize();
  360. for (int idx = 0; idx < cActions; idx++) {
  361. Action * pact = aracFire[idx];
  362. #if DBG
  363. DEBUG_CheckValid(aracFire, idx);
  364. #endif // DBG
  365. if (fFire) {
  366. pact->xwFireNL();
  367. }
  368. #if DBG
  369. aracFire[idx]->DEBUG_MarkInFire(FALSE);
  370. #endif // DBG
  371. pact->xwUnlock();
  372. #if DBG
  373. aracFire[idx] = NULL;
  374. #endif // DBG
  375. }
  376. //
  377. // NOTE: Since we pass in a Action * array, we don't need to worry about
  378. // the destructors getting called and the Actions being incorrectly
  379. // destroyed.
  380. //
  381. }
  382. /***************************************************************************\
  383. *****************************************************************************
  384. *
  385. * Global Functions
  386. *
  387. *****************************************************************************
  388. \***************************************************************************/
  389. //---------------------------------------------------------------------------
  390. HACTION
  391. GdCreateAction(const GMA_ACTION * pma)
  392. {
  393. return (HACTION) GetHandle(GetMotionSC()->GetScheduler()->AddAction(pma));
  394. }