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.

466 lines
11 KiB

  1. /*
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. atktimer.c
  5. Abstract:
  6. This file implements the timer routines used by the stack.
  7. Author:
  8. Jameel Hyder (jameelh@microsoft.com)
  9. Nikhil Kamkolkar (nikhilk@microsoft.com)
  10. Revision History:
  11. 23 Feb 1993 Initial Version
  12. Notes: Tab stop: 4
  13. --*/
  14. #include <atalk.h>
  15. #pragma hdrstop
  16. #define FILENUM ATKTIMER
  17. // Discardable code after Init time
  18. #ifdef ALLOC_PRAGMA
  19. #pragma alloc_text(INIT, AtalkTimerInit)
  20. #pragma alloc_text(PAGEINIT, AtalkTimerFlushAndStop)
  21. #endif
  22. /*** AtalkTimerInit
  23. *
  24. * Initialize the timer component for the appletalk stack.
  25. */
  26. NTSTATUS
  27. AtalkTimerInit(
  28. VOID
  29. )
  30. {
  31. BOOLEAN TimerStarted;
  32. // Initialize the timer and its associated Dpc and kick it off
  33. KeInitializeEvent(&atalkTimerStopEvent, NotificationEvent, FALSE);
  34. KeInitializeTimer(&atalkTimer);
  35. INITIALIZE_SPIN_LOCK(&atalkTimerLock);
  36. KeInitializeDpc(&atalkTimerDpc, atalkTimerDpcRoutine, NULL);
  37. atalkTimerTick.QuadPart = ATALK_TIMER_TICK;
  38. TimerStarted = KeSetTimer(&atalkTimer,
  39. atalkTimerTick,
  40. &atalkTimerDpc);
  41. ASSERT(!TimerStarted);
  42. return STATUS_SUCCESS;
  43. }
  44. /*** AtalkTimerScheduleEvent
  45. *
  46. * Insert an event in the timer event list. If the list is empty, then
  47. * fire off a timer. The time is specified in ticks. Each tick is currently
  48. * 100ms. It may not be zero or negative. The internal timer also fires at
  49. * 100ms granularity.
  50. */
  51. VOID FASTCALL
  52. AtalkTimerScheduleEvent(
  53. IN PTIMERLIST pList // TimerList to use for queuing
  54. )
  55. {
  56. KIRQL OldIrql;
  57. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
  58. ("AtalkTimerScheduleEvent: pList %lx\n", pList));
  59. ASSERT(VALID_TMR(pList));
  60. ASSERT (pList->tmr_Routine != NULL);
  61. ASSERT (pList->tmr_AbsTime != 0);
  62. if (!atalkTimerStopped)
  63. {
  64. ACQUIRE_SPIN_LOCK(&atalkTimerLock, &OldIrql);
  65. // Enqueue this handler
  66. atalkTimerEnqueue(pList);
  67. RELEASE_SPIN_LOCK(&atalkTimerLock, OldIrql);
  68. }
  69. else
  70. {
  71. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_FATAL,
  72. ("AtalkTimerScheduleEvent: Called after Flush !!\n"));
  73. }
  74. }
  75. /*** atalkTimerDpcRoutine
  76. *
  77. * This is called in at DISPATCH_LEVEL when the timer expires. The entry at
  78. * the head of the list is decremented and if ZERO unlinked and dispatched.
  79. * If the list is non-empty, the timer is fired again.
  80. */
  81. LOCAL VOID
  82. atalkTimerDpcRoutine(
  83. IN PKDPC pKDpc,
  84. IN PVOID pContext,
  85. IN PVOID SystemArgument1,
  86. IN PVOID SystemArgument2
  87. )
  88. {
  89. PTIMERLIST pList;
  90. BOOLEAN TimerStarted;
  91. LONG ReQueue;
  92. pKDpc; pContext; SystemArgument1; SystemArgument2;
  93. if (atalkTimerStopped)
  94. {
  95. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR,
  96. ("atalkTimerDpc: Enetered after Flush !!!\n"));
  97. return;
  98. }
  99. ACQUIRE_SPIN_LOCK_DPC(&atalkTimerLock);
  100. AtalkTimerCurrentTick ++; // Update our relative time
  101. // We should never be here if we have no work to do
  102. if ((atalkTimerList != NULL))
  103. {
  104. // Careful here. If two guys wanna go off together - let them !!
  105. if (atalkTimerList->tmr_RelDelta != 0)
  106. (atalkTimerList->tmr_RelDelta)--;
  107. // Dispatch the entry if it is ready to go
  108. pList = atalkTimerList;
  109. if (pList->tmr_RelDelta == 0)
  110. {
  111. ASSERT(VALID_TMR(pList));
  112. // Unlink from the list
  113. // AtalkUnlinkDouble(pList, tmr_Next, tmr_Prev);
  114. atalkTimerList = pList->tmr_Next;
  115. if (atalkTimerList != NULL)
  116. atalkTimerList->tmr_Prev = &atalkTimerList;
  117. pList->tmr_Queued = FALSE;
  118. pList->tmr_Running = TRUE;
  119. atalkTimerRunning = TRUE;
  120. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
  121. ("atalkTimerDpcRoutine: Dispatching %lx\n", pList->tmr_Routine));
  122. RELEASE_SPIN_LOCK_DPC(&atalkTimerLock);
  123. ReQueue = (*pList->tmr_Routine)(pList, FALSE);
  124. ACQUIRE_SPIN_LOCK_DPC(&atalkTimerLock);
  125. atalkTimerRunning = FALSE;
  126. if (ReQueue != ATALK_TIMER_NO_REQUEUE)
  127. {
  128. ASSERT(VALID_TMR(pList));
  129. pList->tmr_Running = FALSE;
  130. if (pList->tmr_CancelIt)
  131. {
  132. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
  133. ("atalkTimerDpcRoutine: Delayed cancel for %lx\n", pList));
  134. RELEASE_SPIN_LOCK_DPC(&atalkTimerLock);
  135. ReQueue = (*pList->tmr_Routine)(pList, TRUE);
  136. ACQUIRE_SPIN_LOCK_DPC(&atalkTimerLock);
  137. ASSERT(ReQueue == ATALK_TIMER_NO_REQUEUE);
  138. }
  139. else
  140. {
  141. if (ReQueue != ATALK_TIMER_REQUEUE)
  142. pList->tmr_AbsTime = (USHORT)ReQueue;
  143. atalkTimerEnqueue(pList);
  144. }
  145. }
  146. }
  147. }
  148. RELEASE_SPIN_LOCK_DPC(&atalkTimerLock);
  149. if (!atalkTimerStopped)
  150. {
  151. TimerStarted = KeSetTimer(&atalkTimer,
  152. atalkTimerTick,
  153. &atalkTimerDpc);
  154. ASSERT(!TimerStarted);
  155. }
  156. else
  157. {
  158. KeSetEvent(&atalkTimerStopEvent, IO_NETWORK_INCREMENT, FALSE);
  159. }
  160. }
  161. /*** atalkTimerEnqueue
  162. *
  163. * Here is a thesis on the code that follows.
  164. *
  165. * The timer events are maintained as a list which the timer dpc routine
  166. * looks at every timer tick. The list is maintained in such a way that only
  167. * the head of the list needs to be updated every tick i.e. the entire list
  168. * is never scanned. The way this is achieved is by keeping delta times
  169. * relative to the previous entry.
  170. *
  171. * Every timer tick, the relative time at the head of the list is decremented.
  172. * When that goes to ZERO, the head of the list is unlinked and dispatched.
  173. *
  174. * To give an example, we have the following events queued at time slots
  175. * X Schedule A after 10 ticks.
  176. * X+3 Schedule B after 5 ticks.
  177. * X+5 Schedule C after 4 ticks.
  178. * X+8 Schedule D after 6 ticks.
  179. *
  180. * So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and
  181. * D at X+14 (X+8+6).
  182. *
  183. * The above example covers all the situations.
  184. *
  185. * - NULL List.
  186. * - Inserting at head of list.
  187. * - Inserting in the middle of the list.
  188. * - Appending to the list tail.
  189. *
  190. * The list will look as follows.
  191. *
  192. * BEFORE AFTER
  193. * ------ -----
  194. *
  195. * X Head -->| Head -> A(10) ->|
  196. * A(10)
  197. *
  198. * X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->|
  199. * B(5)
  200. *
  201. * X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->|
  202. * C(4)
  203. *
  204. * X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->|
  205. * D(6)
  206. *
  207. * The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD.
  208. */
  209. VOID FASTCALL
  210. atalkTimerEnqueue(
  211. IN PTIMERLIST pListNew
  212. )
  213. {
  214. PTIMERLIST pList, *ppList;
  215. USHORT DeltaTime = pListNew->tmr_AbsTime;
  216. // The DeltaTime is adjusted in every pass of the loop to reflect the
  217. // time after the previous entry that the new entry will schedule.
  218. for (ppList = &atalkTimerList;
  219. (pList = *ppList) != NULL;
  220. ppList = &pList->tmr_Next)
  221. {
  222. ASSERT(VALID_TMR(pList));
  223. if (DeltaTime <= pList->tmr_RelDelta)
  224. {
  225. pList->tmr_RelDelta -= DeltaTime;
  226. break;
  227. }
  228. DeltaTime -= pList->tmr_RelDelta;
  229. }
  230. // Link this in the chain
  231. pListNew->tmr_RelDelta = DeltaTime;
  232. pListNew->tmr_Next = pList;
  233. pListNew->tmr_Prev = ppList;
  234. *ppList = pListNew;
  235. if (pList != NULL)
  236. {
  237. pList->tmr_Prev = &pListNew->tmr_Next;
  238. }
  239. pListNew->tmr_Queued = TRUE;
  240. pListNew->tmr_Cancelled = FALSE;
  241. pListNew->tmr_CancelIt = FALSE;
  242. }
  243. /*** AtalkTimerFlushAndStop
  244. *
  245. * Force all entries in the timer queue to be dispatched immediately. No
  246. * more queue'ing of timer routines is permitted after this. The timer
  247. * essentially shuts down.
  248. */
  249. VOID
  250. AtalkTimerFlushAndStop(
  251. VOID
  252. )
  253. {
  254. PTIMERLIST pList;
  255. LONG ReQueue;
  256. KIRQL OldIrql;
  257. BOOLEAN Wait;
  258. ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
  259. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR,
  260. ("AtalkTimerFlushAndStop: Entered\n"));
  261. KeCancelTimer(&atalkTimer);
  262. // The timer routines assume they are being called at DISPATCH level.
  263. // Raise our Irql for this routine.
  264. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  265. ACQUIRE_SPIN_LOCK_DPC(&atalkTimerLock);
  266. atalkTimerStopped = TRUE;
  267. Wait = atalkTimerRunning;
  268. // Dispatch all entries right away
  269. while (atalkTimerList != NULL)
  270. {
  271. pList = atalkTimerList;
  272. ASSERT(VALID_TMR(pList));
  273. atalkTimerList = pList->tmr_Next;
  274. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
  275. ("atalkTimerFlushAndStop: Dispatching %lx\n",
  276. pList->tmr_Routine));
  277. pList->tmr_Queued = FALSE;
  278. pList->tmr_Running = TRUE;
  279. RELEASE_SPIN_LOCK_DPC(&atalkTimerLock);
  280. ReQueue = (*pList->tmr_Routine)(pList, TRUE);
  281. ASSERT (ReQueue == ATALK_TIMER_NO_REQUEUE);
  282. pList->tmr_Running = FALSE;
  283. ACQUIRE_SPIN_LOCK_DPC(&atalkTimerLock);
  284. }
  285. RELEASE_SPIN_LOCK_DPC(&atalkTimerLock);
  286. KeLowerIrql(OldIrql);
  287. if (Wait)
  288. {
  289. // Wait for any timer events that are currently running. Only an MP issue
  290. KeWaitForSingleObject(&atalkTimerStopEvent,
  291. Executive,
  292. KernelMode,
  293. TRUE,
  294. NULL);
  295. }
  296. }
  297. /*** AtalkTimerCancelEvent
  298. *
  299. * Cancel a previously scheduled timer event, if it hasn't fired already.
  300. */
  301. BOOLEAN FASTCALL
  302. AtalkTimerCancelEvent(
  303. IN PTIMERLIST pList,
  304. IN PDWORD pdwOldState
  305. )
  306. {
  307. KIRQL OldIrql;
  308. BOOLEAN Cancelled = FALSE;
  309. DWORD OldState=ATALK_TIMER_QUEUED;
  310. ACQUIRE_SPIN_LOCK(&atalkTimerLock, &OldIrql);
  311. // If this is not running, unlink it from the list
  312. // adjusting relative deltas carefully
  313. if (pList->tmr_Queued)
  314. {
  315. ASSERT (!(pList->tmr_Running));
  316. OldState = ATALK_TIMER_QUEUED;
  317. if (pList->tmr_Next != NULL)
  318. {
  319. pList->tmr_Next->tmr_RelDelta += pList->tmr_RelDelta;
  320. pList->tmr_Next->tmr_Prev = pList->tmr_Prev;
  321. }
  322. *(pList->tmr_Prev) = pList->tmr_Next;
  323. // pointing to timer being removed? fix it!
  324. if (atalkTimerList == pList)
  325. {
  326. atalkTimerList = pList->tmr_Next;
  327. }
  328. Cancelled = pList->tmr_Cancelled = TRUE;
  329. pList->tmr_Queued = FALSE;
  330. }
  331. else if (pList->tmr_Running)
  332. {
  333. DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR,
  334. ("AtalkTimerCancelEvent: %lx Running, cancel set\n",
  335. pList->tmr_Routine));
  336. pList->tmr_CancelIt = TRUE; // Set to cancel after handler returns.
  337. OldState = ATALK_TIMER_RUNNING;
  338. }
  339. RELEASE_SPIN_LOCK(&atalkTimerLock, OldIrql);
  340. if (pdwOldState)
  341. {
  342. *pdwOldState = OldState;
  343. }
  344. return Cancelled;
  345. }
  346. #if DBG
  347. VOID
  348. AtalkTimerDumpList(
  349. VOID
  350. )
  351. {
  352. PTIMERLIST pList;
  353. ULONG CumTime = 0;
  354. DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
  355. ("TIMER LIST: (Times are in 100ms units)\n"));
  356. DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
  357. ("\tTime(Abs) Time(Rel) Routine Address TimerList\n"));
  358. ACQUIRE_SPIN_LOCK_DPC(&atalkTimerLock);
  359. for (pList = atalkTimerList;
  360. pList != NULL;
  361. pList = pList->tmr_Next)
  362. {
  363. CumTime += pList->tmr_RelDelta;
  364. DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
  365. ("\t %5d %5ld %lx %lx\n",
  366. pList->tmr_AbsTime, CumTime,
  367. pList->tmr_Routine, pList));
  368. }
  369. RELEASE_SPIN_LOCK_DPC(&atalkTimerLock);
  370. }
  371. #endif
  372.