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.

584 lines
13 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. timerobj.c
  5. Abstract:
  6. This module implements the kernel timer object. Functions are
  7. provided to initialize, read, set, and cancel timer objects.
  8. Author:
  9. David N. Cutler (davec) 2-Mar-1989
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "ki.h"
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text(PAGELK, KeQueryTimerDueTime)
  17. #endif
  18. //
  19. // The following assert macro is used to check that an input timer is
  20. // really a ktimer and not something else, like deallocated pool.
  21. //
  22. #define ASSERT_TIMER(E) { \
  23. ASSERT(((E)->Header.Type == TimerNotificationObject) || \
  24. ((E)->Header.Type == TimerSynchronizationObject)); \
  25. }
  26. VOID
  27. KeInitializeTimer (
  28. IN PKTIMER Timer
  29. )
  30. /*++
  31. Routine Description:
  32. This function initializes a kernel timer object.
  33. Arguments:
  34. Timer - Supplies a pointer to a dispatcher object of type timer.
  35. Return Value:
  36. None.
  37. --*/
  38. {
  39. //
  40. // Initialize extended timer object with a type of notification and a
  41. // period of zero.
  42. //
  43. KeInitializeTimerEx(Timer, NotificationTimer);
  44. return;
  45. }
  46. VOID
  47. KeInitializeTimerEx (
  48. IN PKTIMER Timer,
  49. IN TIMER_TYPE Type
  50. )
  51. /*++
  52. Routine Description:
  53. This function initializes an extended kernel timer object.
  54. Arguments:
  55. Timer - Supplies a pointer to a dispatcher object of type timer.
  56. Type - Supplies the type of timer object; NotificationTimer or
  57. SynchronizationTimer;
  58. Return Value:
  59. None.
  60. --*/
  61. {
  62. //
  63. // Initialize standard dispatcher object header and set initial
  64. // state of timer.
  65. //
  66. Timer->Header.Type = (UCHAR)(TimerNotificationObject + Type);
  67. Timer->Header.Inserted = FALSE;
  68. Timer->Header.Size = sizeof(KTIMER) / sizeof(LONG);
  69. Timer->Header.SignalState = FALSE;
  70. #if DBG
  71. Timer->TimerListEntry.Flink = NULL;
  72. Timer->TimerListEntry.Blink = NULL;
  73. #endif
  74. InitializeListHead(&Timer->Header.WaitListHead);
  75. Timer->DueTime.QuadPart = 0;
  76. Timer->Period = 0;
  77. return;
  78. }
  79. VOID
  80. KeClearTimer (
  81. IN PKTIMER Timer
  82. )
  83. /*++
  84. Routine Description:
  85. This function clears the signal state of an timer object.
  86. Arguments:
  87. Event - Supplies a pointer to a dispatcher object of type timer.
  88. Return Value:
  89. None.
  90. --*/
  91. {
  92. ASSERT_TIMER(Timer);
  93. //
  94. // Clear signal state of timer object.
  95. //
  96. Timer->Header.SignalState = 0;
  97. return;
  98. }
  99. BOOLEAN
  100. KeCancelTimer (
  101. IN PKTIMER Timer
  102. )
  103. /*++
  104. Routine Description:
  105. This function cancels a timer that was previously set to expire at
  106. a specified time. If the timer is not currently set, then no operation
  107. is performed. Canceling a timer does not set the state of the timer to
  108. Signaled.
  109. Arguments:
  110. Timer - Supplies a pointer to a dispatcher object of type timer.
  111. Return Value:
  112. A boolean value of TRUE is returned if the the specified timer was
  113. currently set. Else a value of FALSE is returned.
  114. --*/
  115. {
  116. BOOLEAN Inserted;
  117. KIRQL OldIrql;
  118. ASSERT_TIMER(Timer);
  119. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  120. //
  121. // Raise IRQL to dispatcher level, lock the dispatcher database, and
  122. // capture the timer inserted status. If the timer is currently set,
  123. // then remove it from the timer list.
  124. //
  125. KiLockDispatcherDatabase(&OldIrql);
  126. Inserted = Timer->Header.Inserted;
  127. if (Inserted != FALSE) {
  128. KiRemoveTreeTimer(Timer);
  129. }
  130. //
  131. // Unlock the dispatcher database, lower IRQL to its previous value, and
  132. // return boolean value that signifies whether the timer was set of not.
  133. //
  134. KiUnlockDispatcherDatabase(OldIrql);
  135. return Inserted;
  136. }
  137. BOOLEAN
  138. KeReadStateTimer (
  139. IN PKTIMER Timer
  140. )
  141. /*++
  142. Routine Description:
  143. This function reads the current signal state of a timer object.
  144. Arguments:
  145. Timer - Supplies a pointer to a dispatcher object of type timer.
  146. Return Value:
  147. The current signal state of the timer object.
  148. --*/
  149. {
  150. ASSERT_TIMER(Timer);
  151. //
  152. // Return current signal state of timer object.
  153. //
  154. return (BOOLEAN)Timer->Header.SignalState;
  155. }
  156. BOOLEAN
  157. KeSetTimer (
  158. IN PKTIMER Timer,
  159. IN LARGE_INTEGER DueTime,
  160. IN PKDPC Dpc OPTIONAL
  161. )
  162. /*++
  163. Routine Description:
  164. This function sets a timer to expire at a specified time. If the timer is
  165. already set, then it is implicitly canceled before it is set to expire at
  166. the specified time. Setting a timer causes its due time to be computed,
  167. its state to be set to Not-Signaled, and the timer object itself to be
  168. inserted in the timer list.
  169. Arguments:
  170. Timer - Supplies a pointer to a dispatcher object of type timer.
  171. DueTime - Supplies an absolute or relative time at which the timer
  172. is to expire.
  173. Dpc - Supplies an optional pointer to a control object of type DPC.
  174. Return Value:
  175. A boolean value of TRUE is returned if the the specified timer was
  176. currently set. Else a value of FALSE is returned.
  177. --*/
  178. {
  179. //
  180. // Set the timer with a period of zero.
  181. //
  182. return KeSetTimerEx(Timer, DueTime, 0, Dpc);
  183. }
  184. BOOLEAN
  185. KeSetTimerEx (
  186. IN PKTIMER Timer,
  187. IN LARGE_INTEGER DueTime,
  188. IN LONG Period OPTIONAL,
  189. IN PKDPC Dpc OPTIONAL
  190. )
  191. /*++
  192. Routine Description:
  193. This function sets a timer to expire at a specified time. If the timer is
  194. already set, then it is implicitly canceled before it is set to expire at
  195. the specified time. Setting a timer causes its due time to be computed,
  196. its state to be set to Not-Signaled, and the timer object itself to be
  197. inserted in the timer list.
  198. Arguments:
  199. Timer - Supplies a pointer to a dispatcher object of type timer.
  200. DueTime - Supplies an absolute or relative time at which the timer
  201. is to expire.
  202. Period - Supplies an optional period for the timer in milliseconds.
  203. Dpc - Supplies an optional pointer to a control object of type DPC.
  204. Return Value:
  205. A boolean value of TRUE is returned if the the specified timer was
  206. currently set. Else a value of FALSE is returned.
  207. --*/
  208. {
  209. BOOLEAN Inserted;
  210. LARGE_INTEGER Interval;
  211. KIRQL OldIrql;
  212. LARGE_INTEGER SystemTime;
  213. ASSERT_TIMER(Timer);
  214. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  215. //
  216. // Raise IRQL to dispatcher level and lock dispatcher database.
  217. //
  218. KiLockDispatcherDatabase(&OldIrql);
  219. //
  220. // Capture the timer inserted status and if the timer is currently
  221. // set, then remove it from the timer list.
  222. //
  223. Inserted = Timer->Header.Inserted;
  224. if (Inserted != FALSE) {
  225. KiRemoveTreeTimer(Timer);
  226. }
  227. //
  228. // Clear the signal state, set the period, set the DPC address, and
  229. // insert the timer in the timer tree. If the timer is not inserted
  230. // in the timer tree, then it has already expired and as many waiters
  231. // as possible should be continued, and a DPC, if specified should be
  232. // queued.
  233. //
  234. // N.B. The signal state must be cleared in case the period is not
  235. // zero.
  236. //
  237. Timer->Header.SignalState = FALSE;
  238. Timer->Dpc = Dpc;
  239. Timer->Period = Period;
  240. if (KiInsertTreeTimer((PRKTIMER)Timer, DueTime) == FALSE) {
  241. if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) {
  242. KiWaitTest(Timer, TIMER_EXPIRE_INCREMENT);
  243. }
  244. //
  245. // If a DPC is specfied, then call the DPC routine.
  246. //
  247. if (Dpc != NULL) {
  248. KiQuerySystemTime(&SystemTime);
  249. KeInsertQueueDpc(Timer->Dpc,
  250. ULongToPtr(SystemTime.LowPart),
  251. ULongToPtr(SystemTime.HighPart));
  252. }
  253. //
  254. // If the timer is periodic, then compute the next interval time
  255. // and reinsert the timer in the timer tree.
  256. //
  257. // N.B. Even though the timer insertion is relative, it can still
  258. // fail if the period of the timer elapses in between computing
  259. // the time and inserting the timer. If this happens, then the
  260. // the insertion is retried.
  261. //
  262. if (Period != 0) {
  263. Interval.QuadPart = Int32x32To64(Timer->Period, - 10 * 1000);
  264. do {
  265. } while (KiInsertTreeTimer(Timer, Interval) == FALSE);
  266. }
  267. }
  268. //
  269. // Unlock the dispatcher database and lower IRQL to its previous
  270. // value.
  271. //
  272. KiUnlockDispatcherDatabase(OldIrql);
  273. //
  274. // Return boolean value that signifies whether the timer was set of
  275. // not.
  276. //
  277. return Inserted;
  278. }
  279. ULONGLONG
  280. KeQueryTimerDueTime (
  281. IN PKTIMER Timer
  282. )
  283. /*++
  284. Routine Description:
  285. This function returns the InterruptTime at which the timer is
  286. pending. 0 is returned if the timer is not pending.
  287. N.B. This function may only be called by the system sleep code.
  288. Arguments:
  289. Timer - Supplies a pointer to a dispatcher object of type timer.
  290. Return Value:
  291. Returns the amount of time remaining on the timer, or 0 if the
  292. timer is not pending.
  293. --*/
  294. {
  295. KIRQL OldIrql;
  296. ULONGLONG DueTime;
  297. ASSERT_TIMER(Timer);
  298. DueTime = 0;
  299. //
  300. // Raise IRQL to dispatcher level and lock dispatcher database.
  301. //
  302. KiLockDispatcherDatabase(&OldIrql);
  303. //
  304. // If the timer is currently pending, compute its due time
  305. //
  306. if (Timer->Header.Inserted) {
  307. DueTime = Timer->DueTime.QuadPart;
  308. }
  309. //
  310. // Unlock the dispatcher database and lower IRQL to its previous
  311. // value, and return the due time
  312. //
  313. KiUnlockDispatcherDatabase(OldIrql);
  314. return DueTime;
  315. }
  316. VOID
  317. KeCheckForTimer(
  318. IN PVOID BlockStart,
  319. IN SIZE_T BlockSize
  320. )
  321. /*++
  322. Routine Description:
  323. This function is used for debugging by checking all timers
  324. to see if any is in the memory block passed. If so, the
  325. system bugchecks.
  326. Arguments:
  327. BlockStart - Base address to check for timer.
  328. BlockSize - Size (in bytes) to check in the memory block.
  329. Return Value:
  330. None.
  331. --*/
  332. {
  333. ULONG Index;
  334. PLIST_ENTRY ListHead;
  335. PLIST_ENTRY NextEntry;
  336. KIRQL OldIrql;
  337. PKTIMER Timer;
  338. PUCHAR Address;
  339. PUCHAR Start;
  340. PUCHAR End;
  341. //
  342. // Compute the ending memory location.
  343. //
  344. Start = (PUCHAR)BlockStart;
  345. End = Start + BlockSize;
  346. //
  347. // Raise IRQL to dispatcher level and lock dispatcher database.
  348. //
  349. KiLockDispatcherDatabase(&OldIrql);
  350. //
  351. // Run the entire timer database and check for any timers in
  352. // the memory block
  353. //
  354. Index = 0;
  355. do {
  356. ListHead = &KiTimerTableListHead[Index];
  357. NextEntry = ListHead->Flink;
  358. while (NextEntry != ListHead) {
  359. Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
  360. Address = (PUCHAR)Timer;
  361. NextEntry = NextEntry->Flink;
  362. //
  363. // Check this timer object is not in the range.
  364. // In each of the following, we check that the object
  365. // does not overlap the range, for example, if the timer
  366. // object (in this first check), starts one dword before
  367. // the range being checked, we have an overlap and should
  368. // stop.
  369. //
  370. if ((Address > (Start - sizeof(KTIMER))) &&
  371. (Address < End)) {
  372. KeBugCheckEx(TIMER_OR_DPC_INVALID,
  373. 0x0,
  374. (ULONG_PTR)Address,
  375. (ULONG_PTR)Start,
  376. (ULONG_PTR)End);
  377. }
  378. if (Timer->Dpc) {
  379. //
  380. // Check the timer's DPC object isn't in the range.
  381. //
  382. Address = (PUCHAR)Timer->Dpc;
  383. if ((Address > (Start - sizeof(KDPC))) &&
  384. (Address < End)) {
  385. KeBugCheckEx(TIMER_OR_DPC_INVALID,
  386. 0x1,
  387. (ULONG_PTR)Address,
  388. (ULONG_PTR)Start,
  389. (ULONG_PTR)End);
  390. }
  391. //
  392. // Check the timer's DPC routine is not in the range.
  393. //
  394. Address = (PUCHAR)(ULONG_PTR) Timer->Dpc->DeferredRoutine;
  395. if (Address >= Start && Address < End) {
  396. KeBugCheckEx(TIMER_OR_DPC_INVALID,
  397. 0x2,
  398. (ULONG_PTR)Address,
  399. (ULONG_PTR)Start,
  400. (ULONG_PTR)End);
  401. }
  402. }
  403. }
  404. Index += 1;
  405. } while(Index < TIMER_TABLE_SIZE);
  406. //
  407. // Unlock the dispatcher database and lower IRQL to its previous value
  408. //
  409. KiUnlockDispatcherDatabase(OldIrql);
  410. }