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.

3075 lines
80 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. This module defines functions for the timer thread pool.
  7. Author:
  8. Gurdeep Singh Pall (gurdeep) Nov 13, 1997
  9. Revision History:
  10. lokeshs - extended/modified threadpool.
  11. Rob Earhart (earhart) September 29, 2000
  12. Split off from threads.c
  13. Environment:
  14. These routines are statically linked in the caller's executable
  15. and are callable only from user mode. They make use of Nt system
  16. services.
  17. --*/
  18. #include <ntos.h>
  19. #include <ntrtl.h>
  20. #include <wow64t.h>
  21. #include "ntrtlp.h"
  22. #include "threads.h"
  23. // Timer Thread Pool
  24. // -----------------
  25. // Clients create one or more Timer Queues and insert one shot or periodic
  26. // timers in them. All timers in a queue are kept in a "Delta List" with each
  27. // timer's firing time relative to the timer before it. All Queues are also
  28. // kept in a "Delta List" with each Queue's firing time (set to the firing time
  29. // of the nearest firing timer) relative to the Queue before it. One NT Timer
  30. // is used to service all timers in all queues.
  31. ULONG StartedTimerInitialization ; // Used by Timer thread startup synchronization
  32. ULONG CompletedTimerInitialization ; // Used for to check if Timer thread is initialized
  33. HANDLE TimerThreadHandle ; // Holds the timer thread handle
  34. ULONG TimerThreadId ; // Used to check if current thread is a timer thread
  35. LIST_ENTRY TimerQueues ; // All timer queues are linked in this list
  36. HANDLE TimerHandle ; // Holds handle of NT Timer used by the Timer Thread
  37. HANDLE TimerThreadStartedEvent ; // Indicates that the timer thread has started
  38. ULONG NumTimerQueues ; // Number of timer queues
  39. RTL_CRITICAL_SECTION TimerCriticalSection ; // Exclusion used by timer threads
  40. LARGE_INTEGER Last64BitTickCount ;
  41. LARGE_INTEGER Resync64BitTickCount ;
  42. LARGE_INTEGER Firing64BitTickCount ;
  43. #if DBG
  44. ULONG RtlpDueTimeMax = 0;
  45. #endif
  46. #if DBG1
  47. ULONG NextTimerDbgId;
  48. #endif
  49. #define RtlpGetResync64BitTickCount() Resync64BitTickCount.QuadPart
  50. #define RtlpSetFiring64BitTickCount(Timeout) \
  51. Firing64BitTickCount.QuadPart = (Timeout)
  52. __inline
  53. LONGLONG
  54. RtlpGet64BitTickCount(
  55. LARGE_INTEGER *Last64BitTickCount
  56. )
  57. /*++
  58. Routine Description:
  59. This routine is used for getting the latest 64bit tick count.
  60. Arguments:
  61. Return Value: 64bit tick count
  62. --*/
  63. {
  64. LARGE_INTEGER liCurTime ;
  65. liCurTime.QuadPart = NtGetTickCount() + Last64BitTickCount->HighPart ;
  66. // see if timer has wrapped.
  67. if (liCurTime.LowPart < Last64BitTickCount->LowPart) {
  68. liCurTime.HighPart++ ;
  69. }
  70. return (Last64BitTickCount->QuadPart = liCurTime.QuadPart) ;
  71. }
  72. __inline
  73. LONGLONG
  74. RtlpResync64BitTickCount(
  75. )
  76. /*++
  77. Routine Description:
  78. This routine is used for getting the latest 64bit tick count.
  79. Arguments:
  80. Return Value: 64bit tick count
  81. Remarks: This call should be made in the first line of any APC queued
  82. to the timer thread and nowhere else. It is used to reduce the drift
  83. --*/
  84. {
  85. return Resync64BitTickCount.QuadPart =
  86. RtlpGet64BitTickCount(&Last64BitTickCount);
  87. }
  88. VOID
  89. RtlpAsyncTimerCallbackCompletion(
  90. PVOID Context
  91. )
  92. /*++
  93. Routine Description:
  94. This routine is called in a (IO)worker thread and is used to decrement the
  95. RefCount at the end and call RtlpDeleteTimer if required
  96. Arguments:
  97. Context - pointer to the Timer object,
  98. Return Value:
  99. --*/
  100. {
  101. PRTLP_TIMER Timer = (PRTLP_TIMER) Context;
  102. #if DBG
  103. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  104. RTLP_THREADPOOL_TRACE_MASK,
  105. "<%d> Calling WaitOrTimer:Timer: fn:%x context:%x bool:%d Thread<%d:%d>\n",
  106. Timer->DbgId,
  107. (ULONG_PTR)Timer->Function, (ULONG_PTR)Timer->Context,
  108. TRUE,
  109. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  110. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  111. #endif
  112. RtlpWaitOrTimerCallout(Timer->Function,
  113. Timer->Context,
  114. TRUE,
  115. Timer->ActivationContext,
  116. Timer->ImpersonationToken,
  117. NULL);
  118. // decrement RefCount after function is executed so that the context is not deleted
  119. if ( InterlockedDecrement( &Timer->RefCount ) == 0 ) {
  120. RtlpDeleteTimer( Timer ) ;
  121. }
  122. }
  123. VOID
  124. RtlpFireTimers (
  125. PLIST_ENTRY TimersToFireList
  126. )
  127. /*++
  128. Routine Description:
  129. Finally all the timers are fired here.
  130. Arguments:
  131. TimersToFireList: List of timers to fire
  132. --*/
  133. {
  134. PLIST_ENTRY Node ;
  135. PRTLP_TIMER Timer ;
  136. NTSTATUS Status;
  137. BOOLEAN IsSingleShotWaitTimer;
  138. for (Node = TimersToFireList->Flink; Node != TimersToFireList; Node = TimersToFireList->Flink)
  139. {
  140. Timer = CONTAINING_RECORD (Node, RTLP_TIMER, TimersToFireList) ;
  141. RemoveEntryList( Node ) ;
  142. InitializeListHead( Node ) ;
  143. IsSingleShotWaitTimer = (Timer->Wait != NULL
  144. && Timer->Period == 0);
  145. if ( (Timer->State & STATE_DONTFIRE)
  146. || (Timer->Queue->State & STATE_DONTFIRE) )
  147. {
  148. //
  149. // Wait timers *never* use STATE_DONTFIRE. Let's just
  150. // make sure this isn't one:
  151. //
  152. ASSERT(Timer->Wait == NULL);
  153. } else if ( Timer->Flags & (WT_EXECUTEINTIMERTHREAD | WT_EXECUTEINWAITTHREAD ) ) {
  154. #if DBG
  155. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  156. RTLP_THREADPOOL_TRACE_MASK,
  157. "<%d> Calling WaitOrTimer(Timer): fn:%x context:%x bool:%d Thread<%d:%d>\n",
  158. Timer->DbgId,
  159. (ULONG_PTR)Timer->Function, (ULONG_PTR)Timer->Context,
  160. TRUE,
  161. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  162. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  163. #endif
  164. {
  165. PRTL_CRITICAL_SECTION const LocksHeld[] = {
  166. &TimerCriticalSection,
  167. NULL
  168. };
  169. RtlpWaitOrTimerCallout(Timer->Function,
  170. Timer->Context,
  171. TRUE,
  172. Timer->ActivationContext,
  173. Timer->ImpersonationToken,
  174. LocksHeld);
  175. }
  176. } else {
  177. // timer associated with WaitEvents should be treated differently
  178. if ( Timer->Wait != NULL ) {
  179. InterlockedIncrement( Timer->RefCountPtr ) ;
  180. // Set the low bit of the context to indicate to
  181. // RtlpAsyncWaitCallbackCompletion that this is a
  182. // timer-initiated callback.
  183. Status = RtlQueueWorkItem(RtlpAsyncWaitCallbackCompletion,
  184. (PVOID)(((ULONG_PTR) Timer->Wait) | 1),
  185. Timer->Flags);
  186. } else {
  187. InterlockedIncrement( &Timer->RefCount ) ;
  188. Status = RtlQueueWorkItem(RtlpAsyncTimerCallbackCompletion,
  189. Timer,
  190. Timer->Flags);
  191. }
  192. if (!NT_SUCCESS(Status)) {
  193. // NTRAID#202802-2000/10/12-earhart: we really ought
  194. // to deal with this case in a better way, since we
  195. // can't guarantee (with our current architecture)
  196. // that the enqueue will work.
  197. if ( Timer->Wait != NULL ) {
  198. InterlockedDecrement( Timer->RefCountPtr );
  199. } else {
  200. InterlockedDecrement( &Timer->RefCount );
  201. }
  202. }
  203. }
  204. //
  205. // If it's a singleshot wait timer, we can free it now.
  206. // (N.B. if it's *not*, the timer may be invalid by now.)
  207. //
  208. if (IsSingleShotWaitTimer) {
  209. RtlpFreeTPHeap(Timer);
  210. }
  211. }
  212. }
  213. VOID
  214. RtlpFireTimersAndReorder (
  215. PRTLP_TIMER_QUEUE Queue,
  216. ULONG *NewFiringTime,
  217. PLIST_ENTRY TimersToFireList
  218. )
  219. /*++
  220. Routine Description:
  221. Fires all timers in TimerList that have DeltaFiringTime == 0. After firing the timers
  222. it reorders the timers based on their periodic times OR frees the fired one shot timers.
  223. Arguments:
  224. TimerList - Timer list to work thru.
  225. NewFiringTime - Location where the new firing time for the first timer in the delta list
  226. is returned.
  227. Return Value:
  228. --*/
  229. {
  230. PLIST_ENTRY TNode ;
  231. PRTLP_TIMER Timer ;
  232. LIST_ENTRY ReinsertTimerList ;
  233. PLIST_ENTRY TimerList = &Queue->TimerList ;
  234. InitializeListHead (&ReinsertTimerList) ;
  235. *NewFiringTime = 0 ;
  236. for (TNode = TimerList->Flink ; (TNode != TimerList) && (*NewFiringTime == 0);
  237. TNode = TimerList->Flink)
  238. {
  239. Timer = CONTAINING_RECORD (TNode, RTLP_TIMER, List) ;
  240. // Fire all timers with delta time of 0
  241. if (Timer->DeltaFiringTime == 0) {
  242. // detach this timer from the list
  243. RemoveEntryList (TNode) ;
  244. // get next firing time
  245. if (!IsListEmpty(TimerList)) {
  246. PRTLP_TIMER TmpTimer ;
  247. TmpTimer = CONTAINING_RECORD (TimerList->Flink, RTLP_TIMER, List) ;
  248. *NewFiringTime = TmpTimer->DeltaFiringTime ;
  249. TmpTimer->DeltaFiringTime = 0 ;
  250. } else {
  251. *NewFiringTime = INFINITE_TIME ;
  252. }
  253. // if timer is not periodic then remove active state. Timer will be deleted
  254. // when cancel timer is called.
  255. if (Timer->Period == 0) {
  256. if ( Timer->Wait ) {
  257. // If one shot wait was timed out, then deactivate the
  258. // wait. Make sure that RtlpDeactivateWait knows
  259. // we're going to continue using the timer's memory.
  260. RtlpDeactivateWait( Timer->Wait, FALSE ) ;
  261. // The timer does *not* go on the uncancelled
  262. // timer list. Initialize its list head to avoid
  263. // refering to other timers.
  264. InitializeListHead( &Timer->List );
  265. }
  266. else {
  267. // If a normal non-periodic timer was timed out,
  268. // then insert it into the uncancelled timer list.
  269. InsertHeadList( &Queue->UncancelledTimerList, &Timer->List ) ;
  270. // should be set at the end
  271. RtlInterlockedClearBitsDiscardReturn(&Timer->State,
  272. STATE_ACTIVE);
  273. }
  274. RtlInterlockedSetBitsDiscardReturn(&Timer->State,
  275. STATE_ONE_SHOT_FIRED);
  276. } else {
  277. // Set the DeltaFiringTime to be the next period
  278. Timer->DeltaFiringTime = Timer->Period ;
  279. // reinsert the timer in the list.
  280. RtlpInsertInDeltaList (TimerList, Timer, *NewFiringTime, NewFiringTime) ;
  281. }
  282. // Call the function associated with this timer. call it in the end
  283. // so that RtlTimer calls can be made in the timer function
  284. if ( (Timer->State & STATE_DONTFIRE)
  285. || (Timer->Queue->State & STATE_DONTFIRE) )
  286. {
  287. //
  288. // Wait timers *never* use STATE_DONTFIRE. Let's just
  289. // make sure this isn't one:
  290. //
  291. ASSERT(Timer->Wait == NULL);
  292. } else {
  293. InsertTailList( TimersToFireList, &Timer->TimersToFireList ) ;
  294. }
  295. } else {
  296. // No more Timers with DeltaFiringTime == 0
  297. break ;
  298. }
  299. }
  300. if ( *NewFiringTime == 0 ) {
  301. *NewFiringTime = INFINITE_TIME ;
  302. }
  303. }
  304. VOID
  305. RtlpInsertTimersIntoDeltaList (
  306. IN PLIST_ENTRY NewTimerList,
  307. IN PLIST_ENTRY DeltaTimerList,
  308. IN ULONG TimeRemaining,
  309. OUT ULONG *NewFiringTime
  310. )
  311. /*++
  312. Routine Description:
  313. This routine walks thru a list of timers in NewTimerList and inserts them into a delta
  314. timers list pointed to by DeltaTimerList. The timeout associated with the first element
  315. in the new list is returned in NewFiringTime.
  316. Arguments:
  317. NewTimerList - List of timers that need to be inserted into the DeltaTimerList
  318. DeltaTimerList - Existing delta list of zero or more timers.
  319. TimeRemaining - Firing time of the first element in the DeltaTimerList
  320. NewFiringTime - Location where the new firing time will be returned
  321. Return Value:
  322. --*/
  323. {
  324. PRTLP_GENERIC_TIMER Timer ;
  325. PLIST_ENTRY TNode ;
  326. PLIST_ENTRY Temp ;
  327. for (TNode = NewTimerList->Flink ; TNode != NewTimerList ; TNode = TNode->Flink) {
  328. Temp = TNode->Blink ;
  329. RemoveEntryList (Temp->Flink) ;
  330. Timer = CONTAINING_RECORD (TNode, RTLP_GENERIC_TIMER, List) ;
  331. if (RtlpInsertInDeltaList (DeltaTimerList, Timer, TimeRemaining, NewFiringTime)) {
  332. TimeRemaining = *NewFiringTime ;
  333. }
  334. TNode = Temp ;
  335. }
  336. }
  337. VOID
  338. RtlpServiceTimer (
  339. PVOID NotUsedArg,
  340. ULONG NotUsedLowTimer,
  341. LONG NotUsedHighTimer
  342. )
  343. /*++
  344. Routine Description:
  345. Services the timer. Runs in an APC.
  346. Arguments:
  347. NotUsedArg - Argument is not used in this function.
  348. NotUsedLowTimer - Argument is not used in this function.
  349. NotUsedHighTimer - Argument is not used in this function.
  350. Return Value:
  351. Remarks:
  352. This APC is called only for timeouts of timer threads.
  353. --*/
  354. {
  355. PRTLP_TIMER Timer ;
  356. PRTLP_TIMER_QUEUE Queue ;
  357. PLIST_ENTRY TNode ;
  358. PLIST_ENTRY QNode ;
  359. PLIST_ENTRY Temp ;
  360. ULONG NewFiringTime ;
  361. LIST_ENTRY ReinsertTimerQueueList ;
  362. LIST_ENTRY TimersToFireList ;
  363. RtlpResync64BitTickCount() ;
  364. #if DBG
  365. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  366. RTLP_THREADPOOL_VERBOSE_MASK,
  367. "Before service timer ThreadId<%x:%x>\n",
  368. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  369. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  370. RtlDebugPrintTimes ();
  371. #endif
  372. ACQUIRE_GLOBAL_TIMER_LOCK();
  373. // fire it if it even 200ms ahead. else reset the timer
  374. if (Firing64BitTickCount.QuadPart > RtlpGet64BitTickCount(&Last64BitTickCount) + 200) {
  375. RtlpResetTimer (TimerHandle, RtlpGetTimeRemaining (TimerHandle), NULL) ;
  376. RELEASE_GLOBAL_TIMER_LOCK() ;
  377. return ;
  378. }
  379. InitializeListHead (&ReinsertTimerQueueList) ;
  380. InitializeListHead (&TimersToFireList) ;
  381. // We run thru all queues with DeltaFiringTime == 0 and fire all timers that
  382. // have DeltaFiringTime == 0. We remove the fired timers and either free them
  383. // (for one shot timers) or put them in aside list (for periodic timers).
  384. // After we have finished firing all timers in a queue we reinsert the timers
  385. // in the aside list back into the queue based on their new firing times.
  386. //
  387. // Similarly, we remove each fired Queue and put it in a aside list. After firing
  388. // all queues with DeltaFiringTime == 0, we reinsert the Queues in the aside list
  389. // and reprogram the NT timer to be the firing time of the first queue in the list
  390. for (QNode = TimerQueues.Flink ; QNode != &TimerQueues ; QNode = QNode->Flink) {
  391. Queue = CONTAINING_RECORD (QNode, RTLP_TIMER_QUEUE, List) ;
  392. // If the delta time in the timer queue is 0 - then this queue
  393. // has timers that are ready to fire. Walk the list and fire all timers with
  394. // Delta time of 0
  395. if (Queue->DeltaFiringTime == 0) {
  396. // Walk all timers with DeltaFiringTime == 0 and fire them. After that
  397. // reinsert the periodic timers in the appropriate place.
  398. RtlpFireTimersAndReorder (Queue, &NewFiringTime, &TimersToFireList) ;
  399. // detach this Queue from the list
  400. QNode = QNode->Blink ;
  401. RemoveEntryList (QNode->Flink) ;
  402. // If there are timers in the queue then prepare to reinsert the queue in
  403. // TimerQueues.
  404. if (NewFiringTime != INFINITE_TIME) {
  405. Queue->DeltaFiringTime = NewFiringTime ;
  406. // put the timer in list that we will process after we have
  407. // fired all elements in this queue
  408. InsertHeadList (&ReinsertTimerQueueList, &Queue->List) ;
  409. } else {
  410. // Queue has no more timers in it. Let the Queue float.
  411. InitializeListHead (&Queue->List) ;
  412. }
  413. } else {
  414. // No more Queues with DeltaFiringTime == 0
  415. break ;
  416. }
  417. }
  418. // At this point we have fired all the ready timers. We have two lists that need to be
  419. // merged - TimerQueues and ReinsertTimerQueueList. The following steps do this - at the
  420. // end of this we will reprogram the NT Timer.
  421. if (!IsListEmpty(&TimerQueues)) {
  422. Queue = CONTAINING_RECORD (TimerQueues.Flink, RTLP_TIMER_QUEUE, List) ;
  423. NewFiringTime = Queue->DeltaFiringTime ;
  424. Queue->DeltaFiringTime = 0 ;
  425. if (!IsListEmpty (&ReinsertTimerQueueList)) {
  426. // TimerQueues and ReinsertTimerQueueList are both non-empty. Merge them.
  427. RtlpInsertTimersIntoDeltaList (&ReinsertTimerQueueList, &TimerQueues,
  428. NewFiringTime, &NewFiringTime) ;
  429. }
  430. // NewFiringTime contains the time the NT Timer should be programmed to.
  431. } else {
  432. if (!IsListEmpty (&ReinsertTimerQueueList)) {
  433. // TimerQueues is empty. ReinsertTimerQueueList is not.
  434. RtlpInsertTimersIntoDeltaList (&ReinsertTimerQueueList, &TimerQueues, 0,
  435. &NewFiringTime) ;
  436. } else {
  437. NewFiringTime = INFINITE_TIME ;
  438. }
  439. // NewFiringTime contains the time the NT Timer should be programmed to.
  440. }
  441. // Reset the timer to reflect the Delta time associated with the first Queue
  442. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  443. #if DBG
  444. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  445. RTLP_THREADPOOL_VERBOSE_MASK,
  446. "After service timer:ThreadId<%x:%x>\n",
  447. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  448. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  449. RtlDebugPrintTimes ();
  450. #endif
  451. // finally fire all the timers
  452. RtlpFireTimers( &TimersToFireList ) ;
  453. RELEASE_GLOBAL_TIMER_LOCK();
  454. }
  455. VOID
  456. RtlpResetTimer (
  457. HANDLE TimerHandle,
  458. ULONG DueTime,
  459. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB
  460. )
  461. /*++
  462. Routine Description:
  463. This routine resets the timer object with the new due time.
  464. Arguments:
  465. TimerHandle - Handle to the timer object
  466. DueTime - Relative timer due time in Milliseconds
  467. Return Value:
  468. --*/
  469. {
  470. LARGE_INTEGER LongDueTime ;
  471. NtCancelTimer (TimerHandle, NULL) ;
  472. // If the DueTime is INFINITE_TIME then set the timer to the largest integer possible
  473. if (DueTime >= PSEUDO_INFINITE_TIME) {
  474. LongDueTime.LowPart = 0x1 ;
  475. LongDueTime.HighPart = 0x80000000 ;
  476. } else {
  477. //
  478. // set the absolute time when timer is to be fired
  479. //
  480. if (ThreadCB) {
  481. ThreadCB->Firing64BitTickCount = DueTime
  482. + RtlpGet64BitTickCount(&ThreadCB->Current64BitTickCount) ;
  483. } else {
  484. //
  485. // adjust for drift only if it is a global timer
  486. //
  487. ULONG Drift ;
  488. LONGLONG llCurrentTick ;
  489. llCurrentTick = RtlpGet64BitTickCount(&Last64BitTickCount) ;
  490. Drift = (ULONG) (llCurrentTick - RtlpGetResync64BitTickCount()) ;
  491. DueTime = (DueTime > Drift) ? DueTime-Drift : 1 ;
  492. RtlpSetFiring64BitTickCount(llCurrentTick + DueTime) ;
  493. }
  494. LongDueTime.QuadPart = (LONGLONG) UInt32x32To64( DueTime, 10000 );
  495. LongDueTime.QuadPart *= -1;
  496. }
  497. #if DBG
  498. if ((RtlpDueTimeMax != 0) && (DueTime > RtlpDueTimeMax)) {
  499. DbgPrint("\n*** Requested timer due time %d is greater than max allowed (%d)\n",
  500. DueTime,
  501. RtlpDueTimeMax);
  502. DbgBreakPoint();
  503. }
  504. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  505. RTLP_THREADPOOL_TRACE_MASK,
  506. "RtlpResetTimer: %dms => %p'%p in thread:<%x:%x>\n",
  507. DueTime,
  508. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  509. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  510. #endif
  511. NtSetTimer (
  512. TimerHandle,
  513. &LongDueTime,
  514. ThreadCB ? NULL : RtlpServiceTimer,
  515. NULL,
  516. FALSE,
  517. 0,
  518. NULL
  519. ) ;
  520. }
  521. #if _MSC_FULL_VER >= 13008827
  522. #pragma warning(push)
  523. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  524. #endif
  525. LONG
  526. RtlpTimerThread (
  527. PVOID Parameter
  528. )
  529. /*++
  530. Routine Description:
  531. All the timer activity takes place in APCs.
  532. Arguments:
  533. HandlePtr - Pointer to our handle
  534. Return Value:
  535. --*/
  536. {
  537. LARGE_INTEGER TimeOut ;
  538. // no structure initializations should be done here as new timer thread
  539. // may be created after threadPoolCleanup
  540. UNREFERENCED_PARAMETER(Parameter);
  541. #if DBG
  542. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  543. RTLP_THREADPOOL_TRACE_MASK,
  544. "Starting timer thread\n");
  545. #endif
  546. TimerThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  547. // Reset the NT Timer to never fire initially
  548. RtlpResetTimer (TimerHandle, -1, NULL) ;
  549. // Signal the thread creation path that we're ready to go
  550. NtSetEvent(TimerThreadStartedEvent, NULL);
  551. // Sleep alertably so that all the activity can take place
  552. // in APCs
  553. for ( ; ; ) {
  554. // Set timeout for the largest timeout possible
  555. TimeOut.LowPart = 0 ;
  556. TimeOut.HighPart = 0x80000000 ;
  557. NtDelayExecution (TRUE, &TimeOut) ;
  558. }
  559. return 0 ; // Keep compiler happy
  560. }
  561. #if _MSC_FULL_VER >= 13008827
  562. #pragma warning(pop)
  563. #endif
  564. NTSTATUS
  565. RtlpInitializeTimerThreadPool (
  566. )
  567. /*++
  568. Routine Description:
  569. This routine is used to initialize structures used for Timer Thread
  570. Arguments:
  571. Return Value:
  572. --*/
  573. {
  574. NTSTATUS Status = STATUS_SUCCESS;
  575. LARGE_INTEGER TimeOut ;
  576. PRTLP_EVENT Event;
  577. ASSERT(! RtlIsImpersonating());
  578. // In order to avoid an explicit RtlInitialize() function to initialize the wait thread pool
  579. // we use StartedTimerInitialization and CompletedTimerInitialization to provide us the
  580. // necessary synchronization to avoid multiple threads from initializing the thread pool.
  581. // This scheme does not work if RtlInitializeCriticalSection() or NtCreateEvent fails - but in this case the
  582. // caller has not choices left.
  583. if (!InterlockedExchange(&StartedTimerInitialization, 1L)) {
  584. if (CompletedTimerInitialization)
  585. InterlockedExchange(&CompletedTimerInitialization, 0 ) ;
  586. do {
  587. // Initialize global timer lock
  588. Status = RtlInitializeCriticalSection( &TimerCriticalSection ) ;
  589. if (! NT_SUCCESS( Status )) {
  590. break ;
  591. }
  592. Status = NtCreateTimer(
  593. &TimerHandle,
  594. TIMER_ALL_ACCESS,
  595. NULL,
  596. NotificationTimer
  597. ) ;
  598. if (!NT_SUCCESS(Status) ) {
  599. RtlDeleteCriticalSection( &TimerCriticalSection );
  600. break ;
  601. }
  602. InitializeListHead (&TimerQueues) ; // Initialize Timer Queue Structures
  603. // initialize tick count
  604. Resync64BitTickCount.QuadPart = NtGetTickCount() ;
  605. Firing64BitTickCount.QuadPart = 0 ;
  606. Event = RtlpGetWaitEvent();
  607. if (! Event) {
  608. Status = STATUS_NO_MEMORY;
  609. RtlDeleteCriticalSection(&TimerCriticalSection);
  610. NtClose(TimerHandle);
  611. TimerHandle = NULL;
  612. break;
  613. }
  614. TimerThreadStartedEvent = Event->Handle;
  615. Status = RtlpStartThreadpoolThread (RtlpTimerThread,
  616. NULL,
  617. &TimerThreadHandle);
  618. if (!NT_SUCCESS(Status) ) {
  619. RtlpFreeWaitEvent(Event);
  620. RtlDeleteCriticalSection( &TimerCriticalSection );
  621. NtClose(TimerHandle);
  622. TimerHandle = NULL;
  623. break ;
  624. }
  625. Status = NtWaitForSingleObject(TimerThreadStartedEvent,
  626. FALSE,
  627. NULL);
  628. RtlpFreeWaitEvent(Event);
  629. TimerThreadStartedEvent = NULL;
  630. if (! NT_SUCCESS(Status)) {
  631. RtlDeleteCriticalSection( &TimerCriticalSection );
  632. NtClose(TimerHandle);
  633. TimerHandle = NULL;
  634. break ;
  635. }
  636. } while(FALSE ) ;
  637. if (!NT_SUCCESS(Status) ) {
  638. StartedTimerInitialization = 0 ;
  639. InterlockedExchange (&CompletedTimerInitialization, ~0) ;
  640. return Status ;
  641. }
  642. InterlockedExchange (&CompletedTimerInitialization, 1L) ;
  643. } else {
  644. // Sleep 1 ms and see if the other thread has completed initialization
  645. ONE_MILLISECOND_TIMEOUT(TimeOut) ;
  646. while (!*((ULONG volatile *)&CompletedTimerInitialization)) {
  647. NtDelayExecution (FALSE, &TimeOut) ;
  648. }
  649. if (CompletedTimerInitialization != 1)
  650. Status = STATUS_NO_MEMORY ;
  651. }
  652. return NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ;
  653. }
  654. NTSTATUS
  655. RtlCreateTimerQueue(
  656. OUT PHANDLE TimerQueueHandle
  657. )
  658. /*++
  659. Routine Description:
  660. This routine creates a queue that can be used to queue time based tasks.
  661. Arguments:
  662. TimerQueueHandle - Returns back the Handle identifying the timer queue created.
  663. Return Value:
  664. NTSTATUS - Result code from call. The following are returned
  665. STATUS_SUCCESS - Timer Queue created successfully.
  666. STATUS_NO_MEMORY - There was not sufficient heap to perform the
  667. requested operation.
  668. --*/
  669. {
  670. PRTLP_TIMER_QUEUE Queue ;
  671. NTSTATUS Status;
  672. HANDLE Token = NULL;
  673. if (LdrpShutdownInProgress) {
  674. return STATUS_UNSUCCESSFUL;
  675. }
  676. Status = RtlpCaptureImpersonation(FALSE, &Token);
  677. if (! NT_SUCCESS(Status)) {
  678. return Status;
  679. }
  680. // Initialize the timer component if it hasnt been done already
  681. if (CompletedTimerInitialization != 1) {
  682. Status = RtlpInitializeTimerThreadPool () ;
  683. if ( !NT_SUCCESS(Status) )
  684. goto cleanup ;
  685. }
  686. InterlockedIncrement( &NumTimerQueues ) ;
  687. // Allocate a Queue structure
  688. Queue = (PRTLP_TIMER_QUEUE) RtlpAllocateTPHeap (
  689. sizeof (RTLP_TIMER_QUEUE),
  690. HEAP_ZERO_MEMORY
  691. ) ;
  692. if (Queue == NULL) {
  693. InterlockedDecrement( &NumTimerQueues ) ;
  694. Status = STATUS_NO_MEMORY;
  695. goto cleanup;
  696. }
  697. Queue->RefCount = 1 ;
  698. // Initialize the allocated queue
  699. InitializeListHead (&Queue->List) ;
  700. InitializeListHead (&Queue->TimerList) ;
  701. InitializeListHead (&Queue->UncancelledTimerList) ;
  702. SET_TIMER_QUEUE_SIGNATURE( Queue ) ;
  703. Queue->DeltaFiringTime = 0 ;
  704. #if DBG1
  705. Queue->DbgId = ++NextTimerDbgId ;
  706. Queue->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  707. #endif
  708. #if DBG
  709. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  710. RTLP_THREADPOOL_TRACE_MASK,
  711. "<%d:%d> TimerQueue %x created by thread:<%x:%x>\n",
  712. Queue->DbgId, 1, (ULONG_PTR)Queue,
  713. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  714. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  715. #endif
  716. *TimerQueueHandle = Queue ;
  717. Status = STATUS_SUCCESS;
  718. cleanup:
  719. if (Token) {
  720. RtlpRestartImpersonation(Token);
  721. NtClose(Token);
  722. }
  723. return Status;
  724. }
  725. ULONG
  726. RtlpGetQueueRelativeTime (
  727. PRTLP_TIMER_QUEUE Queue
  728. )
  729. /*++
  730. Routine Description:
  731. Walks the list of queues and returns the relative firing time by adding all the
  732. DeltaFiringTimes for all queues before it.
  733. Arguments:
  734. Queue - Queue for which to find the relative firing time
  735. Return Value:
  736. Time in milliseconds
  737. --*/
  738. {
  739. PLIST_ENTRY Node ;
  740. ULONG RelativeTime ;
  741. PRTLP_TIMER_QUEUE CurrentQueue ;
  742. RelativeTime = 0 ;
  743. // It the Queue is not attached to TimerQueues List because it has no timer
  744. // associated with it simply returns 0 as the relative time. Else run thru
  745. // all queues before it in the list and compute the relative firing time
  746. if (!IsListEmpty (&Queue->List)) {
  747. for (Node = TimerQueues.Flink; Node != &Queue->List; Node=Node->Flink) {
  748. CurrentQueue = CONTAINING_RECORD (Node, RTLP_TIMER_QUEUE, List) ;
  749. RelativeTime += CurrentQueue->DeltaFiringTime ;
  750. }
  751. // Add the queue's delta firing time as well
  752. RelativeTime += Queue->DeltaFiringTime ;
  753. }
  754. return RelativeTime ;
  755. }
  756. VOID
  757. RtlpDeactivateTimer (
  758. PRTLP_TIMER_QUEUE Queue,
  759. PRTLP_TIMER Timer
  760. )
  761. /*++
  762. Routine Description:
  763. This routine executes in an APC and cancels the specified timer if it exists
  764. Arguments:
  765. Timer - Specifies pointer to a timer structure that contains Queue and Timer information
  766. Return Value:
  767. --*/
  768. {
  769. ULONG TimeRemaining, QueueRelTimeRemaining ;
  770. ULONG NewFiringTime ;
  771. // Remove the timer from the appropriate queue
  772. TimeRemaining = RtlpGetTimeRemaining (TimerHandle) ;
  773. QueueRelTimeRemaining = TimeRemaining + RtlpGetQueueRelativeTime (Queue) ;
  774. #if DBG
  775. if ((RtlpDueTimeMax != 0)
  776. && (QueueRelTimeRemaining > RtlpDueTimeMax)) {
  777. DbgPrint("\n*** Queue due time %d is greater than max allowed (%d) in RtlpDeactivateTimer\n",
  778. QueueRelTimeRemaining,
  779. RtlpDueTimeMax);
  780. DbgBreakPoint();
  781. }
  782. #endif
  783. if (RtlpRemoveFromDeltaList (&Queue->TimerList, Timer, QueueRelTimeRemaining, &NewFiringTime)) {
  784. // If we removed the last timer from the queue then we should remove the queue
  785. // from TimerQueues, else we should readjust its position based on the delta time change
  786. if (IsListEmpty (&Queue->TimerList)) {
  787. // Remove the queue from TimerQueues
  788. if (RtlpRemoveFromDeltaList (&TimerQueues, Queue, TimeRemaining, &NewFiringTime)) {
  789. // There is a new element at the head of the queue we need to reset the NT
  790. // timer to fire later
  791. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  792. }
  793. InitializeListHead (&Queue->List) ;
  794. } else {
  795. // If we remove from the head of the timer delta list we will need to
  796. // make sure the queue delta list is readjusted
  797. if (RtlpReOrderDeltaList (&TimerQueues, Queue, TimeRemaining, &NewFiringTime, NewFiringTime)) {
  798. // There is a new element at the head of the queue we need to reset the NT
  799. // timer to fire later
  800. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  801. }
  802. }
  803. }
  804. }
  805. VOID
  806. RtlpCancelTimerEx (
  807. PRTLP_TIMER Timer,
  808. BOOLEAN DeletingQueue
  809. )
  810. /*++
  811. Routine Description:
  812. This routine cancels the specified timer.
  813. Arguments:
  814. Timer - Specifies pointer to a timer structure that contains Queue and Timer information
  815. DeletingQueue - FALSE: routine executing in an APC. Delete timer only.
  816. TRUE : routine called by timer queue which is being deleted. So dont
  817. reset the queue's position
  818. Return Value:
  819. --*/
  820. {
  821. PRTLP_TIMER_QUEUE Queue ;
  822. RtlpResync64BitTickCount() ;
  823. CHECK_SIGNATURE( Timer ) ;
  824. SET_DEL_SIGNATURE( Timer ) ;
  825. #if DBG
  826. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  827. RTLP_THREADPOOL_TRACE_MASK,
  828. "<%d:%d> RtlpCancelTimerEx: Timer: %p Thread<%d:%d>\n",
  829. Timer->Queue->DbgId,
  830. Timer->DbgId,
  831. Timer,
  832. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  833. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  834. #endif
  835. Queue = Timer->Queue ;
  836. if ( Timer->State & STATE_ACTIVE ) {
  837. // if queue is being deleted, then the timer should not be reset
  838. if ( ! DeletingQueue )
  839. RtlpDeactivateTimer( Queue, Timer ) ;
  840. } else {
  841. // remove one shot Inactive timer from Queue->UncancelledTimerList
  842. // called only when the time queue is being deleted
  843. RemoveEntryList( &Timer->List ) ;
  844. }
  845. // Set the State to deleted
  846. RtlInterlockedSetBitsDiscardReturn(&Timer->State,
  847. STATE_DELETE);
  848. // delete timer if refcount == 0
  849. if ( InterlockedDecrement( &Timer->RefCount ) == 0 ) {
  850. RtlpDeleteTimer( Timer ) ;
  851. }
  852. }
  853. VOID
  854. RtlpDeleteTimerQueueComplete (
  855. PRTLP_TIMER_QUEUE Queue
  856. )
  857. /*++
  858. Routine Description:
  859. This routine frees the queue and sets the event.
  860. Arguments:
  861. Queue - queue to delete
  862. Event - Event Handle used for signalling completion of request
  863. Return Value:
  864. --*/
  865. {
  866. #if DBG
  867. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  868. RTLP_THREADPOOL_TRACE_MASK,
  869. "<%d> Queue: %x: deleted\n", Queue->DbgId,
  870. (ULONG_PTR)Queue) ;
  871. #endif
  872. InterlockedDecrement( &NumTimerQueues ) ;
  873. // Notify the thread issuing the cancel that the request is completed
  874. if ( Queue->CompletionEvent )
  875. NtSetEvent (Queue->CompletionEvent, NULL) ;
  876. RtlpFreeTPHeap( Queue ) ;
  877. }
  878. NTSTATUS
  879. RtlpDeleteTimerQueue (
  880. PRTLP_TIMER_QUEUE Queue
  881. )
  882. /*++
  883. Routine Description:
  884. This routine deletes the queue specified in the Request and frees all timers
  885. Arguments:
  886. Queue - queue to delete
  887. Event - Event Handle used for signalling completion of request
  888. Return Value:
  889. --*/
  890. {
  891. ULONG TimeRemaining ;
  892. ULONG NewFiringTime ;
  893. PLIST_ENTRY Node ;
  894. PRTLP_TIMER Timer ;
  895. RtlpResync64BitTickCount() ;
  896. SET_DEL_SIGNATURE( Queue ) ;
  897. SET_DEL_TIMERQ_SIGNATURE( Queue ) ;
  898. // If there are no timers in the queue then it is not attached to TimerQueues
  899. // In this case simply free the memory and return. Otherwise we have to first
  900. // remove the queue from the TimerQueues List, update the firing time if this
  901. // was the first queue in the list and then walk all the timers and free them
  902. // before freeing the Timer Queue.
  903. if (!IsListEmpty (&Queue->List)) {
  904. TimeRemaining = RtlpGetTimeRemaining (TimerHandle)
  905. + RtlpGetQueueRelativeTime (Queue) ;
  906. #if DBG
  907. if ((RtlpDueTimeMax != 0)
  908. && (TimeRemaining > RtlpDueTimeMax)) {
  909. DbgPrint("\n*** Queue due time %d is greater than max allowed (%d) in RtlpDeleteTimerQueue\n",
  910. TimeRemaining,
  911. RtlpDueTimeMax);
  912. DbgBreakPoint();
  913. }
  914. #endif
  915. if (RtlpRemoveFromDeltaList (&TimerQueues, Queue, TimeRemaining,
  916. &NewFiringTime))
  917. {
  918. // If removed from head of queue list, reset the timer
  919. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  920. }
  921. // Free all the timers associated with this queue
  922. for (Node = Queue->TimerList.Flink ; Node != &Queue->TimerList ; ) {
  923. Timer = CONTAINING_RECORD (Node, RTLP_TIMER, List) ;
  924. Node = Node->Flink ;
  925. RtlpCancelTimerEx( Timer ,TRUE ) ; // Queue being deleted
  926. }
  927. }
  928. // Free all the uncancelled one shot timers in this queue
  929. for (Node = Queue->UncancelledTimerList.Flink ; Node != &Queue->UncancelledTimerList ; ) {
  930. Timer = CONTAINING_RECORD (Node, RTLP_TIMER, List) ;
  931. Node = Node->Flink ;
  932. RtlpCancelTimerEx( Timer ,TRUE ) ; // Queue being deleted
  933. }
  934. // delete the queue completely if the RefCount is 0
  935. if ( InterlockedDecrement( &Queue->RefCount ) == 0 ) {
  936. RtlpDeleteTimerQueueComplete( Queue ) ;
  937. return STATUS_SUCCESS ;
  938. } else {
  939. return STATUS_PENDING ;
  940. }
  941. }
  942. NTSTATUS
  943. RtlDeleteTimerQueueEx (
  944. HANDLE QueueHandle,
  945. HANDLE Event
  946. )
  947. /*++
  948. Routine Description:
  949. This routine deletes the queue specified in the Request and frees all timers.
  950. This call is blocking or non-blocking depending on the value passed for Event.
  951. Blocking calls cannot be made from ANY Timer callbacks. After this call returns,
  952. no new Callbacks will be fired for any timer associated with the queue.
  953. Arguments:
  954. QueueHandle - queue to delete
  955. Event - Event to wait upon.
  956. (HANDLE)-1: The function creates an event and waits on it.
  957. Event : The caller passes an event. The function marks the queue for deletion,
  958. but does not wait for all callbacks to complete. The event is
  959. signalled after all callbacks have completed.
  960. NULL : The function is non-blocking. The function marks the queue for deletion,
  961. but does not wait for all callbacks to complete.
  962. Return Value:
  963. STATUS_SUCCESS - All timer callbacks have completed.
  964. STATUS_PENDING - Non-Blocking call. Some timer callbacks associated with timers
  965. in this queue may not have completed.
  966. --*/
  967. {
  968. NTSTATUS Status;
  969. LARGE_INTEGER TimeOut ;
  970. PRTLP_EVENT CompletionEvent = NULL ;
  971. PRTLP_TIMER_QUEUE Queue = (PRTLP_TIMER_QUEUE)QueueHandle ;
  972. #if DBG
  973. ULONG QueueDbgId;
  974. #endif
  975. HANDLE Token = NULL;
  976. if (LdrpShutdownInProgress) {
  977. return STATUS_SUCCESS;
  978. }
  979. if (!Queue) {
  980. return STATUS_INVALID_PARAMETER_1 ;
  981. }
  982. Status = RtlpCaptureImpersonation(FALSE, &Token);
  983. if (! NT_SUCCESS(Status)) {
  984. return Status;
  985. }
  986. CHECK_DEL_SIGNATURE( Queue );
  987. SET_DEL_PENDING_SIGNATURE( Queue ) ;
  988. #if DBG1
  989. Queue->ThreadId2 = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  990. #endif
  991. #if DBG
  992. QueueDbgId = Queue->DbgId;
  993. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  994. RTLP_THREADPOOL_TRACE_MASK,
  995. "<%d:%d> Queue Delete(Queue:%x Event:%x by Thread:<%x:%x>)\n",
  996. QueueDbgId, Queue->RefCount, (ULONG_PTR)Queue, (ULONG_PTR)Event,
  997. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  998. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  999. #endif
  1000. if (Event == (HANDLE)-1 ) {
  1001. // Get an event from the event cache
  1002. CompletionEvent = RtlpGetWaitEvent () ;
  1003. if (!CompletionEvent) {
  1004. Status = STATUS_NO_MEMORY ;
  1005. goto cleanup;
  1006. }
  1007. }
  1008. Queue->CompletionEvent = CompletionEvent
  1009. ? CompletionEvent->Handle
  1010. : Event ;
  1011. // once this flag is set, no timer will be fired
  1012. ACQUIRE_GLOBAL_TIMER_LOCK();
  1013. RtlInterlockedSetBitsDiscardReturn(&Queue->State,
  1014. STATE_DONTFIRE);
  1015. RELEASE_GLOBAL_TIMER_LOCK();
  1016. // queue an APC
  1017. Status = NtQueueApcThread(
  1018. TimerThreadHandle,
  1019. (PPS_APC_ROUTINE)RtlpDeleteTimerQueue,
  1020. (PVOID) QueueHandle,
  1021. NULL,
  1022. NULL
  1023. );
  1024. if (! NT_SUCCESS(Status)) {
  1025. if ( CompletionEvent ) {
  1026. RtlpFreeWaitEvent( CompletionEvent ) ;
  1027. }
  1028. goto cleanup;
  1029. }
  1030. if (CompletionEvent) {
  1031. // wait for Event to be fired. Return if the thread has been killed.
  1032. #if DBG
  1033. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1034. RTLP_THREADPOOL_TRACE_MASK,
  1035. "<%d> Queue %p delete waiting Thread<%d:%d>\n",
  1036. QueueDbgId,
  1037. (ULONG_PTR)Queue,
  1038. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1039. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  1040. #endif
  1041. Status = RtlpWaitForEvent( CompletionEvent->Handle, TimerThreadHandle ) ;
  1042. #if DBG
  1043. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1044. RTLP_THREADPOOL_TRACE_MASK,
  1045. "<%d> Queue %p delete completed\n",
  1046. QueueDbgId,
  1047. (ULONG_PTR) Queue) ;
  1048. #endif
  1049. RtlpFreeWaitEvent( CompletionEvent ) ;
  1050. Status = NT_SUCCESS( Status ) ? STATUS_SUCCESS : Status ;
  1051. goto cleanup;
  1052. } else {
  1053. Status = STATUS_PENDING ;
  1054. goto cleanup;
  1055. }
  1056. cleanup:
  1057. if (Token) {
  1058. RtlpRestartImpersonation(Token);
  1059. NtClose(Token);
  1060. }
  1061. return Status;
  1062. }
  1063. NTSTATUS
  1064. RtlDeleteTimerQueue(
  1065. IN HANDLE TimerQueueHandle
  1066. )
  1067. /*++
  1068. Routine Description:
  1069. This routine deletes a previously created queue. This call is non-blocking and
  1070. can be made from Callbacks. Pending callbacks already queued to worker threads
  1071. are not cancelled.
  1072. Arguments:
  1073. TimerQueueHandle - Handle identifying the timer queue created.
  1074. Return Value:
  1075. NTSTATUS - Result code from call.
  1076. STATUS_PENDING - Timer Queue created successfully.
  1077. --*/
  1078. {
  1079. return RtlDeleteTimerQueueEx( TimerQueueHandle, NULL ) ;
  1080. }
  1081. VOID
  1082. RtlpAddTimer (
  1083. PRTLP_TIMER Timer,
  1084. PRTLP_EVENT StartEvent
  1085. )
  1086. /*++
  1087. Routine Description:
  1088. This routine runs as an APC into the Timer thread. It adds a new timer to the
  1089. specified queue.
  1090. Arguments:
  1091. Timer - Pointer to the timer to add
  1092. Return Value:
  1093. --*/
  1094. {
  1095. PRTLP_TIMER_QUEUE Queue = Timer->Queue;
  1096. ULONG TimeRemaining, QueueRelTimeRemaining ;
  1097. ULONG NewFiringTime ;
  1098. ASSERT(StartEvent);
  1099. NtWaitForSingleObject(StartEvent->Handle, FALSE, NULL);
  1100. RtlpFreeWaitEvent(StartEvent);
  1101. RtlpResync64BitTickCount() ;
  1102. // the timer was set to be deleted in a callback function.
  1103. if (Timer->State & STATE_DELETE ) {
  1104. RtlpDeleteTimer( Timer ) ;
  1105. return ;
  1106. }
  1107. // Check if the timer queue is already deleted -- that is, if it's
  1108. // delete has already been fully processed, not just enqueued.
  1109. if (IS_DEL_SIGNATURE_SET(Queue)) {
  1110. RtlpDeleteTimer(Timer);
  1111. return;
  1112. }
  1113. #if DBG
  1114. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1115. RTLP_THREADPOOL_TRACE_MASK,
  1116. "<%d:%d> RtlpAddTimer: Timer: %p Delta: %dms Period: %dms Thread<%d:%d>\n",
  1117. Timer->Queue->DbgId,
  1118. Timer->DbgId,
  1119. Timer,
  1120. Timer->DeltaFiringTime,
  1121. Timer->Period,
  1122. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1123. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  1124. #endif
  1125. // TimeRemaining is the time left in the current timer + the relative time of
  1126. // the queue it is being inserted into
  1127. TimeRemaining = RtlpGetTimeRemaining (TimerHandle) ;
  1128. QueueRelTimeRemaining = TimeRemaining + RtlpGetQueueRelativeTime (Queue) ;
  1129. #if DBG
  1130. if ((RtlpDueTimeMax != 0)
  1131. && (QueueRelTimeRemaining > RtlpDueTimeMax)) {
  1132. DbgPrint("\n*** Queue due time %d is greater than max allowed (%d) in RtlpAddTimer\n",
  1133. QueueRelTimeRemaining,
  1134. RtlpDueTimeMax);
  1135. DbgBreakPoint();
  1136. }
  1137. #endif
  1138. if (RtlpInsertInDeltaList (&Queue->TimerList, Timer, QueueRelTimeRemaining,
  1139. &NewFiringTime))
  1140. {
  1141. // If the Queue is not attached to TimerQueues since it had no timers
  1142. // previously then insert the queue into the TimerQueues list, else just
  1143. // reorder its existing position.
  1144. if (IsListEmpty (&Queue->List)) {
  1145. Queue->DeltaFiringTime = NewFiringTime ;
  1146. if (RtlpInsertInDeltaList (&TimerQueues, Queue, TimeRemaining,
  1147. &NewFiringTime))
  1148. {
  1149. // There is a new element at the head of the queue we need to reset the NT
  1150. // timer to fire sooner.
  1151. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  1152. }
  1153. } else {
  1154. // If we insert at the head of the timer delta list we will need to
  1155. // make sure the queue delta list is readjusted
  1156. if (RtlpReOrderDeltaList(&TimerQueues, Queue, TimeRemaining, &NewFiringTime, NewFiringTime)){
  1157. // There is a new element at the head of the queue we need to reset the NT
  1158. // timer to fire sooner.
  1159. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  1160. }
  1161. }
  1162. }
  1163. RtlInterlockedSetBitsDiscardReturn(&Timer->State,
  1164. STATE_REGISTERED | STATE_ACTIVE);
  1165. }
  1166. VOID
  1167. RtlpTimerReleaseWorker(ULONG Flags)
  1168. {
  1169. if (! (Flags & WT_EXECUTEINTIMERTHREAD)) {
  1170. RtlpReleaseWorker(Flags);
  1171. }
  1172. }
  1173. NTSTATUS
  1174. RtlCreateTimer(
  1175. IN HANDLE TimerQueueHandle,
  1176. OUT HANDLE *Handle,
  1177. IN WAITORTIMERCALLBACKFUNC Function,
  1178. IN PVOID Context,
  1179. IN ULONG DueTime,
  1180. IN ULONG Period,
  1181. IN ULONG Flags
  1182. )
  1183. /*++
  1184. Routine Description:
  1185. This routine puts a timer request in the queue identified in by TimerQueueHandle.
  1186. The timer request can be one shot or periodic.
  1187. Arguments:
  1188. TimerQueueHandle - Handle identifying the timer queue in which to insert the timer
  1189. request.
  1190. Handle - Specifies a location to return a handle to this timer request
  1191. Function - Routine that is called when the timer fires
  1192. Context - Opaque pointer passed in as an argument to WorkerProc
  1193. DueTime - Specifies the time in milliseconds after which the timer fires.
  1194. Period - Specifies the period of the timer in milliseconds. This should be 0 for
  1195. one shot requests.
  1196. Flags - Can be one of:
  1197. WT_EXECUTEINTIMERTHREAD - if WorkerProc should be invoked in the wait thread
  1198. it this should only be used for small routines.
  1199. WT_EXECUTELONGFUNCTION - if WorkerProc can possibly block for a long time.
  1200. WT_EXECUTEINIOTHREAD - if WorkerProc should be invoked in IO worker thread
  1201. Return Value:
  1202. NTSTATUS - Result code from call. The following are returned
  1203. STATUS_SUCCESS - Timer Queue created successfully.
  1204. STATUS_NO_MEMORY - There was not sufficient heap to perform the
  1205. requested operation.
  1206. --*/
  1207. {
  1208. NTSTATUS Status;
  1209. PRTLP_TIMER Timer;
  1210. PRTLP_TIMER_QUEUE Queue = (PRTLP_TIMER_QUEUE) TimerQueueHandle;
  1211. PRTLP_EVENT StartEvent;
  1212. HANDLE Token = NULL;
  1213. if (LdrpShutdownInProgress) {
  1214. return STATUS_UNSUCCESSFUL;
  1215. }
  1216. Status = RtlpCaptureImpersonation(Flags & WT_TRANSFER_IMPERSONATION,
  1217. &Token);
  1218. if (! NT_SUCCESS(Status)) {
  1219. return Status;
  1220. }
  1221. if (Flags&0xffff0000) {
  1222. MaxThreads = (Flags & 0xffff0000)>>16;
  1223. }
  1224. // check if timer queue already deleted
  1225. if (IS_DEL_PENDING_SIGNATURE_SET(Queue)) {
  1226. Status = STATUS_INVALID_HANDLE;
  1227. goto cleanup_token;
  1228. }
  1229. StartEvent = RtlpGetWaitEvent();
  1230. if (! StartEvent) {
  1231. Status = STATUS_NO_MEMORY;
  1232. goto cleanup_token;
  1233. }
  1234. if (! (Flags & WT_EXECUTEINTIMERTHREAD)) {
  1235. Status = RtlpAcquireWorker(Flags);
  1236. if (! NT_SUCCESS(Status)) {
  1237. goto cleanup_waitevent;
  1238. }
  1239. }
  1240. Timer = (PRTLP_TIMER) RtlpAllocateTPHeap (
  1241. sizeof (RTLP_TIMER),
  1242. HEAP_ZERO_MEMORY
  1243. ) ;
  1244. if (Timer == NULL) {
  1245. Status = STATUS_NO_MEMORY;
  1246. goto cleanup_worker;
  1247. }
  1248. // Initialize the allocated timer
  1249. Status = RtlpThreadPoolGetActiveActivationContext(&Timer->ActivationContext);
  1250. if (!NT_SUCCESS(Status)) {
  1251. if (Status == STATUS_SXS_THREAD_QUERIES_DISABLED) {
  1252. Timer->ActivationContext = INVALID_ACTIVATION_CONTEXT;
  1253. Status = STATUS_SUCCESS;
  1254. } else {
  1255. goto cleanup_timerblock;
  1256. }
  1257. }
  1258. if (Token && (Flags & WT_TRANSFER_IMPERSONATION)) {
  1259. Status = NtDuplicateToken(Token,
  1260. TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
  1261. NULL,
  1262. FALSE,
  1263. TokenImpersonation,
  1264. &Timer->ImpersonationToken);
  1265. if (! NT_SUCCESS(Status)) {
  1266. goto cleanup_actctx;
  1267. }
  1268. } else {
  1269. Timer->ImpersonationToken = NULL;
  1270. }
  1271. Timer->DeltaFiringTime = DueTime ;
  1272. Timer->Queue = Queue;
  1273. Timer->RefCount = 1 ;
  1274. Timer->Flags = Flags ;
  1275. Timer->Function = Function ;
  1276. Timer->Context = Context ;
  1277. //todo:remove below
  1278. Timer->Period = (Period == -1) ? 0 : Period;
  1279. InitializeListHead( &Timer->TimersToFireList ) ;
  1280. InitializeListHead( &Timer->List ) ;
  1281. SET_TIMER_SIGNATURE( Timer ) ;
  1282. #if DBG1
  1283. Timer->DbgId = ++ Timer->Queue->NextDbgId ;
  1284. Timer->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  1285. #endif
  1286. #if DBG
  1287. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1288. RTLP_THREADPOOL_TRACE_MASK,
  1289. "<%d:%d:%d> Timer: created by Thread:<%x:%x>\n",
  1290. Timer->Queue->DbgId, Timer->DbgId, 1,
  1291. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1292. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  1293. #endif
  1294. // Increment the total number of timers in the queue
  1295. InterlockedIncrement( &((PRTLP_TIMER_QUEUE)TimerQueueHandle)->RefCount ) ;
  1296. // Queue APC to timer thread
  1297. Status = NtQueueApcThread(
  1298. TimerThreadHandle,
  1299. (PPS_APC_ROUTINE)RtlpAddTimer,
  1300. (PVOID)Timer,
  1301. (PVOID)StartEvent,
  1302. NULL
  1303. ) ;
  1304. if (NT_SUCCESS(Status)) {
  1305. // We successfully queued the APC -- the timer is now valid
  1306. *Handle = Timer ;
  1307. NtSetEvent(StartEvent->Handle, NULL);
  1308. Status = STATUS_SUCCESS;
  1309. goto cleanup_token;
  1310. }
  1311. // Error path
  1312. if (InterlockedDecrement(&((PRTLP_TIMER_QUEUE)TimerQueueHandle)->RefCount)
  1313. == 0) {
  1314. RtlpDeleteTimerQueueComplete(Queue);
  1315. }
  1316. if (Timer->ImpersonationToken) {
  1317. NtClose(Timer->ImpersonationToken);
  1318. }
  1319. cleanup_actctx:
  1320. if (Timer->ActivationContext != INVALID_ACTIVATION_CONTEXT)
  1321. RtlReleaseActivationContext (Timer->ActivationContext);
  1322. cleanup_timerblock:
  1323. RtlpFreeTPHeap(Timer);
  1324. cleanup_worker:
  1325. RtlpTimerReleaseWorker(Flags);
  1326. cleanup_waitevent:
  1327. RtlpFreeWaitEvent(StartEvent);
  1328. // Common cleanup path
  1329. cleanup_token:
  1330. if (Token) {
  1331. RtlpRestartImpersonation(Token);
  1332. NtClose(Token);
  1333. }
  1334. return Status ;
  1335. }
  1336. VOID
  1337. RtlpUpdateTimer (
  1338. PRTLP_TIMER Timer,
  1339. PRTLP_TIMER UpdatedTimer
  1340. )
  1341. /*++
  1342. Routine Description:
  1343. This routine executes in an APC and updates the specified timer if it exists
  1344. Arguments:
  1345. Timer - Timer that is actually updated
  1346. UpdatedTimer - Specifies pointer to a timer structure that contains Queue and
  1347. Timer information
  1348. Return Value:
  1349. --*/
  1350. {
  1351. PRTLP_TIMER_QUEUE Queue ;
  1352. ULONG TimeRemaining, QueueRelTimeRemaining ;
  1353. ULONG NewFiringTime ;
  1354. #if DBG
  1355. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1356. RTLP_THREADPOOL_TRACE_MASK,
  1357. "<%d:%d> RtlpUpdateTimer: Timer: %p Updated: %p Delta: %dms Period: %dms Thread<%d:%d>\n",
  1358. Timer->Queue->DbgId,
  1359. Timer->DbgId,
  1360. Timer,
  1361. UpdatedTimer,
  1362. UpdatedTimer->DeltaFiringTime,
  1363. UpdatedTimer->Period,
  1364. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1365. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  1366. #endif
  1367. try {
  1368. RtlpResync64BitTickCount( ) ;
  1369. CHECK_SIGNATURE(Timer) ;
  1370. Queue = Timer->Queue ;
  1371. if (IS_DEL_SIGNATURE_SET(Queue)) {
  1372. leave;
  1373. }
  1374. // Update the periodic time on the timer
  1375. Timer->Period = UpdatedTimer->Period ;
  1376. // if timer is not in active state, then dont update it
  1377. if ( ! ( Timer->State & STATE_ACTIVE ) ) {
  1378. leave;
  1379. }
  1380. // Get the time remaining on the NT timer
  1381. TimeRemaining = RtlpGetTimeRemaining (TimerHandle) ;
  1382. QueueRelTimeRemaining = TimeRemaining + RtlpGetQueueRelativeTime (Queue) ;
  1383. #if DBG
  1384. if ((RtlpDueTimeMax != 0)
  1385. && (QueueRelTimeRemaining > RtlpDueTimeMax)) {
  1386. DbgPrint("\n*** Queue due time %d is greater than max allowed (%d) in RtlpUpdateTimer\n",
  1387. QueueRelTimeRemaining,
  1388. RtlpDueTimeMax);
  1389. DbgBreakPoint();
  1390. }
  1391. #endif
  1392. // Update the timer based on the due time
  1393. if (RtlpReOrderDeltaList (&Queue->TimerList, Timer, QueueRelTimeRemaining,
  1394. &NewFiringTime,
  1395. UpdatedTimer->DeltaFiringTime))
  1396. {
  1397. // If this update caused the timer at the head of the queue to change, then reinsert
  1398. // this queue in the list of queues.
  1399. if (RtlpReOrderDeltaList (&TimerQueues, Queue, TimeRemaining, &NewFiringTime, NewFiringTime)) {
  1400. // NT timer needs to be updated since the change caused the queue at the head of
  1401. // the TimerQueues to change.
  1402. RtlpResetTimer (TimerHandle, NewFiringTime, NULL) ;
  1403. }
  1404. }
  1405. } finally {
  1406. RtlpFreeTPHeap( UpdatedTimer ) ;
  1407. }
  1408. }
  1409. NTSTATUS
  1410. RtlUpdateTimer(
  1411. IN HANDLE TimerQueueHandle,
  1412. IN HANDLE Timer,
  1413. IN ULONG DueTime,
  1414. IN ULONG Period
  1415. )
  1416. /*++
  1417. Routine Description:
  1418. This routine updates the timer
  1419. Arguments:
  1420. TimerQueueHandle - Handle identifying the queue in which the timer to be updated exists
  1421. Timer - Specifies a handle to the timer which needs to be updated
  1422. DueTime - Specifies the time in milliseconds after which the timer fires.
  1423. Period - Specifies the period of the timer in milliseconds. This should be
  1424. 0 for one shot requests.
  1425. Return Value:
  1426. NTSTATUS - Result code from call. The following are returned
  1427. STATUS_SUCCESS - Timer updated successfully.
  1428. --*/
  1429. {
  1430. NTSTATUS Status;
  1431. PRTLP_TIMER TmpTimer, ActualTimer=(PRTLP_TIMER)Timer ;
  1432. PRTLP_TIMER_QUEUE Queue = (PRTLP_TIMER_QUEUE) TimerQueueHandle;
  1433. if (LdrpShutdownInProgress) {
  1434. return STATUS_UNSUCCESSFUL;
  1435. }
  1436. if (!TimerQueueHandle) {
  1437. return STATUS_INVALID_PARAMETER_1;
  1438. }
  1439. if (!Timer) {
  1440. return STATUS_INVALID_PARAMETER_2;
  1441. }
  1442. // check if timer queue already deleted
  1443. if (IS_DEL_PENDING_SIGNATURE_SET(Queue)) {
  1444. return STATUS_INVALID_HANDLE;
  1445. }
  1446. CHECK_DEL_SIGNATURE(ActualTimer) ;
  1447. TmpTimer = (PRTLP_TIMER) RtlpAllocateTPHeap (
  1448. sizeof (RTLP_TIMER),
  1449. 0
  1450. ) ;
  1451. if (TmpTimer == NULL) {
  1452. return STATUS_NO_MEMORY ;
  1453. }
  1454. TmpTimer->DeltaFiringTime = DueTime;
  1455. //todo:remove below
  1456. if (Period==-1) Period = 0;
  1457. TmpTimer->Period = Period ;
  1458. #if DBG1
  1459. ActualTimer->ThreadId2 =
  1460. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  1461. #endif
  1462. #if DBG
  1463. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1464. RTLP_THREADPOOL_TRACE_MASK,
  1465. "<%d:%d:%d> Timer: updated by Thread:<%x:%x>\n",
  1466. ((PRTLP_TIMER)Timer)->Queue->DbgId,
  1467. ((PRTLP_TIMER)Timer)->DbgId, ((PRTLP_TIMER)Timer)->RefCount,
  1468. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1469. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  1470. #endif
  1471. // queue APC to update timer
  1472. Status = NtQueueApcThread (
  1473. TimerThreadHandle,
  1474. (PPS_APC_ROUTINE)RtlpUpdateTimer,
  1475. (PVOID)Timer, //Actual timer
  1476. (PVOID)TmpTimer,
  1477. NULL
  1478. );
  1479. if (!NT_SUCCESS (Status)) {
  1480. RtlpFreeTPHeap(TmpTimer);
  1481. }
  1482. return Status ;
  1483. }
  1484. VOID
  1485. RtlpCancelTimer (
  1486. PRTLP_TIMER Timer
  1487. )
  1488. /*++
  1489. Routine Description:
  1490. This routine executes in an APC and cancels the specified timer if it exists
  1491. Arguments:
  1492. Timer - Specifies pointer to a timer structure that contains Queue and Timer information
  1493. Return Value:
  1494. --*/
  1495. {
  1496. RtlpCancelTimerEx( Timer, FALSE ) ; // queue not being deleted
  1497. }
  1498. NTSTATUS
  1499. RtlDeleteTimer (
  1500. IN HANDLE TimerQueueHandle,
  1501. IN HANDLE TimerToCancel,
  1502. IN HANDLE Event
  1503. )
  1504. /*++
  1505. Routine Description:
  1506. This routine cancels the timer
  1507. Arguments:
  1508. TimerQueueHandle - Handle identifying the queue from which to delete timer
  1509. TimerToCancel - Handle identifying the timer to cancel
  1510. Event - Event to be signalled when the timer is deleted
  1511. (HANDLE)-1: The function creates an event and waits on it.
  1512. Event : The caller passes an event. The function marks the timer for deletion,
  1513. but does not wait for all callbacks to complete. The event is
  1514. signalled after all callbacks have completed.
  1515. NULL : The function is non-blocking. The function marks the timer for deletion,
  1516. but does not wait for all callbacks to complete.
  1517. Return Value:
  1518. NTSTATUS - Result code from call. The following are returned
  1519. STATUS_SUCCESS - Timer cancelled. No pending callbacks.
  1520. STATUS_PENDING - Timer cancelled. Some callbacks still not completed.
  1521. --*/
  1522. {
  1523. NTSTATUS Status;
  1524. PRTLP_EVENT CompletionEvent = NULL ;
  1525. PRTLP_TIMER Timer = (PRTLP_TIMER) TimerToCancel ;
  1526. ULONG TimerRefCount ;
  1527. HANDLE Token = NULL;
  1528. #if DBG
  1529. ULONG QueueDbgId ;
  1530. #endif
  1531. if (LdrpShutdownInProgress) {
  1532. return STATUS_SUCCESS;
  1533. }
  1534. if (!TimerQueueHandle) {
  1535. return STATUS_INVALID_PARAMETER_1 ;
  1536. }
  1537. if (!TimerToCancel) {
  1538. return STATUS_INVALID_PARAMETER_2 ;
  1539. }
  1540. Status = RtlpCaptureImpersonation(FALSE, &Token);
  1541. if (! NT_SUCCESS(Status)) {
  1542. return Status;
  1543. }
  1544. #if DBG
  1545. QueueDbgId = Timer->Queue->DbgId ;
  1546. #endif
  1547. CHECK_DEL_SIGNATURE( Timer );
  1548. SET_DEL_PENDING_SIGNATURE( Timer );
  1549. CHECK_DEL_PENDING_SIGNATURE( (PRTLP_TIMER_QUEUE)TimerQueueHandle ) ;
  1550. if (Event == (HANDLE)-1 ) {
  1551. // Get an event from the event cache
  1552. CompletionEvent = RtlpGetWaitEvent () ;
  1553. if (!CompletionEvent) {
  1554. Status = STATUS_NO_MEMORY ;
  1555. goto cleanup;
  1556. }
  1557. }
  1558. #if DBG1
  1559. Timer->ThreadId2 = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  1560. #endif
  1561. #if DBG
  1562. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1563. RTLP_THREADPOOL_TRACE_MASK,
  1564. "<%d:%d:%d> Timer: Cancel:(Timer:%x, Event:%x)\n",
  1565. Timer->Queue->DbgId, Timer->DbgId, Timer->RefCount,
  1566. (ULONG_PTR)Timer, (ULONG_PTR)Event) ;
  1567. #endif
  1568. Timer->CompletionEvent = CompletionEvent
  1569. ? CompletionEvent->Handle
  1570. : Event ;
  1571. ACQUIRE_GLOBAL_TIMER_LOCK();
  1572. RtlInterlockedSetBitsDiscardReturn(&Timer->State,
  1573. STATE_DONTFIRE);
  1574. TimerRefCount = Timer->RefCount ;
  1575. RELEASE_GLOBAL_TIMER_LOCK();
  1576. Status = NtQueueApcThread(
  1577. TimerThreadHandle,
  1578. (PPS_APC_ROUTINE)RtlpCancelTimer,
  1579. (PVOID)TimerToCancel,
  1580. NULL,
  1581. NULL
  1582. );
  1583. if (! NT_SUCCESS(Status)) {
  1584. if ( CompletionEvent ) {
  1585. RtlpFreeWaitEvent( CompletionEvent ) ;
  1586. }
  1587. goto cleanup;
  1588. }
  1589. if ( CompletionEvent ) {
  1590. // wait for the event to be signalled
  1591. #if DBG
  1592. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1593. RTLP_THREADPOOL_TRACE_MASK,
  1594. "<%d> Timer: %x: Cancel waiting Thread<%d:%d>\n",
  1595. QueueDbgId, (ULONG_PTR)Timer,
  1596. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1597. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  1598. #endif
  1599. Status = RtlpWaitForEvent( CompletionEvent->Handle, TimerThreadHandle ) ;
  1600. #if DBG
  1601. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1602. RTLP_THREADPOOL_TRACE_MASK,
  1603. "<%d> Timer: %x: Cancel waiting done\n", QueueDbgId,
  1604. (ULONG_PTR)Timer) ;
  1605. #endif
  1606. RtlpFreeWaitEvent( CompletionEvent ) ;
  1607. Status = NT_SUCCESS(Status) ? STATUS_SUCCESS : Status ;
  1608. } else {
  1609. Status = (TimerRefCount > 1) ? STATUS_PENDING : STATUS_SUCCESS;
  1610. }
  1611. cleanup:
  1612. if (Token) {
  1613. RtlpRestartImpersonation(Token);
  1614. NtClose(Token);
  1615. }
  1616. return Status;
  1617. }
  1618. VOID
  1619. RtlpDeleteTimer (
  1620. PRTLP_TIMER Timer
  1621. )
  1622. /*++
  1623. Routine Description:
  1624. This routine executes in worker or timer thread and deletes the timer
  1625. whose RefCount == 0. The function can be called outside timer thread,
  1626. so no structure outside Timer can be touched (no list etc).
  1627. Arguments:
  1628. Timer - Specifies pointer to a timer structure that contains Queue and Timer information
  1629. Return Value:
  1630. --*/
  1631. {
  1632. PRTLP_TIMER_QUEUE Queue = Timer->Queue ;
  1633. HANDLE Event;
  1634. CHECK_SIGNATURE( Timer ) ;
  1635. CLEAR_SIGNATURE( Timer ) ;
  1636. #if DBG
  1637. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1638. RTLP_THREADPOOL_TRACE_MASK,
  1639. "<%d> Timer: %x: deleted\n", Timer->Queue->DbgId,
  1640. (ULONG_PTR)Timer) ;
  1641. #endif
  1642. // safe to call this. Either the timer is in the TimersToFireList and
  1643. // the function is being called in time context or else it is not in the
  1644. // list
  1645. RemoveEntryList( &Timer->TimersToFireList ) ;
  1646. Event = Timer->CompletionEvent;
  1647. // decrement the total number of timers in the queue
  1648. if ( InterlockedDecrement( &Queue->RefCount ) == 0 )
  1649. RtlpDeleteTimerQueueComplete( Queue ) ;
  1650. RtlpTimerReleaseWorker(Timer->Flags);
  1651. if (Timer->ActivationContext != INVALID_ACTIVATION_CONTEXT)
  1652. RtlReleaseActivationContext(Timer->ActivationContext);
  1653. if (Timer->ImpersonationToken) {
  1654. NtClose(Timer->ImpersonationToken);
  1655. }
  1656. RtlpFreeTPHeap( Timer ) ;
  1657. if ( Event ) {
  1658. NtSetEvent( Event, NULL ) ;
  1659. }
  1660. }
  1661. ULONG
  1662. RtlpGetTimeRemaining (
  1663. HANDLE TimerHandle
  1664. )
  1665. /*++
  1666. Routine Description:
  1667. Gets the time remaining on the specified NT timer
  1668. Arguments:
  1669. TimerHandle - Handle to the NT timer
  1670. Return Value:
  1671. Time remaining on the timer
  1672. --*/
  1673. {
  1674. ULONG InfoLen ;
  1675. TIMER_BASIC_INFORMATION Info ;
  1676. NTSTATUS Status ;
  1677. LARGE_INTEGER RemainingTime;
  1678. Status = NtQueryTimer (TimerHandle, TimerBasicInformation, &Info, sizeof(Info), &InfoLen) ;
  1679. if (! NT_SUCCESS(Status)) {
  1680. ASSERTMSG ("NtQueryTimer failed", Status == STATUS_SUCCESS) ;
  1681. return 0;
  1682. }
  1683. #if DBG
  1684. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1685. RTLP_THREADPOOL_TRACE_MASK,
  1686. "RtlpGetTimeRemaining: Read SignalState %d, time %p'%p in thread:<%x:%x>\n",
  1687. Info.TimerState,
  1688. Info.RemainingTime.HighPart,
  1689. Info.RemainingTime.LowPart,
  1690. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1691. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  1692. #endif
  1693. // Due to an executive bug, Info.TimerState and Info.RemainingTime
  1694. // may be out of sync -- it's possible for us to be told that the
  1695. // timer has not fired, but that it will fire very far into the
  1696. // future (because we use ULONGLONGs), when in fact it's *just* fired.
  1697. //
  1698. // So: if the time remaining on the timer is negative, we'll
  1699. // assume that it just fired, and invert it. We'll use this as
  1700. // our signal state, too, instead of trusting the one from the
  1701. // executive.
  1702. if (Info.RemainingTime.QuadPart < 0) {
  1703. // The timer has fired.
  1704. return 0;
  1705. } else {
  1706. // Capture the remaining time.
  1707. RemainingTime = Info.RemainingTime;
  1708. // Translate the remaining time from 100ns units to ms,
  1709. // clamping at PSEUDO_INFINITE_TIME.
  1710. RemainingTime.QuadPart /= (10 * 1000); /* 100ns per ms */
  1711. if (RemainingTime.QuadPart > PSEUDO_INFINITE_TIME) {
  1712. RemainingTime.QuadPart = PSEUDO_INFINITE_TIME;
  1713. }
  1714. ASSERT(RemainingTime.HighPart == 0);
  1715. #if DBG
  1716. if ((RtlpDueTimeMax != 0)
  1717. && ((ULONG) RemainingTime.LowPart > RtlpDueTimeMax)) {
  1718. DbgPrint("\n*** Discovered timer due time %d is greater than max allowed (%d)\n",
  1719. RemainingTime.LowPart,
  1720. RtlpDueTimeMax);
  1721. DbgBreakPoint();
  1722. }
  1723. #endif
  1724. return RemainingTime.LowPart;
  1725. }
  1726. }
  1727. BOOLEAN
  1728. RtlpInsertInDeltaList (
  1729. PLIST_ENTRY DeltaList,
  1730. PRTLP_GENERIC_TIMER NewTimer,
  1731. ULONG TimeRemaining,
  1732. ULONG *NewFiringTime
  1733. )
  1734. /*++
  1735. Routine Description:
  1736. Inserts the timer element in the appropriate place in the delta list.
  1737. Arguments:
  1738. DeltaList - Delta list to insert into
  1739. NewTimer - Timer element to insert into list
  1740. TimeRemaining - This time must be added to the head of the list to get "real"
  1741. relative time.
  1742. NewFiringTime - If the new element was inserted at the head of the list - this
  1743. will contain the new firing time in milliseconds. The caller
  1744. can use this time to re-program the NT timer. This MUST NOT be
  1745. changed if the function returns FALSE.
  1746. Return Value:
  1747. TRUE - If the timer was inserted at head of delta list
  1748. FALSE - otherwise
  1749. --*/
  1750. {
  1751. PLIST_ENTRY Node ;
  1752. PRTLP_GENERIC_TIMER Temp ;
  1753. PRTLP_GENERIC_TIMER Head ;
  1754. if (IsListEmpty (DeltaList)) {
  1755. InsertHeadList (DeltaList, &NewTimer->List) ;
  1756. *NewFiringTime = NewTimer->DeltaFiringTime ;
  1757. NewTimer->DeltaFiringTime = 0 ;
  1758. return TRUE ;
  1759. }
  1760. // Adjust the head of the list to reflect the time remaining on the NT timer
  1761. Head = CONTAINING_RECORD (DeltaList->Flink, RTLP_GENERIC_TIMER, List) ;
  1762. Head->DeltaFiringTime += TimeRemaining ;
  1763. // Find the appropriate location to insert this element in
  1764. for (Node = DeltaList->Flink ; Node != DeltaList ; Node = Node->Flink) {
  1765. Temp = CONTAINING_RECORD (Node, RTLP_GENERIC_TIMER, List) ;
  1766. if (Temp->DeltaFiringTime <= NewTimer->DeltaFiringTime) {
  1767. NewTimer->DeltaFiringTime -= Temp->DeltaFiringTime ;
  1768. } else {
  1769. // found appropriate place to insert this timer
  1770. break ;
  1771. }
  1772. }
  1773. // Either we have found the appopriate node to insert before in terms of deltas.
  1774. // OR we have come to the end of the list. Insert this timer here.
  1775. InsertHeadList (Node->Blink, &NewTimer->List) ;
  1776. // If this isnt the last element in the list - adjust the delta of the
  1777. // next element
  1778. if (Node != DeltaList) {
  1779. Temp->DeltaFiringTime -= NewTimer->DeltaFiringTime ;
  1780. }
  1781. // Check if element was inserted at head of list
  1782. if (DeltaList->Flink == &NewTimer->List) {
  1783. // Set NewFiringTime to the time in milliseconds when the new head of list
  1784. // should be serviced.
  1785. *NewFiringTime = NewTimer->DeltaFiringTime ;
  1786. // This means the timer must be programmed to service this request
  1787. NewTimer->DeltaFiringTime = 0 ;
  1788. return TRUE ;
  1789. } else {
  1790. // No change to the head of the list, set the delta time back
  1791. Head->DeltaFiringTime -= TimeRemaining ;
  1792. return FALSE ;
  1793. }
  1794. }
  1795. BOOLEAN
  1796. RtlpRemoveFromDeltaList (
  1797. PLIST_ENTRY DeltaList,
  1798. PRTLP_GENERIC_TIMER Timer,
  1799. ULONG TimeRemaining,
  1800. ULONG* NewFiringTime
  1801. )
  1802. /*++
  1803. Routine Description:
  1804. Removes the specified timer from the delta list
  1805. Arguments:
  1806. DeltaList - Delta list to insert into
  1807. Timer - Timer element to insert into list
  1808. TimerHandle - Handle of the NT Timer object
  1809. TimeRemaining - This time must be added to the head of the list to get "real"
  1810. relative time.
  1811. Return Value:
  1812. TRUE if the timer was removed from head of timer list
  1813. FALSE otherwise
  1814. --*/
  1815. {
  1816. PLIST_ENTRY Next ;
  1817. PRTLP_GENERIC_TIMER Temp ;
  1818. Next = Timer->List.Flink ;
  1819. RemoveEntryList (&Timer->List) ;
  1820. if (IsListEmpty (DeltaList)) {
  1821. *NewFiringTime = INFINITE_TIME ;
  1822. return TRUE ;
  1823. }
  1824. if (Next == DeltaList) {
  1825. // If we removed the last element in the list nothing to do either
  1826. return FALSE ;
  1827. } else {
  1828. Temp = CONTAINING_RECORD ( Next, RTLP_GENERIC_TIMER, List) ;
  1829. Temp->DeltaFiringTime += Timer->DeltaFiringTime ;
  1830. // Check if element was removed from head of list
  1831. if (DeltaList->Flink == Next) {
  1832. *NewFiringTime = Temp->DeltaFiringTime + TimeRemaining ;
  1833. Temp->DeltaFiringTime = 0 ;
  1834. return TRUE ;
  1835. } else {
  1836. return FALSE ;
  1837. }
  1838. }
  1839. }
  1840. BOOLEAN
  1841. RtlpReOrderDeltaList (
  1842. PLIST_ENTRY DeltaList,
  1843. PRTLP_GENERIC_TIMER Timer,
  1844. ULONG TimeRemaining,
  1845. ULONG *NewFiringTime,
  1846. ULONG ChangedFiringTime
  1847. )
  1848. /*++
  1849. Routine Description:
  1850. Called when a timer in the delta list needs to be re-inserted because the firing time
  1851. has changed.
  1852. Arguments:
  1853. DeltaList - List in which to re-insert
  1854. Timer - Timer for which the firing time has changed
  1855. TimeRemaining - Time before the head of the delta list is fired
  1856. NewFiringTime - If the new element was inserted at the head of the list - this
  1857. will contain the new firing time in milliseconds. The caller
  1858. can use this time to re-program the NT timer.
  1859. ChangedFiringTime - Changed Time for the specified timer.
  1860. Return Value:
  1861. TRUE if the timer was removed from head of timer list
  1862. FALSE otherwise
  1863. --*/
  1864. {
  1865. ULONG NewTimeRemaining ;
  1866. PRTLP_GENERIC_TIMER Temp ;
  1867. // Remove the timer from the list
  1868. if (RtlpRemoveFromDeltaList (DeltaList, Timer, TimeRemaining, NewFiringTime)) {
  1869. // If element was removed from the head of the list we should record that
  1870. NewTimeRemaining = *NewFiringTime ;
  1871. } else {
  1872. // Element was not removed from head of delta list, the current TimeRemaining is valid
  1873. NewTimeRemaining = TimeRemaining ;
  1874. }
  1875. // Before inserting Timer, set its delta time to the ChangedFiringTime
  1876. Timer->DeltaFiringTime = ChangedFiringTime ;
  1877. // Reinsert this element back in the list
  1878. if (!RtlpInsertInDeltaList (DeltaList, Timer, NewTimeRemaining, NewFiringTime)) {
  1879. // If we did not add at the head of the list, then we should return TRUE if
  1880. // RtlpRemoveFromDeltaList() had returned TRUE. We also update the NewFiringTime to
  1881. // the reflect the new firing time returned by RtlpRemoveFromDeltaList()
  1882. *NewFiringTime = NewTimeRemaining ;
  1883. return (NewTimeRemaining != TimeRemaining) ;
  1884. } else {
  1885. // NewFiringTime contains the time the NT timer must be programmed for
  1886. return TRUE ;
  1887. }
  1888. }
  1889. VOID
  1890. RtlpAddTimerQueue (
  1891. PVOID Queue
  1892. )
  1893. /*++
  1894. Routine Description:
  1895. This routine runs as an APC into the Timer thread. It does whatever necessary to
  1896. create a new timer queue
  1897. Arguments:
  1898. Queue - Pointer to the queue to add
  1899. Return Value:
  1900. --*/
  1901. {
  1902. // We do nothing here. The newly created queue is free floating until a timer is
  1903. // queued onto it.
  1904. }
  1905. VOID
  1906. RtlpProcessTimeouts (
  1907. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB
  1908. )
  1909. /*++
  1910. Routine Description:
  1911. This routine processes timeouts for the wait thread
  1912. Arguments:
  1913. ThreadCB - The wait thread to add the wait to
  1914. Return Value:
  1915. --*/
  1916. {
  1917. ULONG NewFiringTime, TimeRemaining ;
  1918. LIST_ENTRY TimersToFireList ;
  1919. //
  1920. // check if incorrect timer fired
  1921. //
  1922. if (ThreadCB->Firing64BitTickCount >
  1923. RtlpGet64BitTickCount(&ThreadCB->Current64BitTickCount) + 200 )
  1924. {
  1925. RtlpResetTimer (ThreadCB->TimerHandle,
  1926. RtlpGetTimeRemaining (ThreadCB->TimerHandle),
  1927. ThreadCB) ;
  1928. return ;
  1929. }
  1930. InitializeListHead( &TimersToFireList ) ;
  1931. // Walk thru the timer list and fire all waits with DeltaFiringTime == 0
  1932. RtlpFireTimersAndReorder (&ThreadCB->TimerQueue, &NewFiringTime, &TimersToFireList) ;
  1933. // Reset the NT timer
  1934. RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ;
  1935. RtlpFireTimers( &TimersToFireList ) ;
  1936. }
  1937. NTSTATUS
  1938. RtlpTimerCleanup(
  1939. VOID
  1940. )
  1941. {
  1942. BOOLEAN Cleanup;
  1943. IS_COMPONENT_INITIALIZED(StartedTimerInitialization,
  1944. CompletedTimerInitialization,
  1945. Cleanup ) ;
  1946. if ( Cleanup ) {
  1947. ACQUIRE_GLOBAL_TIMER_LOCK() ;
  1948. if (NumTimerQueues != 0 ) {
  1949. RELEASE_GLOBAL_TIMER_LOCK() ;
  1950. return STATUS_UNSUCCESSFUL ;
  1951. }
  1952. NtQueueApcThread(
  1953. TimerThreadHandle,
  1954. (PPS_APC_ROUTINE)RtlpThreadCleanup,
  1955. NULL,
  1956. NULL,
  1957. NULL
  1958. );
  1959. NtClose( TimerThreadHandle ) ;
  1960. TimerThreadHandle = NULL ;
  1961. RELEASE_GLOBAL_TIMER_LOCK() ;
  1962. }
  1963. return STATUS_SUCCESS;
  1964. }
  1965. #if DBG
  1966. VOID
  1967. PrintTimerQueue(PLIST_ENTRY QNode, ULONG Delta, ULONG Count
  1968. )
  1969. {
  1970. PLIST_ENTRY Tnode ;
  1971. PRTLP_TIMER Timer ;
  1972. PRTLP_TIMER_QUEUE Queue ;
  1973. Queue = CONTAINING_RECORD (QNode, RTLP_TIMER_QUEUE, List) ;
  1974. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1975. RTLP_THREADPOOL_VERBOSE_MASK,
  1976. "<%1d> Queue: %x FiringTime:%d\n", Count, (ULONG_PTR)Queue,
  1977. Queue->DeltaFiringTime);
  1978. for (Tnode=Queue->TimerList.Flink; Tnode!=&Queue->TimerList;
  1979. Tnode=Tnode->Flink)
  1980. {
  1981. Timer = CONTAINING_RECORD (Tnode, RTLP_TIMER, List) ;
  1982. Delta += Timer->DeltaFiringTime ;
  1983. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1984. RTLP_THREADPOOL_VERBOSE_MASK,
  1985. " Timer: %x Delta:%d Period:%d\n",(ULONG_PTR)Timer,
  1986. Delta, Timer->Period);
  1987. }
  1988. }
  1989. #endif
  1990. VOID
  1991. RtlDebugPrintTimes (
  1992. )
  1993. {
  1994. #if DBG
  1995. PLIST_ENTRY QNode ;
  1996. ULONG Count = 0 ;
  1997. ULONG Delta = RtlpGetTimeRemaining (TimerHandle) ;
  1998. ULONG CurrentThreadId =
  1999. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  2000. RtlpResync64BitTickCount();
  2001. if (CompletedTimerInitialization != 1) {
  2002. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2003. RTLP_THREADPOOL_ERROR_MASK,
  2004. "RtlTimerThread not yet initialized\n");
  2005. return ;
  2006. }
  2007. if (CurrentThreadId == TimerThreadId)
  2008. {
  2009. PRTLP_TIMER_QUEUE Queue ;
  2010. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2011. RTLP_THREADPOOL_VERBOSE_MASK,
  2012. "================Printing timerqueues====================\n");
  2013. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2014. RTLP_THREADPOOL_VERBOSE_MASK,
  2015. "TimeRemaining: %d\n", Delta);
  2016. for (QNode = TimerQueues.Flink; QNode != &TimerQueues;
  2017. QNode = QNode->Flink)
  2018. {
  2019. Queue = CONTAINING_RECORD (QNode, RTLP_TIMER_QUEUE, List) ;
  2020. Delta += Queue->DeltaFiringTime ;
  2021. PrintTimerQueue(QNode, Delta, ++Count);
  2022. }
  2023. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2024. RTLP_THREADPOOL_VERBOSE_MASK,
  2025. "================Printed ================================\n");
  2026. }
  2027. else
  2028. {
  2029. NtQueueApcThread(
  2030. TimerThreadHandle,
  2031. (PPS_APC_ROUTINE)RtlDebugPrintTimes,
  2032. NULL,
  2033. NULL,
  2034. NULL
  2035. );
  2036. }
  2037. #endif
  2038. return;
  2039. }
  2040. /*DO NOT USE THIS FUNCTION: REPLACED BY RTLCREATETIMER*/
  2041. NTSTATUS
  2042. RtlSetTimer(
  2043. IN HANDLE TimerQueueHandle,
  2044. OUT HANDLE *Handle,
  2045. IN WAITORTIMERCALLBACKFUNC Function,
  2046. IN PVOID Context,
  2047. IN ULONG DueTime,
  2048. IN ULONG Period,
  2049. IN ULONG Flags
  2050. )
  2051. {
  2052. #if DBG
  2053. static ULONG Count = 0;
  2054. if (Count++ ==0 ) {
  2055. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2056. RTLP_THREADPOOL_ERROR_MASK,
  2057. "Using obsolete function call: RtlSetTimer\n");
  2058. DbgBreakPoint();
  2059. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2060. RTLP_THREADPOOL_ERROR_MASK,
  2061. "Using obsolete function call: RtlSetTimer\n");
  2062. }
  2063. #endif
  2064. return RtlCreateTimer(TimerQueueHandle,
  2065. Handle,
  2066. Function,
  2067. Context,
  2068. DueTime,
  2069. Period,
  2070. Flags
  2071. ) ;
  2072. }
  2073. /*DO NOT USE THIS FUNCTION: REPLACED BY RTLDeleteTimer*/
  2074. NTSTATUS
  2075. RtlCancelTimer(
  2076. IN HANDLE TimerQueueHandle,
  2077. IN HANDLE TimerToCancel
  2078. )
  2079. /*++
  2080. Routine Description:
  2081. This routine cancels the timer. This call is non-blocking. The timer Callback
  2082. will not be executed after this call returns.
  2083. Arguments:
  2084. TimerQueueHandle - Handle identifying the queue from which to delete timer
  2085. TimerToCancel - Handle identifying the timer to cancel
  2086. Return Value:
  2087. NTSTATUS - Result code from call. The following are returned
  2088. STATUS_SUCCESS - Timer cancelled. All callbacks completed.
  2089. STATUS_PENDING - Timer cancelled. Some callbacks still not completed.
  2090. --*/
  2091. {
  2092. #if DBG
  2093. static ULONG Count = 0;
  2094. if (Count++ ==0) {
  2095. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2096. RTLP_THREADPOOL_ERROR_MASK,
  2097. "Using obsolete function call: RtlCancelTimer\n");
  2098. DbgBreakPoint();
  2099. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  2100. RTLP_THREADPOOL_ERROR_MASK,
  2101. "Using obsolete function call: RtlCancelTimer\n");
  2102. }
  2103. #endif
  2104. return RtlDeleteTimer( TimerQueueHandle, TimerToCancel, NULL ) ;
  2105. }