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.

506 lines
13 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. NtTimer.c
  5. Abstract:
  6. This module implements the nt version of the timer and worker thread management routines.
  7. These services are provided to all mini redirector writers. The timer service comes in two
  8. flavours - a periodic trigger and a one shot notification.
  9. Author:
  10. Joe Linn [JoeLinn] 2-mar-95
  11. Revision History:
  12. Balan Sethu Raman [SethuR] 7-Mar-95
  13. Included one shot, periodic notification for work queue items.
  14. --*/
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. #ifdef ALLOC_PRAGMA
  18. #pragma alloc_text(PAGE, RxInitializeRxTimer)
  19. #pragma alloc_text(PAGE, RxTearDownRxTimer)
  20. #pragma alloc_text(PAGE, RxPostRecurrentTimerRequest)
  21. #pragma alloc_text(PAGE, RxRecurrentTimerWorkItemDispatcher)
  22. #endif
  23. typedef struct _RX_RECURRENT_WORK_ITEM_ {
  24. RX_WORK_ITEM WorkItem;
  25. LIST_ENTRY RecurrentWorkItemsList;
  26. LARGE_INTEGER TimeInterval;
  27. PRX_WORKERTHREAD_ROUTINE Routine;
  28. PVOID pContext;
  29. } RX_RECURRENT_WORK_ITEM, *PRX_RECURRENT_WORK_ITEM;
  30. //
  31. // Forward declarations of routines
  32. //
  33. extern VOID
  34. RxTimerDispatch(
  35. IN PKDPC Dpc,
  36. IN PVOID DeferredContext,
  37. IN PVOID SystemArgument1,
  38. IN PVOID SystemArgument2
  39. );
  40. extern VOID
  41. RxRecurrentTimerWorkItemDispatcher (
  42. IN PVOID Context
  43. );
  44. // The Bug check file id for this module
  45. #define BugCheckFileId (RDBSS_BUG_CHECK_NTTIMER)
  46. // The local trace mask for this part of the module
  47. #define Dbg (DEBUG_TRACE_NTTIMER)
  48. LARGE_INTEGER s_RxTimerInterval;
  49. KSPIN_LOCK s_RxTimerLock;
  50. KDPC s_RxTimerDpc;
  51. LIST_ENTRY s_RxTimerQueueHead; // queue of the list of timer calls
  52. LIST_ENTRY s_RxRecurrentWorkItemsList;
  53. KTIMER s_RxTimer;
  54. ULONG s_RxTimerTickCount;
  55. #define NoOf100nsTicksIn1ms (10 * 1000)
  56. #define NoOf100nsTicksIn55ms (10 * 1000 * 55)
  57. NTSTATUS
  58. RxInitializeRxTimer()
  59. /*++
  60. Routine Description:
  61. The routine initializes everything having to do with the timer stuff.
  62. Arguments:
  63. none
  64. Return Value:
  65. none
  66. --*/
  67. {
  68. NTSTATUS Status = STATUS_SUCCESS;
  69. PAGED_CODE();
  70. s_RxTimerInterval.LowPart = (ULONG)(-((LONG)NoOf100nsTicksIn55ms));
  71. s_RxTimerInterval.HighPart = -1;
  72. KeInitializeSpinLock( &s_RxTimerLock );
  73. InitializeListHead( &s_RxTimerQueueHead );
  74. InitializeListHead( &s_RxRecurrentWorkItemsList );
  75. KeInitializeDpc( &s_RxTimerDpc, RxTimerDispatch, NULL );
  76. KeInitializeTimer( &s_RxTimer );
  77. s_RxTimerTickCount = 0;
  78. return Status;
  79. }
  80. VOID
  81. RxTearDownRxTimer(
  82. void)
  83. /*++
  84. Routine Description:
  85. This routine is used by drivers to initialize a timer entry for a device
  86. object.
  87. Arguments:
  88. TimerEntry - Pointer to a timer entry to be used.
  89. TimerRoutine - Driver routine to be executed when timer expires.
  90. Context - Context parameter that is passed to the driver routine.
  91. Return Value:
  92. The function value indicates whether or not the timer was initialized.
  93. --*/
  94. {
  95. PRX_RECURRENT_WORK_ITEM pWorkItem;
  96. PLIST_ENTRY pListEntry;
  97. PAGED_CODE();
  98. KeCancelTimer( &s_RxTimer );
  99. // Walk down the list freeing up the recurrent requests since the memory was
  100. // allocated by us.
  101. while (!IsListEmpty(&s_RxRecurrentWorkItemsList)) {
  102. pListEntry = RemoveHeadList(&s_RxRecurrentWorkItemsList);
  103. pWorkItem = (PRX_RECURRENT_WORK_ITEM)
  104. CONTAINING_RECORD(
  105. pListEntry,
  106. RX_RECURRENT_WORK_ITEM,
  107. RecurrentWorkItemsList);
  108. RxFreePool(pWorkItem);
  109. }
  110. }
  111. VOID
  112. RxTimerDispatch(
  113. IN PKDPC Dpc,
  114. IN PVOID DeferredContext,
  115. IN PVOID SystemArgument1,
  116. IN PVOID SystemArgument2
  117. )
  118. /*++
  119. Routine Description:
  120. This routine scans the timer database and posts a work item for all those requests
  121. whose temporal constraints have been satisfied.
  122. Arguments:
  123. Dpc - Supplies a pointer to a control object of type DPC.
  124. DeferredContext - Optional deferred context; not used.
  125. SystemArgument1 - Optional argument 1; not used.
  126. SystemArgument2 - Optional argument 2; not used.
  127. Return Value:
  128. None.
  129. --*/
  130. {
  131. PLIST_ENTRY pListEntry;
  132. LIST_ENTRY ExpiredList;
  133. //KIRQL Irql;
  134. BOOLEAN ContinueTimer = FALSE;
  135. PRX_WORK_QUEUE_ITEM pWorkQueueItem;
  136. PRX_WORK_ITEM pWorkItem;
  137. UNREFERENCED_PARAMETER( Dpc );
  138. UNREFERENCED_PARAMETER( DeferredContext );
  139. UNREFERENCED_PARAMETER( SystemArgument1 );
  140. UNREFERENCED_PARAMETER( SystemArgument2 );
  141. InitializeListHead(&ExpiredList);
  142. KeAcquireSpinLockAtDpcLevel( &s_RxTimerLock );
  143. s_RxTimerTickCount++;
  144. pListEntry = s_RxTimerQueueHead.Flink;
  145. while (pListEntry != &s_RxTimerQueueHead) {
  146. pWorkQueueItem = CONTAINING_RECORD(
  147. pListEntry,
  148. RX_WORK_QUEUE_ITEM,
  149. List );
  150. pWorkItem = CONTAINING_RECORD(
  151. pWorkQueueItem,
  152. RX_WORK_ITEM,
  153. WorkQueueItem);
  154. if (pWorkItem->LastTick == s_RxTimerTickCount) {
  155. PLIST_ENTRY pExpiredEntry = pListEntry;
  156. pListEntry = pListEntry->Flink;
  157. RemoveEntryList(pExpiredEntry);
  158. InsertTailList(&ExpiredList,pExpiredEntry);
  159. } else {
  160. pListEntry = pListEntry->Flink;
  161. }
  162. }
  163. ContinueTimer = !(IsListEmpty(&s_RxTimerQueueHead));
  164. KeReleaseSpinLockFromDpcLevel( &s_RxTimerLock );
  165. // Resubmit the timer queue dispatch routine so that it will be reinvoked.
  166. if (ContinueTimer)
  167. KeSetTimer( &s_RxTimer, s_RxTimerInterval, &s_RxTimerDpc );
  168. // Queue all the expired entries on the worker threads.
  169. while (!IsListEmpty(&ExpiredList)) {
  170. pListEntry = RemoveHeadList(&ExpiredList);
  171. pListEntry->Flink = pListEntry->Blink = NULL;
  172. pWorkQueueItem = CONTAINING_RECORD(
  173. pListEntry,
  174. RX_WORK_QUEUE_ITEM,
  175. List );
  176. // Post the work item to a worker thread
  177. RxPostToWorkerThread(
  178. pWorkQueueItem->pDeviceObject,
  179. CriticalWorkQueue,
  180. pWorkQueueItem,
  181. pWorkQueueItem->WorkerRoutine,
  182. pWorkQueueItem->Parameter);
  183. }
  184. }
  185. NTSTATUS
  186. RxPostOneShotTimerRequest(
  187. IN PRDBSS_DEVICE_OBJECT pDeviceObject,
  188. IN PRX_WORK_ITEM pWorkItem,
  189. IN PRX_WORKERTHREAD_ROUTINE Routine,
  190. IN PVOID pContext,
  191. IN LARGE_INTEGER TimeInterval)
  192. /*++
  193. Routine Description:
  194. This routine is used by drivers to initialize a timer entry for a device
  195. object.
  196. Arguments:
  197. pDeviceObject - the device object
  198. pWorkItem - the work item
  199. Routine - the routine to be invoked on timeout
  200. pContext - the Context parameter that is passed to the driver routine.
  201. TimeInterval - the time interval in 100 ns ticks.
  202. Return Value:
  203. The function value indicates whether or not the timer was initialized.
  204. --*/
  205. {
  206. BOOLEAN StartTimer;
  207. //NTSTATUS Status;
  208. ULONG NumberOf55msIntervals;
  209. KIRQL Irql;
  210. LARGE_INTEGER StrobeInterval;
  211. ASSERT(pWorkItem != NULL);
  212. // Initialize the work queue item.
  213. ExInitializeWorkItem(
  214. (PWORK_QUEUE_ITEM)&pWorkItem->WorkQueueItem,
  215. Routine,
  216. pContext );
  217. pWorkItem->WorkQueueItem.pDeviceObject = pDeviceObject;
  218. // Compute the time interval in number of ticks.
  219. StrobeInterval.QuadPart= NoOf100nsTicksIn55ms;
  220. NumberOf55msIntervals = (ULONG)(TimeInterval.QuadPart / StrobeInterval.QuadPart);
  221. NumberOf55msIntervals += 1; // Take the ceiling to be conservative
  222. RxDbgTraceLV( 0, Dbg, 1500, ("Timer will expire after %ld 55ms intervals\n",NumberOf55msIntervals));
  223. // Insert the entry in the timer queue.
  224. KeAcquireSpinLock( &s_RxTimerLock, &Irql );
  225. // Update the tick relative to the current tick.
  226. pWorkItem->LastTick = s_RxTimerTickCount + NumberOf55msIntervals;
  227. StartTimer = IsListEmpty(&s_RxTimerQueueHead);
  228. InsertTailList( &s_RxTimerQueueHead,&pWorkItem->WorkQueueItem.List);
  229. KeReleaseSpinLock( &s_RxTimerLock, Irql );
  230. if (StartTimer) {
  231. KeSetTimer( &s_RxTimer, s_RxTimerInterval, &s_RxTimerDpc );
  232. }
  233. return STATUS_SUCCESS;
  234. }
  235. NTSTATUS
  236. RxPostRecurrentTimerRequest(
  237. IN PRDBSS_DEVICE_OBJECT pDeviceObject,
  238. IN PRX_WORKERTHREAD_ROUTINE Routine,
  239. IN PVOID pContext,
  240. IN LARGE_INTEGER TimeInterval)
  241. /*++
  242. Routine Description:
  243. This routine is used to post a recurrent timer request. The passed in routine once every
  244. (TimeInterval) milli seconds.
  245. Arguments:
  246. pDeviceObject - the device object
  247. Routine - the routine to be invoked on timeout
  248. pContext - the Context parameter that is passed to the driver routine.
  249. TimeInterval - the time interval in 100ns ticks.
  250. Return Value:
  251. The function value indicates whether or not the timer was initialized.
  252. --*/
  253. {
  254. PRX_RECURRENT_WORK_ITEM pRecurrentWorkItem;
  255. NTSTATUS Status;
  256. PAGED_CODE();
  257. // Allocate a work item.
  258. pRecurrentWorkItem = (PRX_RECURRENT_WORK_ITEM)
  259. RxAllocatePoolWithTag(
  260. NonPagedPool,
  261. sizeof(RX_RECURRENT_WORK_ITEM),
  262. RX_TIMER_POOLTAG);
  263. if (pRecurrentWorkItem != NULL) {
  264. InsertTailList(
  265. &s_RxRecurrentWorkItemsList,
  266. &pRecurrentWorkItem->RecurrentWorkItemsList);
  267. pRecurrentWorkItem->Routine = Routine;
  268. pRecurrentWorkItem->pContext = pContext;
  269. pRecurrentWorkItem->TimeInterval = TimeInterval;
  270. pRecurrentWorkItem->WorkItem.WorkQueueItem.pDeviceObject = pDeviceObject;
  271. Status = RxPostOneShotTimerRequest(
  272. pRecurrentWorkItem->WorkItem.WorkQueueItem.pDeviceObject,
  273. &pRecurrentWorkItem->WorkItem,
  274. RxRecurrentTimerWorkItemDispatcher,
  275. pRecurrentWorkItem,
  276. TimeInterval);
  277. } else {
  278. Status = STATUS_INSUFFICIENT_RESOURCES;
  279. }
  280. return Status;
  281. }
  282. NTSTATUS
  283. RxCancelTimerRequest(
  284. IN PRDBSS_DEVICE_OBJECT pDeviceObject,
  285. IN PRX_WORKERTHREAD_ROUTINE Routine,
  286. IN PVOID pContext)
  287. /*++
  288. Routine Description:
  289. This routine cancels a timer request. The request to be cancelled is identified
  290. by the routine and context.
  291. Arguments:
  292. Routine - the routine to be invoked on timeout
  293. pContext - the Context parameter that is passed to the driver routine.
  294. --*/
  295. {
  296. NTSTATUS Status = STATUS_NOT_FOUND;
  297. PLIST_ENTRY pListEntry;
  298. PWORK_QUEUE_ITEM pWorkQueueItem;
  299. PRX_WORK_ITEM pWorkItem;
  300. PRX_RECURRENT_WORK_ITEM pRecurrentWorkItem = NULL;
  301. KIRQL Irql;
  302. KeAcquireSpinLock( &s_RxTimerLock, &Irql );
  303. // Walk through the list of entries
  304. for (pListEntry = s_RxTimerQueueHead.Flink;
  305. (pListEntry != &s_RxTimerQueueHead);
  306. pListEntry = pListEntry->Flink ) {
  307. pWorkQueueItem = CONTAINING_RECORD( pListEntry, WORK_QUEUE_ITEM, List );
  308. pWorkItem = CONTAINING_RECORD( pWorkQueueItem, RX_WORK_ITEM, WorkQueueItem);
  309. if ((pWorkItem->WorkQueueItem.pDeviceObject == pDeviceObject) &&
  310. (pWorkItem->WorkQueueItem.WorkerRoutine == Routine) &&
  311. (pWorkItem->WorkQueueItem.Parameter == pContext)) {
  312. RemoveEntryList(pListEntry);
  313. Status = STATUS_SUCCESS;
  314. pRecurrentWorkItem = NULL;
  315. break;
  316. } else if (pWorkItem->WorkQueueItem.WorkerRoutine == RxRecurrentTimerWorkItemDispatcher) {
  317. pRecurrentWorkItem = (PRX_RECURRENT_WORK_ITEM)pWorkItem->WorkQueueItem.Parameter;
  318. if ((pRecurrentWorkItem->Routine == Routine) &&
  319. (pRecurrentWorkItem->pContext == pContext)) {
  320. RemoveEntryList(pListEntry);
  321. RemoveEntryList(&pRecurrentWorkItem->RecurrentWorkItemsList);
  322. Status = STATUS_SUCCESS;
  323. } else {
  324. pRecurrentWorkItem = NULL;
  325. }
  326. }
  327. }
  328. KeReleaseSpinLock( &s_RxTimerLock, Irql );
  329. if (pRecurrentWorkItem != NULL) {
  330. RxFreePool(pRecurrentWorkItem);
  331. }
  332. return Status;
  333. }
  334. VOID
  335. RxRecurrentTimerWorkItemDispatcher (
  336. IN PVOID Context
  337. )
  338. /*++
  339. Routine Description:
  340. This routine dispatches a recurrent timer request. On completion of the invocation of the
  341. associated routine the request s requeued.
  342. Arguments:
  343. Routine - the routine to be invoked on timeout
  344. pContext - the Context parameter that is passed to the driver routine.
  345. --*/
  346. {
  347. PRX_RECURRENT_WORK_ITEM pPeriodicWorkItem = (PRX_RECURRENT_WORK_ITEM)Context;
  348. PRX_WORKERTHREAD_ROUTINE Routine = pPeriodicWorkItem->Routine;
  349. PVOID pContext = pPeriodicWorkItem->pContext;
  350. PAGED_CODE();
  351. //KIRQL Irql;
  352. // Invoke the routine.
  353. Routine(pContext);
  354. // enqueue the item if necessary.
  355. RxPostOneShotTimerRequest(
  356. pPeriodicWorkItem->WorkItem.WorkQueueItem.pDeviceObject,
  357. &pPeriodicWorkItem->WorkItem,
  358. RxRecurrentTimerWorkItemDispatcher,
  359. pPeriodicWorkItem,
  360. pPeriodicWorkItem->TimeInterval);
  361. }
  362.