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.

668 lines
16 KiB

  1. /*************************************************************************
  2. *
  3. * timer.c
  4. *
  5. * Common timer routines
  6. *
  7. * Copyright Microsoft Corporation, 1998
  8. *
  9. *
  10. *************************************************************************/
  11. /*
  12. * Includes
  13. */
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. /*=============================================================================
  17. == Local structures
  18. =============================================================================*/
  19. typedef VOID (*PCLIBTIMERFUNC)(PVOID);
  20. typedef NTSTATUS (*PCREATETHREAD)( PUSER_THREAD_START_ROUTINE, PVOID, BOOLEAN, PHANDLE );
  21. /*
  22. * Timer thread structure
  23. */
  24. typedef struct _CLIBTIMERTHREAD {
  25. HANDLE hTimerThread;
  26. HANDLE hTimer;
  27. LIST_ENTRY TimerHead;
  28. } CLIBTIMERTHREAD, * PCLIBTIMERTHREAD;
  29. /*
  30. * Timer structures
  31. */
  32. typedef struct _CLIBTIMER {
  33. PCLIBTIMERTHREAD pThread;
  34. LIST_ENTRY Links;
  35. LARGE_INTEGER ExpireTime;
  36. PCLIBTIMERFUNC pFunc;
  37. PVOID pParam;
  38. ULONG Flags;
  39. } CLIBTIMER, * PCLIBTIMER;
  40. #define TIMER_ENABLED 0x00000001
  41. /*=============================================================================
  42. == External Functions Defined
  43. =============================================================================*/
  44. NTSTATUS IcaTimerCreate( ULONG, HANDLE * );
  45. NTSTATUS IcaTimerStart( HANDLE, PVOID, PVOID, ULONG );
  46. BOOLEAN IcaTimerCancel( HANDLE );
  47. BOOLEAN IcaTimerClose( HANDLE );
  48. /*=============================================================================
  49. == Internal Functions Defined
  50. =============================================================================*/
  51. NTSTATUS _TimersInit( PCLIBTIMERTHREAD );
  52. NTSTATUS _TimerSet( PCLIBTIMERTHREAD );
  53. BOOLEAN _TimerRemove( PCLIBTIMERTHREAD, PCLIBTIMER, BOOLEAN );
  54. DWORD _TimerThread( PCLIBTIMERTHREAD );
  55. /*=============================================================================
  56. == Global data
  57. =============================================================================*/
  58. CLIBTIMERTHREAD ThreadData[ 3 ];
  59. /*******************************************************************************
  60. *
  61. * _TimersInit
  62. *
  63. * Initialize timers for process
  64. *
  65. * NOTE: timer semaphore must be locked
  66. *
  67. *
  68. * ENTRY:
  69. * pThread (input)
  70. * pointer to timer thread structure
  71. *
  72. * EXIT:
  73. * NO_ERROR : successful
  74. *
  75. ******************************************************************************/
  76. NTSTATUS
  77. _TimersInit( PCLIBTIMERTHREAD pThread )
  78. {
  79. ULONG Tid;
  80. NTSTATUS Status;
  81. /*
  82. * Check if someone beat us here
  83. */
  84. if ( pThread->hTimerThread )
  85. return( STATUS_SUCCESS );
  86. /*
  87. * Initialize timer variables
  88. */
  89. InitializeListHead( &pThread->TimerHead );
  90. pThread->hTimerThread = NULL;
  91. pThread->hTimer = NULL;
  92. /*
  93. * Create timer object
  94. */
  95. Status = NtCreateTimer( &pThread->hTimer, TIMER_ALL_ACCESS, NULL, NotificationTimer );
  96. if ( !NT_SUCCESS(Status) )
  97. goto badtimer;
  98. pThread->hTimerThread = CreateThread( NULL,
  99. 0,
  100. _TimerThread,
  101. pThread,
  102. THREAD_SET_INFORMATION,
  103. &Tid );
  104. if ( !pThread->hTimerThread ) {
  105. Status = NtCurrentTeb()->LastStatusValue;
  106. goto badthread;
  107. }
  108. SetThreadPriority( pThread->hTimerThread, THREAD_PRIORITY_TIME_CRITICAL-2 );
  109. return( STATUS_SUCCESS );
  110. /*=============================================================================
  111. == Error returns
  112. =============================================================================*/
  113. /*
  114. * bad thread create
  115. */
  116. badthread:
  117. NtClose( pThread->hTimer );
  118. /*
  119. * bad timer object
  120. */
  121. badtimer:
  122. pThread->hTimerThread = NULL;
  123. ASSERT( Status == STATUS_SUCCESS );
  124. return( Status );
  125. }
  126. /*******************************************************************************
  127. *
  128. * IcaTimerCreate
  129. *
  130. * Create a timer
  131. *
  132. *
  133. * ENTRY:
  134. * TimerThread (input)
  135. * index of time thread (TIMERTHREAD_?) clib.h
  136. * phTimer (output)
  137. * address to return timer handle
  138. *
  139. * EXIT:
  140. * STATUS_SUCCESS - no error
  141. *
  142. *
  143. ******************************************************************************/
  144. NTSTATUS
  145. IcaTimerCreate( ULONG TimerThread, HANDLE * phTimer )
  146. {
  147. PCLIBTIMER pTimer;
  148. NTSTATUS Status;
  149. PCLIBTIMERTHREAD pThread;
  150. if ( TimerThread >= 3 )
  151. return( STATUS_INVALID_PARAMETER );
  152. /*
  153. * Lock timer semaphore
  154. */
  155. RtlEnterCriticalSection( &TimerCritSec );
  156. /*
  157. * Get pointer to thread structure
  158. */
  159. pThread = &ThreadData[ TimerThread ];
  160. /*
  161. * Make sure timers are initialized
  162. */
  163. if ( pThread->hTimerThread == NULL ) {
  164. Status = _TimersInit( pThread );
  165. if ( !NT_SUCCESS(Status) )
  166. goto badinit;
  167. }
  168. /*
  169. * Unlock timer semaphore
  170. */
  171. RtlLeaveCriticalSection( &TimerCritSec );
  172. /*
  173. * Allocate timer event
  174. */
  175. pTimer = MemAlloc( sizeof(CLIBTIMER) );
  176. if ( !pTimer ) {
  177. Status = STATUS_NO_MEMORY;
  178. goto badmalloc;
  179. }
  180. /*
  181. * Initialize timer event
  182. */
  183. RtlZeroMemory( pTimer, sizeof(CLIBTIMER) );
  184. pTimer->pThread = pThread;
  185. *phTimer = (HANDLE) pTimer;
  186. return( STATUS_SUCCESS );
  187. /*=============================================================================
  188. == Error returns
  189. =============================================================================*/
  190. /*
  191. * timer create failed
  192. * timer initialization failed
  193. */
  194. // badmalloc:
  195. badinit:
  196. RtlLeaveCriticalSection( &TimerCritSec );
  197. badmalloc: /* makarp; dont LeaveCritical Section in case of bad malloc as we have done it already. #182846*/
  198. *phTimer = NULL;
  199. return( Status );
  200. }
  201. /*******************************************************************************
  202. *
  203. * IcaTimerStart
  204. *
  205. * Start a timer
  206. *
  207. *
  208. * ENTRY:
  209. * TimerHandle (input)
  210. * timer handle
  211. * pFunc (input)
  212. * address of procedure to call when timer expires
  213. * pParam (input)
  214. * parameter to pass to procedure
  215. * TimeLeft (input)
  216. * relative time until timer expires (1/1000 seconds)
  217. *
  218. * EXIT:
  219. * NO_ERROR : successful
  220. *
  221. *
  222. ******************************************************************************/
  223. NTSTATUS
  224. IcaTimerStart( HANDLE TimerHandle,
  225. PVOID pFunc,
  226. PVOID pParam,
  227. ULONG TimeLeft )
  228. {
  229. PCLIBTIMER pTimer;
  230. PCLIBTIMER pNextTimer;
  231. LARGE_INTEGER CurrentTime;
  232. LARGE_INTEGER Time;
  233. PLIST_ENTRY Head, Next;
  234. BOOLEAN fSetTimer = FALSE;
  235. NTSTATUS Status;
  236. PCLIBTIMERTHREAD pThread;
  237. /*
  238. * Lock timer semaphore
  239. */
  240. RtlEnterCriticalSection( &TimerCritSec );
  241. /*
  242. * Get timer pointer
  243. */
  244. pTimer = (PCLIBTIMER) TimerHandle;
  245. pThread = pTimer->pThread;
  246. /*
  247. * Remove timer if it is enabled
  248. * (If the timer was the head entry, then fSetTimer
  249. * will be TRUE and _TimerSet will be called below.)
  250. */
  251. if ( (pTimer->Flags & TIMER_ENABLED) )
  252. fSetTimer = _TimerRemove( pThread, pTimer, FALSE );
  253. /*
  254. * Initialize timer event
  255. */
  256. Time = RtlEnlargedUnsignedMultiply( TimeLeft, 10000 );
  257. (VOID) NtQuerySystemTime( &CurrentTime );
  258. pTimer->ExpireTime = RtlLargeIntegerAdd( CurrentTime, Time );
  259. pTimer->pFunc = pFunc;
  260. pTimer->pParam = pParam;
  261. /*
  262. * Locate correct spot in linked list to insert this entry
  263. */
  264. Head = &pThread->TimerHead;
  265. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  266. pNextTimer = CONTAINING_RECORD( Next, CLIBTIMER, Links );
  267. if ( RtlLargeIntegerLessThan( pTimer->ExpireTime,
  268. pNextTimer->ExpireTime ) )
  269. break;
  270. }
  271. /*
  272. * Insert timer event into timer list.
  273. * (InsertTailList inserts 'pTimer' entry in front of 'Next' entry.
  274. * If 'Next' points to TimerHead, either because the list is empty,
  275. * or because we searched thru the entire list and got back to the
  276. * head, this will insert the new entry at the tail.)
  277. */
  278. InsertTailList( Next, &pTimer->Links );
  279. pTimer->Flags |= TIMER_ENABLED;
  280. /*
  281. * Update timer if needed.
  282. * (If we just added this entry to the head of the list, the timer
  283. * needs to be set. Also, if fSetTimer is TRUE, then this entry was
  284. * removed by _TimerRemove and was the head entry, so set the timer.)
  285. */
  286. if ( pThread->TimerHead.Flink == &pTimer->Links || fSetTimer ) {
  287. Status = _TimerSet( pThread );
  288. if ( !NT_SUCCESS(Status) )
  289. goto badset;
  290. }
  291. /*
  292. * Unlock timer semaphore
  293. */
  294. RtlLeaveCriticalSection( &TimerCritSec );
  295. return( STATUS_SUCCESS );
  296. /*=============================================================================
  297. == Error returns
  298. =============================================================================*/
  299. /*
  300. * timer set failed
  301. * timer create failed
  302. * timer initialization failed
  303. */
  304. badset:
  305. RtlLeaveCriticalSection( &TimerCritSec );
  306. ASSERT( Status == STATUS_SUCCESS );
  307. return( Status );
  308. }
  309. /*******************************************************************************
  310. *
  311. * IcaTimerCancel
  312. *
  313. * cancel the specified timer
  314. *
  315. *
  316. * ENTRY:
  317. * TimerHandle (input)
  318. * timer handle
  319. *
  320. * EXIT:
  321. * TRUE : timer was actually canceled
  322. * FALSE : timer was not armed
  323. *
  324. *
  325. ******************************************************************************/
  326. BOOLEAN
  327. IcaTimerCancel( HANDLE TimerHandle )
  328. {
  329. PCLIBTIMERTHREAD pThread;
  330. PCLIBTIMER pTimer;
  331. BOOLEAN fCanceled = FALSE;
  332. /*
  333. * Lock timer semaphore
  334. */
  335. RtlEnterCriticalSection( &TimerCritSec );
  336. /*
  337. * Get timer pointer
  338. */
  339. pTimer = (PCLIBTIMER) TimerHandle;
  340. pThread = pTimer->pThread;
  341. /*
  342. * Remove timer if it is enabled
  343. */
  344. if ( (pTimer->Flags & TIMER_ENABLED) ) {
  345. _TimerRemove( pThread, pTimer, TRUE );
  346. fCanceled = TRUE;
  347. }
  348. /*
  349. * Unlock timer semaphore
  350. */
  351. RtlLeaveCriticalSection( &TimerCritSec );
  352. return( fCanceled );
  353. }
  354. /*******************************************************************************
  355. *
  356. * IcaTimerClose
  357. *
  358. * cancel the specified timer
  359. *
  360. *
  361. * ENTRY:
  362. * TimerHandle (input)
  363. * timer handle
  364. *
  365. * EXIT:
  366. * TRUE : timer was actually canceled
  367. * FALSE : timer was not armed
  368. *
  369. *
  370. ******************************************************************************/
  371. BOOLEAN
  372. IcaTimerClose( HANDLE TimerHandle )
  373. {
  374. BOOLEAN fCanceled;
  375. /*
  376. * Cancel timer if it is enabled
  377. */
  378. fCanceled = IcaTimerCancel( TimerHandle );
  379. /*
  380. * Free timer memory
  381. */
  382. MemFree( TimerHandle );
  383. return( fCanceled );
  384. }
  385. /*******************************************************************************
  386. *
  387. * _TimerSet
  388. *
  389. * set the timer
  390. *
  391. * NOTE: timer semaphore must be locked
  392. *
  393. *
  394. * ENTRY:
  395. * pThread (input)
  396. * pointer to timer thread structure
  397. *
  398. * EXIT:
  399. * NO_ERROR : successful
  400. *
  401. *
  402. ******************************************************************************/
  403. NTSTATUS
  404. _TimerSet( PCLIBTIMERTHREAD pThread )
  405. {
  406. PCLIBTIMER pTimer;
  407. LARGE_INTEGER Time;
  408. // the following is roughly 1 year in 100 nanosecond increments
  409. static LARGE_INTEGER LongWaitTime = { 0, 0x00010000 };
  410. /*
  411. * Get ExpireTime for next timer entry or 'large' value if none
  412. */
  413. if ( pThread->TimerHead.Flink != &pThread->TimerHead ) {
  414. pTimer = CONTAINING_RECORD( pThread->TimerHead.Flink, CLIBTIMER, Links );
  415. Time = pTimer->ExpireTime;
  416. } else {
  417. LARGE_INTEGER CurrentTime;
  418. NtQuerySystemTime( &CurrentTime );
  419. Time = RtlLargeIntegerAdd( CurrentTime, LongWaitTime );
  420. }
  421. /*
  422. * Set the timer
  423. */
  424. return( NtSetTimer( pThread->hTimer, &Time, NULL, NULL, FALSE, 0, NULL ) );
  425. }
  426. /*******************************************************************************
  427. *
  428. * _TimerRemove
  429. *
  430. * remove the specified timer from the timer list
  431. * and optionally set the time for the next timer to trigger
  432. *
  433. * NOTE: timer semaphore must be locked
  434. *
  435. *
  436. * ENTRY:
  437. * pThread (input)
  438. * pointer to timer thread structure
  439. * pTimer (input)
  440. * timer entry pointer
  441. * SetTimer (input)
  442. * BOOLEAN which indicates if _TimerSet should be called
  443. *
  444. * EXIT:
  445. * TRUE : timer needs to be set (removed entry was head of list)
  446. * FALSE : timer does not need to be set
  447. *
  448. *
  449. ******************************************************************************/
  450. BOOLEAN
  451. _TimerRemove( PCLIBTIMERTHREAD pThread, PCLIBTIMER pTimer, BOOLEAN fSetTimer )
  452. {
  453. BOOLEAN fSetNeeded = FALSE;
  454. NTSTATUS Status;
  455. /*
  456. * See if timer is currently enabled
  457. */
  458. if ( (pTimer->Flags & TIMER_ENABLED) ) {
  459. /*
  460. * Unlink the entry from the timer list and clear enabled flag
  461. */
  462. RemoveEntryList( &pTimer->Links );
  463. pTimer->Flags &= ~TIMER_ENABLED;
  464. /*
  465. * If we removed the head entry, then set the timer
  466. * or indicate to caller that it needs to be set.
  467. */
  468. if ( pTimer->Links.Blink == &pThread->TimerHead ) {
  469. if ( fSetTimer ) {
  470. Status = _TimerSet( pThread );
  471. ASSERT( Status == STATUS_SUCCESS );
  472. } else {
  473. fSetNeeded = TRUE;
  474. }
  475. }
  476. }
  477. return( fSetNeeded );
  478. }
  479. /*******************************************************************************
  480. *
  481. * _TimerThread
  482. *
  483. *
  484. * ENTRY:
  485. * pThread (input)
  486. * pointer to timer thread structure
  487. *
  488. * EXIT:
  489. * STATUS_SUCCESS - no error
  490. *
  491. ******************************************************************************/
  492. DWORD
  493. _TimerThread( PCLIBTIMERTHREAD pThread )
  494. {
  495. PCLIBTIMER pTimer;
  496. PCLIBTIMERFUNC pFunc;
  497. PVOID pParam;
  498. LARGE_INTEGER CurrentTime;
  499. NTSTATUS Status;
  500. for (;;) {
  501. /*
  502. * Wait on timer
  503. */
  504. Status = NtWaitForSingleObject( pThread->hTimer, TRUE, NULL );
  505. /*
  506. * Check for an error
  507. */
  508. if ( Status != STATUS_WAIT_0 )
  509. break;
  510. /*
  511. * Lock semaphore
  512. */
  513. RtlEnterCriticalSection( &TimerCritSec );
  514. /*
  515. * Make sure a timer entry exists
  516. */
  517. if ( IsListEmpty( &pThread->TimerHead ) ) {
  518. Status = _TimerSet( pThread );
  519. ASSERT( Status == STATUS_SUCCESS );
  520. RtlLeaveCriticalSection( &TimerCritSec );
  521. continue;
  522. }
  523. /*
  524. * Make sure the head entry should be removed now.
  525. * (The timer may have been triggered while the
  526. * head entry was being removed.)
  527. */
  528. pTimer = CONTAINING_RECORD( pThread->TimerHead.Flink, CLIBTIMER, Links );
  529. NtQuerySystemTime( &CurrentTime );
  530. if ( RtlLargeIntegerGreaterThan( pTimer->ExpireTime, CurrentTime ) ) {
  531. Status = _TimerSet( pThread );
  532. ASSERT( Status == STATUS_SUCCESS );
  533. RtlLeaveCriticalSection( &TimerCritSec );
  534. continue;
  535. }
  536. /*
  537. * Remove the entry and indicate it is no longer enabled
  538. */
  539. RemoveEntryList( &pTimer->Links );
  540. pTimer->Flags &= ~TIMER_ENABLED;
  541. /*
  542. * Set the timer for next time
  543. */
  544. Status = _TimerSet( pThread );
  545. ASSERT( Status == STATUS_SUCCESS );
  546. /*
  547. * Get all the data we need out of the timer structure
  548. */
  549. pFunc = pTimer->pFunc;
  550. pParam = pTimer->pParam;
  551. /*
  552. * Unload semaphore
  553. */
  554. RtlLeaveCriticalSection( &TimerCritSec );
  555. /*
  556. * Call timer function
  557. */
  558. if ( pFunc ) {
  559. (*pFunc)( pParam );
  560. }
  561. }
  562. pThread->hTimerThread = NULL;
  563. return( STATUS_SUCCESS );
  564. }