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.

352 lines
6.8 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. Domain Name System (DNS) Server
  7. Wrap proof timer routines.
  8. The purpose of this module is to create a timer function which
  9. returns a time in seconds and eliminates all timer wrapping issues.
  10. These routines are non-DNS specific and may be picked up
  11. cleanly by any module.
  12. For DNS the added instructions are well worth the cost in that it
  13. eliminates any issue involving cleaning packet queues or resetting
  14. cache timeouts when millisecond timer (GetCurrentTime) wraps.
  15. Author:
  16. Jim Gilroy (jamesg) 9-Sep-1995
  17. Revision History:
  18. --*/
  19. #include "local.h"
  20. // Note: this modules requires only windows.h.
  21. // local.h is included only to allow precompiled header
  22. #include <windows.h>
  23. #if 1
  24. //
  25. // GetTickCount() timer routines
  26. //
  27. //
  28. // Timer globals
  29. //
  30. BOOL g_InitializedTimerCs = FALSE;
  31. BOOL g_TimerInitInProgress = FALSE;
  32. CRITICAL_SECTION csTimerWrap;
  33. DWORD g_WrapTime = 0;
  34. DWORD g_PreviousTopBit = 0;
  35. VOID
  36. Dns_InitializeSecondsTimer(
  37. VOID
  38. )
  39. /*++
  40. Routine Description:
  41. Initialize DNS timer.
  42. This will be done automatically, but allow caller to do it explicitly.
  43. Arguments:
  44. None.
  45. Return Value:
  46. None.
  47. --*/
  48. {
  49. //
  50. // protect CS init with interlock
  51. // - first thread through does CS init
  52. // - any others racing, are not released until init
  53. // completes
  54. //
  55. if ( !g_InitializedTimerCs )
  56. {
  57. if ( InterlockedIncrement( &g_TimerInitInProgress ) == 1 )
  58. {
  59. InitializeCriticalSection( &csTimerWrap );
  60. g_InitializedTimerCs = TRUE;
  61. }
  62. else
  63. {
  64. while ( !g_InitializedTimerCs )
  65. {
  66. Sleep( 10 );
  67. }
  68. }
  69. }
  70. }
  71. DWORD
  72. Dns_GetCurrentTimeInSeconds(
  73. VOID
  74. )
  75. /*++
  76. Routine Description:
  77. Get current time in seconds.
  78. Arguments:
  79. None.
  80. Return Value:
  81. Time since boot in seconds.
  82. --*/
  83. {
  84. DWORD currentTime;
  85. DWORD topBit;
  86. DWORD preWrapTime;
  87. DWORD postWrapTime;
  88. //
  89. // get time
  90. //
  91. // read wrap time on either side so we can detect and handle
  92. // a wrap occuring (handled by another thread) while we are
  93. // in this function
  94. //
  95. preWrapTime = g_WrapTime;
  96. currentTime = GetCurrentTime();
  97. postWrapTime = g_WrapTime;
  98. //
  99. // check for timer wrap
  100. //
  101. // need to detect when timer flips from large to small DWORD;
  102. //
  103. // i first did this by keeping a previous time global, but
  104. // setting this global must also be carefully locked around timer
  105. // wrap to avoid race conditions resulting in double wrap
  106. //
  107. // to avoid locking all the time we can set previous time only
  108. // when it "substantively" changes for our purposes -- this is
  109. // when it changes its top bit; by saving it twice a wrap
  110. // we have enough info to detect the wrap (the change from
  111. // top bit set to clear), yet still only need to lock a few
  112. // times every wrap
  113. //
  114. // algorithm:
  115. // - top bit same as previous => done
  116. // - top bit changed
  117. // - take lock
  118. // - test again
  119. // - no change => no-op
  120. // - changed to top bit set
  121. // - just save new bit setting
  122. // - changed to top bit clear
  123. // - save new bit setting
  124. // - add one cycle to wrap time
  125. //
  126. topBit = currentTime & 0x80000000;
  127. if ( topBit != g_PreviousTopBit )
  128. {
  129. //
  130. // possible wrap or "half-wrap"
  131. //
  132. // not intializing lock until actually need it
  133. // - lock init is MT safe (see above)
  134. //
  135. Dns_InitializeSecondsTimer();
  136. EnterCriticalSection( &csTimerWrap );
  137. //
  138. // timer wrap
  139. // - recheck inequality as another thread might have beaten
  140. // us to the lock and handled wrap already
  141. // - topBit must be clear (time is now low DWORD)
  142. //
  143. if ( topBit != g_PreviousTopBit && topBit == 0 )
  144. {
  145. g_WrapTime += (MAXDWORD / 1000);
  146. }
  147. // reset previous top bit
  148. // - not necessary in equality case, but a no-op
  149. g_PreviousTopBit = topBit;
  150. LeaveCriticalSection( &csTimerWrap );
  151. }
  152. //
  153. // return time
  154. // - current time + any wrap time
  155. // - if pre\post wrap times use topBit to determine which is valid
  156. // - if our time was snapshot right before wrap, use pre time
  157. // - otherwise post time ok
  158. //
  159. // note this is done completely without globals, so no race
  160. //
  161. if ( preWrapTime != postWrapTime )
  162. {
  163. if ( topBit )
  164. {
  165. postWrapTime = preWrapTime;
  166. }
  167. }
  168. return ( currentTime / 1000 + postWrapTime );
  169. }
  170. #else
  171. //
  172. // FILETIME timer routines
  173. //
  174. // Unfortunately these don't work because FILETIME moves
  175. // around when clock reset -- it is not monotonically increasing
  176. //
  177. //
  178. // Timer globals
  179. //
  180. LONGLONG g_TimerBaseTime = 0;
  181. //
  182. // File time timer in 100ns intervals
  183. // (10 million to second)
  184. //
  185. #define FILE_TIME_INTERVALS_IN_SECOND (10000000)
  186. //
  187. // File time base to avoid starting timer at zero
  188. // Give roughly a day to avoid any startup issues.
  189. //
  190. #define FILE_TIME_BASE_OFFSET (1000000000000)
  191. DWORD
  192. Dns_GetCurrentTimeInSeconds(
  193. VOID
  194. )
  195. /*++
  196. Routine Description:
  197. Get current time in seconds.
  198. Time is relative to first call to the timer.
  199. Arguments:
  200. None.
  201. Return Value:
  202. Time since first timer call in seconds.
  203. --*/
  204. {
  205. LONGLONG time64;
  206. GetSystemTimeAsFileTime( (PFILETIME) &time64 );
  207. //
  208. // convert to seconds
  209. // - file time is in 100ns intervals (since Jan 1, 1601)
  210. //
  211. // if first call, save 64-bit base time;
  212. // this allows us to run a DWORD of seconds ~137 years
  213. //
  214. // repeated calls are offset from base time
  215. //
  216. if ( g_TimerBaseTime == 0 )
  217. {
  218. g_TimerBaseTime = time64 - FILE_TIME_BASE_OFFSET;
  219. }
  220. time64 -= g_TimerBaseTime;
  221. time64 = time64 / FILE_TIME_INTERVALS_IN_SECOND;
  222. return (DWORD)time64;
  223. }
  224. VOID
  225. Dns_InitializeSecondsTimer(
  226. VOID
  227. )
  228. /*++
  229. Routine Description:
  230. Initialize DNS timer.
  231. Note, this is not a reset -- it's just backward compatibility
  232. for old timer routines.
  233. Arguments:
  234. None.
  235. Return Value:
  236. None.
  237. --*/
  238. {
  239. //
  240. // call the timer, for uninitialized timer that makes time
  241. // now
  242. Dns_GetCurrentTimeInSeconds();
  243. //
  244. // note if want a timer reset, then zero base, however
  245. // this is NOT MT safe -- thread in function could get
  246. // huge bogus time
  247. //
  248. }
  249. #endif
  250. //
  251. // End of timer.c
  252. //