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.

264 lines
4.8 KiB

  1. /*++
  2. Copyright (c) 1992-1996 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. This file contains the code to manipulate timers.
  7. Author:
  8. Jameel Hyder (jameelh@microsoft.com) July 1996
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include <precomp.h>
  14. #define _FILENUM_ FILENUM_TIMER
  15. VOID
  16. ArpSTimerEnqueue(
  17. IN PINTF pIntF,
  18. IN PTIMER pTimer
  19. )
  20. /*++
  21. Routine Description:
  22. The timer events are maintained as a list which the timer thread wakes up,
  23. it looks at every timer tick. The list is maintained in such a way that
  24. only the head of the list needs to be updated every tick i.e. the entire
  25. list is never scanned. The way this is achieved is by keeping delta times
  26. relative to the previous entry.
  27. Every timer tick, the relative time at the head of the list is decremented.
  28. When that goes to ZERO, the head of the list is unlinked and dispatched.
  29. To give an example, we have the following events queued at time slots
  30. X Schedule A after 10 ticks.
  31. X+3 Schedule B after 5 ticks.
  32. X+5 Schedule C after 4 ticks.
  33. X+8 Schedule D after 6 ticks.
  34. So A will schedule at X+10, B at X+8 (X+3+5), C at X+9 (X+5+4) and
  35. D at X+14 (X+8+6).
  36. The above example covers all the situations.
  37. - NULL List.
  38. - Inserting at head of list.
  39. - Inserting in the middle of the list.
  40. - Appending to the list tail.
  41. The list will look as follows.
  42. BEFORE AFTER
  43. ------ -----
  44. X Head -->| Head -> A(10) ->|
  45. A(10)
  46. X+3 Head -> A(7) ->| Head -> B(5) -> A(2) ->|
  47. B(5)
  48. X+5 Head -> B(3) -> A(2) ->| Head -> B(3) -> C(1) -> A(1) ->|
  49. C(4)
  50. X+8 Head -> C(1) -> A(1) ->| Head -> C(1) -> A(1) -> D(4) ->|
  51. D(6)
  52. The granularity is one tick. THIS MUST BE CALLED WITH THE TIMER LOCK HELD.
  53. Arguments:
  54. Return Value:
  55. --*/
  56. {
  57. PTIMER pList, *ppList;
  58. USHORT DeltaTime = pTimer->AbsTime;
  59. #if DBG
  60. if (pTimer->Routine == (TIMER_ROUTINE)NULL)
  61. {
  62. DBGPRINT(DBG_LEVEL_ERROR,
  63. ("TimerEnqueue: pIntF %x, pTimer %x, NULL Routine!\n",
  64. pIntF, pTimer));
  65. DbgBreakPoint();
  66. }
  67. #endif // DBG
  68. DBGPRINT(DBG_LEVEL_INFO,
  69. ("ArpSTimerEnqueue: Entered for pTimer %lx\n", pTimer));
  70. // The DeltaTime is adjusted in every pass of the loop to reflect the
  71. // time after the previous entry that the new entry will schedule.
  72. for (ppList = &pIntF->ArpTimer;
  73. (pList = *ppList) != NULL;
  74. ppList = &pList->Next)
  75. {
  76. if (DeltaTime <= pList->RelDelta)
  77. {
  78. pList->RelDelta -= DeltaTime;
  79. break;
  80. }
  81. DeltaTime -= pList->RelDelta;
  82. }
  83. // Link this in the chain
  84. pTimer->RelDelta = DeltaTime;
  85. pTimer->Next = pList;
  86. pTimer->Prev = ppList;
  87. *ppList = pTimer;
  88. if (pList != NULL)
  89. {
  90. pList->Prev = &pTimer->Next;
  91. }
  92. }
  93. VOID
  94. ArpSTimerCancel(
  95. IN PTIMER pTimer
  96. )
  97. /*++
  98. Routine Description:
  99. Cancel a previously queued timer. Called with the ArpCache mutex held.
  100. Arguments:
  101. Return Value:
  102. --*/
  103. {
  104. DBGPRINT(DBG_LEVEL_INFO,
  105. ("ArpSTimerCancel: Entered for pTimer %lx\n", pTimer));
  106. //
  107. // Unlink it from the list adjusting relative deltas carefully
  108. //
  109. if (pTimer->Next != NULL)
  110. {
  111. pTimer->Next->RelDelta += pTimer->RelDelta;
  112. pTimer->Next->Prev = pTimer->Prev;
  113. }
  114. *(pTimer->Prev) = pTimer->Next;
  115. }
  116. VOID
  117. ArpSTimerThread(
  118. IN PVOID Context
  119. )
  120. /*++
  121. Routine Description:
  122. Handle timer events here.
  123. Arguments:
  124. None
  125. Return Value:
  126. None
  127. --*/
  128. {
  129. PINTF pIntF = (PINTF)Context;
  130. NTSTATUS Status;
  131. LARGE_INTEGER TimeOut;
  132. PTIMER pTimer;
  133. BOOLEAN ReQueue;
  134. ARPS_PAGED_CODE( );
  135. DBGPRINT(DBG_LEVEL_INFO,
  136. ("ArpSTimerThread: Came to life\n"));
  137. TimeOut.QuadPart = TIMER_TICK;
  138. do
  139. {
  140. WAIT_FOR_OBJECT(Status, &pIntF->TimerThreadEvent, &TimeOut);
  141. if (Status == STATUS_SUCCESS)
  142. {
  143. //
  144. // Signalled to quit, do so.
  145. //
  146. break;
  147. }
  148. WAIT_FOR_OBJECT(Status, &pIntF->ArpCacheMutex, NULL);
  149. if ((pTimer = pIntF->ArpTimer) != NULL)
  150. {
  151. //
  152. // Careful here. If two timers fire together - let them !!
  153. //
  154. if (pTimer->RelDelta != 0)
  155. pTimer->RelDelta --;
  156. if (pTimer->RelDelta == 0)
  157. {
  158. pIntF->ArpTimer = pTimer->Next;
  159. if (pIntF->ArpTimer != NULL)
  160. {
  161. pIntF->ArpTimer->Prev = &pIntF->ArpTimer;
  162. }
  163. ReQueue = (*pTimer->Routine)(pIntF, pTimer, FALSE);
  164. if (ReQueue)
  165. {
  166. ArpSTimerEnqueue(pIntF, pTimer);
  167. }
  168. }
  169. }
  170. RELEASE_MUTEX(&pIntF->ArpCacheMutex);
  171. } while (TRUE);
  172. DBGPRINT(DBG_LEVEL_INFO,
  173. ("ArpSTimerThread: terminating\n"));
  174. //
  175. // Now fire all queued timers
  176. //
  177. WAIT_FOR_OBJECT(Status, &pIntF->ArpCacheMutex, NULL);
  178. for (pTimer = pIntF->ArpTimer;
  179. pTimer != NULL;
  180. pTimer = pIntF->ArpTimer)
  181. {
  182. pIntF->ArpTimer = pTimer->Next;
  183. ReQueue = (*pTimer->Routine)(pIntF, pTimer, TRUE);
  184. ASSERT(ReQueue == FALSE);
  185. }
  186. RELEASE_MUTEX(&pIntF->ArpCacheMutex);
  187. DBGPRINT(DBG_LEVEL_INFO,
  188. ("ArpSTimerThread: terminated\n"));
  189. //
  190. // Finally dereference the IntF
  191. //
  192. ArpSDereferenceIntF(pIntF);
  193. }