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.

593 lines
14 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. bowtimer.c
  5. Abstract:
  6. This module implements all of the timer related routines for the NT
  7. browser
  8. Author:
  9. Larry Osterman (LarryO) 21-Jun-1990
  10. Revision History:
  11. 21-Jun-1990 LarryO
  12. Created
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. BOOL bEnableExceptionBreakpoint = FALSE;
  17. VOID
  18. BowserTimerDpc(
  19. IN PKDPC Dpc,
  20. IN PVOID Context,
  21. IN PVOID SystemArgument1,
  22. IN PVOID SystemArgument2
  23. );
  24. VOID
  25. BowserTimerDispatcher (
  26. IN PVOID Context
  27. );
  28. #ifdef ALLOC_PRAGMA
  29. #pragma alloc_text(PAGE, BowserInitializeTimer)
  30. #pragma alloc_text(PAGE4BROW, BowserUninitializeTimer)
  31. #pragma alloc_text(PAGE4BROW, BowserStartTimer)
  32. #pragma alloc_text(PAGE4BROW, BowserStopTimer)
  33. #pragma alloc_text(PAGE4BROW, BowserTimerDispatcher)
  34. #endif
  35. LONG
  36. BrExceptionFilter( EXCEPTION_POINTERS * pException)
  37. {
  38. //
  39. // Note: BrExceptionFilter is defined only for checked builds (ifdef DBG)
  40. //
  41. DbgPrint("[Browser] exception 0x%lx.\n", pException->ExceptionRecord->ExceptionCode );
  42. if ( bEnableExceptionBreakpoint &&
  43. pException->ExceptionRecord->ExceptionCode != STATUS_INSUFFICIENT_RESOURCES &&
  44. pException->ExceptionRecord->ExceptionCode != STATUS_WORKING_SET_QUOTA ) {
  45. DbgBreakPoint();
  46. }
  47. return EXCEPTION_EXECUTE_HANDLER;
  48. }
  49. VOID
  50. BowserInitializeTimer(
  51. IN PBOWSER_TIMER Timer
  52. )
  53. {
  54. PAGED_CODE();
  55. KeInitializeTimer(&Timer->Timer);
  56. KeInitializeEvent(&Timer->TimerInactiveEvent, NotificationEvent, TRUE);
  57. KeInitializeSpinLock(&Timer->Lock);
  58. ExInitializeWorkItem(&Timer->WorkItem, BowserTimerDispatcher, Timer);
  59. Timer->AlreadySet = FALSE;
  60. Timer->Canceled = FALSE;
  61. Timer->SetAgain = FALSE;
  62. Timer->Initialized = TRUE;
  63. }
  64. VOID
  65. BowserUninitializeTimer(
  66. IN PBOWSER_TIMER Timer
  67. )
  68. /*++
  69. Routine Description:
  70. Prepare the timer for being uninitialized.
  71. Arguments:
  72. IN PBOWSER_TIMER Timer - Timer to stop
  73. Return Value
  74. None.
  75. --*/
  76. {
  77. KIRQL OldIrql;
  78. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  79. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  80. ACQUIRE_SPIN_LOCK(&Timer->Lock, &OldIrql);
  81. Timer->Initialized = FALSE;
  82. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  83. //
  84. // First stop the timer.
  85. //
  86. BowserStopTimer(Timer);
  87. //
  88. // Now wait to make sure that any the timer routine that is currently
  89. // executing the timer completes. This allows us to make sure that we
  90. // never delete a transport while a timer routine is executing.
  91. //
  92. KeWaitForSingleObject(&Timer->TimerInactiveEvent, Executive, KernelMode, FALSE, NULL);
  93. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  94. }
  95. // Notes for stopping a timer
  96. // ==========================
  97. //
  98. // Timers run through various states. During some of them they cannot be
  99. // cancelled. In order to guarantee that we can reliably stop and start
  100. // timers, they can only be started or stopped at LOW_LEVEL (ie, not from
  101. // DPC_LEVEL.
  102. //
  103. // If the timer is not running then StopTimer does nothing.
  104. //
  105. // If queued inside the kernel timer package then KeCancelTimer will work
  106. // and the timer contents cleaned up.
  107. //
  108. // If the kernel timer package has queued the dpc routine then KeCancelTimer
  109. // will fail. We can flag the timer as canceled. BowserTimerDispatcher will
  110. // cleanup the timer when it fires.
  111. //
  112. // Notes for starting a timer
  113. // ==========================
  114. //
  115. // If StartTimer is called on a clean timer then it sets the contents
  116. // appropriately and gives the timer to the kernel timer package.
  117. //
  118. // If the timer is canceled but not cleaned up then StartTimer will update
  119. // the contents of the timer to show where the new TimerRoutine and TimerContext.
  120. // it will indicate that the timer is no longer canceled and is now SetAgain.
  121. //
  122. // If the timer is already SetAgain then StartTimer will update the contents
  123. // of the timer to hold the new TimerRoutine and TimerContext.
  124. //
  125. // When BowserTimerDispatcher is called on a SetAgain timer, it sets the timer
  126. // to its normal state and gives the timer to the kernel timer package.
  127. //
  128. BOOLEAN
  129. BowserStartTimer(
  130. IN PBOWSER_TIMER Timer,
  131. IN ULONG MillisecondsToExpireTimer,
  132. IN PBOWSER_TIMER_ROUTINE TimerRoutine,
  133. IN PVOID Context
  134. )
  135. /*++
  136. Routine Description:
  137. Set Timer to call TimerRoutine after MillisecondsToExpire. TimerRoutine
  138. is to be called at normal level.
  139. Arguments:
  140. IN PBOWSER_TIMER Timer
  141. IN ULONG MillisecondsToExpireTimer
  142. IN PBOWSER_TIMER_ROUTINE TimerRoutine
  143. IN PVOID Context - Parameter to TimerRoutine
  144. Return Value
  145. BOOLEAN - TRUE if timer set.
  146. --*/
  147. {
  148. LARGE_INTEGER Timeout;
  149. BOOLEAN ReturnValue;
  150. KIRQL OldIrql;
  151. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  152. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  153. ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
  154. Timeout.QuadPart = (LONGLONG)MillisecondsToExpireTimer * (LONGLONG)(-10000);
  155. // Timeout = LiNMul(MillisecondsToExpireTimer, -10000);
  156. ACQUIRE_SPIN_LOCK(&Timer->Lock, &OldIrql);
  157. if (!Timer->Initialized) {
  158. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  159. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  160. return(FALSE);
  161. }
  162. dprintf(DPRT_TIMER, ("BowserStartTimer %lx, TimerRoutine %x. Set to expire at %lx%lx (%ld/%ld ms)\n", Timer, TimerRoutine, Timeout.HighPart, Timeout.LowPart, -1 * Timeout.LowPart, MillisecondsToExpireTimer));
  163. //
  164. // We shouldn't be able to start the timer while it is
  165. // already running unless its also cancelled.
  166. //
  167. if (Timer->AlreadySet == TRUE) {
  168. if (Timer->Canceled) {
  169. //
  170. // This timer has been canceled, but the canceled routine
  171. // hasn't run yet.
  172. //
  173. // Flag that the timer has been re-set, and return to
  174. // the caller. When the BowserTimerDispatch is finally
  175. // executed, the new timer will be set.
  176. //
  177. Timer->Timeout = Timeout;
  178. Timer->TimerRoutine = TimerRoutine;
  179. Timer->SetAgain = TRUE;
  180. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  181. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  182. return(TRUE);
  183. }
  184. InternalError(("Timer started without already being set"));
  185. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  186. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  187. return(FALSE);
  188. }
  189. ASSERT (!Timer->Canceled);
  190. ASSERT (!Timer->SetAgain);
  191. Timer->Timeout = Timeout;
  192. Timer->TimerRoutine = TimerRoutine;
  193. Timer->TimerContext = Context;
  194. Timer->AlreadySet = TRUE;
  195. Timer->Canceled = FALSE;
  196. Timer->SetAgain = FALSE;
  197. //
  198. // Set the inactive event to the not signalled state to indicate that
  199. // there is timer activity outstanding.
  200. //
  201. KeResetEvent(&Timer->TimerInactiveEvent);
  202. //
  203. // We are now starting the timer. Initialize the DPC and
  204. // set the timer.
  205. //
  206. KeInitializeDpc(&Timer->Dpc,
  207. BowserTimerDpc,
  208. Timer);
  209. ReturnValue = KeSetTimer(&Timer->Timer, Timeout, &Timer->Dpc);
  210. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  211. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  212. return ReturnValue;
  213. }
  214. VOID
  215. BowserStopTimer(
  216. IN PBOWSER_TIMER Timer
  217. )
  218. /*++
  219. Routine Description:
  220. Stop the timer from calling the TimerRoutine.
  221. Arguments:
  222. IN PBOWSER_TIMER Timer - Timer to stop
  223. Return Value
  224. None.
  225. --*/
  226. {
  227. KIRQL OldIrql;
  228. //
  229. // Do an unsafe test to see if the timer is already set, we can return.
  230. //
  231. if (!Timer->AlreadySet) {
  232. return;
  233. }
  234. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  235. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  236. //
  237. // You can only stop a timer at LOW_LEVEL
  238. //
  239. ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
  240. ACQUIRE_SPIN_LOCK(&Timer->Lock, &OldIrql);
  241. dprintf(DPRT_TIMER, ("BowserStopTimer %lx\n", Timer));
  242. //
  243. // If the timer isn't running, just early out.
  244. //
  245. if (!Timer->AlreadySet) {
  246. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  247. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  248. return;
  249. }
  250. Timer->Canceled = TRUE;
  251. if (!KeCancelTimer(&Timer->Timer)) {
  252. //
  253. // The timer has already fired. It could be in the dpc queue or
  254. // the work queue. The timer is marked as canceled.
  255. //
  256. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  257. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  258. return;
  259. }
  260. //
  261. // The timer was still in the kernel timer package so we cancelled the
  262. // timer completely. Return timer to initial state.
  263. //
  264. Timer->AlreadySet = FALSE;
  265. //
  266. // The timer isn't canceled, so it can't be reset.
  267. //
  268. Timer->SetAgain = FALSE;
  269. Timer->Canceled = FALSE;
  270. KeSetEvent(&Timer->TimerInactiveEvent, IO_NETWORK_INCREMENT, FALSE);
  271. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  272. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  273. // DbgPrint("Cancel timer %lx complete\n", Timer);
  274. }
  275. VOID
  276. BowserTimerDpc(
  277. IN PKDPC Dpc,
  278. IN PVOID Context,
  279. IN PVOID SystemArgument1,
  280. IN PVOID SystemArgument2
  281. )
  282. /*++
  283. Routine Description:
  284. This routine is called when the timeout expires. It is called at Dpc level
  285. to queue a WorkItem to a system worker thread.
  286. Arguments:
  287. IN PKDPC Dpc,
  288. IN PVOID Context,
  289. IN PVOID SystemArgument1,
  290. IN PVOID SystemArgument2
  291. Return Value
  292. None.
  293. --*/
  294. {
  295. PBOWSER_TIMER Timer = Context;
  296. ASSERT (Dpc == &Timer->Dpc);
  297. // DbgPrint("Timer %lx fired\n", Timer);
  298. //
  299. // Due to bug 245645 we need to queue in delayed worker queue rather then execute timed tasks.
  300. // OLD WAY: ExQueueWorkItem(&Timer->WorkItem, DelayedWorkQueue);
  301. //
  302. BowserQueueDelayedWorkItem( &Timer->WorkItem );
  303. UNREFERENCED_PARAMETER(SystemArgument1);
  304. UNREFERENCED_PARAMETER(SystemArgument2);
  305. }
  306. VOID
  307. BowserTimerDispatcher (
  308. IN PVOID Context
  309. )
  310. /*++
  311. Routine Description:
  312. Call the TimerRoutine and cleanup.
  313. Arguments:
  314. IN PVOID Context - Original parameter supplied to BowserStartTimer
  315. Return Value
  316. None.
  317. --*/
  318. {
  319. IN PBOWSER_TIMER Timer = Context;
  320. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  321. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  322. try {
  323. KIRQL OldIrql;
  324. PBOWSER_TIMER_ROUTINE RoutineToCall;
  325. PVOID ContextForRoutine;
  326. ACQUIRE_SPIN_LOCK(&Timer->Lock, &OldIrql);
  327. //
  328. // If the timer was uninitialized, return right away.
  329. //
  330. if (!Timer->Initialized) {
  331. dprintf(DPRT_TIMER, ("Timer %lx was uninitialized. Returning.\n", Timer));
  332. //
  333. // Set the inactive event to the signalled state to indicate that
  334. // the outstanding timer activity has completed.
  335. //
  336. KeSetEvent(&Timer->TimerInactiveEvent, IO_NETWORK_INCREMENT, FALSE);
  337. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  338. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  339. return;
  340. }
  341. if (Timer->Canceled) {
  342. dprintf(DPRT_TIMER, ("Timer %lx was cancelled\n", Timer));
  343. //
  344. // If the timer was reset, this indicates that the timer was
  345. // canceled, but the timer was in the DPC (or executive worker)
  346. // queue. We want to re-run the timer routine.
  347. //
  348. if (Timer->SetAgain) {
  349. ASSERT (Timer->AlreadySet);
  350. Timer->SetAgain = FALSE;
  351. dprintf(DPRT_TIMER, ("Timer %lx was re-set. Re-setting timer\n", Timer));
  352. //
  353. // We are now starting the timer. Initialize the DPC and
  354. // set the timer.
  355. //
  356. KeInitializeDpc(&Timer->Dpc,
  357. BowserTimerDpc,
  358. Timer);
  359. KeSetTimer(&Timer->Timer, Timer->Timeout, &Timer->Dpc);
  360. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  361. } else {
  362. dprintf(DPRT_TIMER, ("Timer %lx was successfully canceled.\n", Timer));
  363. Timer->AlreadySet = FALSE;
  364. Timer->Canceled = FALSE;
  365. //
  366. // Set the inactive event to the signalled state to indicate that
  367. // the outstanding timer activity has completed.
  368. //
  369. KeSetEvent(&Timer->TimerInactiveEvent, IO_NETWORK_INCREMENT, FALSE);
  370. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  371. }
  372. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  373. return;
  374. }
  375. ASSERT (Timer->AlreadySet);
  376. ASSERT (!Timer->SetAgain);
  377. Timer->AlreadySet = FALSE;
  378. dprintf(DPRT_TIMER, ("Timer %lx fired. Calling %lx\n", Timer, Timer->TimerRoutine));
  379. //
  380. // We release the spinlock so save timer contents locally.
  381. //
  382. RoutineToCall = Timer->TimerRoutine;
  383. ContextForRoutine = Timer->TimerContext;
  384. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  385. RoutineToCall(ContextForRoutine);
  386. ACQUIRE_SPIN_LOCK(&Timer->Lock, &OldIrql);
  387. if ( !Timer->AlreadySet ) {
  388. KeSetEvent(&Timer->TimerInactiveEvent, IO_NETWORK_INCREMENT, FALSE);
  389. }
  390. RELEASE_SPIN_LOCK(&Timer->Lock, OldIrql);
  391. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  392. } except (BR_EXCEPTION) {
  393. #if DBG
  394. KdPrint(("BOWSER: Timer routine %lx faulted: %X\n", Timer->TimerRoutine, GetExceptionCode()));
  395. DbgBreakPoint();
  396. #else
  397. KeBugCheck(9999);
  398. #endif
  399. }
  400. }