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.

804 lines
24 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+-------------------------------------------------------------------------
  3. //
  4. // Microsoft Windows
  5. //
  6. // File: timer.cxx
  7. //
  8. // Contents: Code for a timer.
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. //
  15. //
  16. // History: 18-Nov-96 BillMo Created.
  17. //
  18. // Notes:
  19. //
  20. // Codework:
  21. //
  22. //--------------------------------------------------------------------------
  23. #include <pch.cxx>
  24. #pragma hdrstop
  25. #include "trklib.hxx"
  26. BOOL
  27. LoadPersistentFileTime(
  28. const TCHAR * ptszStaticRegName,
  29. CFILETIME * pcft
  30. );
  31. void
  32. UpdatePersistentFileTime(
  33. const TCHAR * ptszStaticRegName,
  34. const CFILETIME & cft
  35. );
  36. void
  37. RemovePersistentFileTime(
  38. const TCHAR * ptszStaticRegName
  39. );
  40. //+----------------------------------------------------------------------------
  41. //
  42. // Method: CNewTimer::Initiliaze
  43. //
  44. // Synopsis: Initialize the object and create the timer but don't set it
  45. // yet.
  46. //
  47. // Inputs: [pTimerCallback] (in)
  48. // Who to call when the timer fires.
  49. // [pWorkManager] (in)
  50. // The WorkManager with which the timer will be registered
  51. // [ptszName] (in, optional)
  52. // If specified, the timer is persistent, and the name is
  53. // used to store the timer information in the registry.
  54. // If not specified the timer is not persistent.
  55. // If data already exists in the registry for this name, it
  56. // is used the next time the timer is set.
  57. // [ulTimerContext] (in)
  58. // Passed to pTimerCallback->Timer.
  59. // [ulPeriodInSeconds] (in)
  60. // The length of this timer when it's set.
  61. // [retrytype] (in)
  62. // From the TimerRetryType enumeration. Can
  63. // be no_retry, retry_randomly, or retry_with_backoff.
  64. // [ulLowerRetryTime] (in)
  65. // Only valid when retrytype isn't no_retry.
  66. // [ulUpperRetryTime] (in)
  67. // Only valid when retrytype isn't no_retry.
  68. // [ulMaxLifetime] (in)
  69. // Only valid when retrytype isn't no_retry.
  70. //
  71. // Returns: Void
  72. //
  73. //+----------------------------------------------------------------------------
  74. void
  75. CNewTimer::Initialize( PTimerCallback *pTimerCallback,
  76. const TCHAR *ptszName,
  77. ULONG ulTimerContext,
  78. ULONG ulPeriodInSeconds,
  79. TimerRetryType retrytype,
  80. ULONG ulLowerRetryTime,
  81. ULONG ulUpperRetryTime,
  82. ULONG ulMaxLifetime )
  83. {
  84. NTSTATUS Status = STATUS_SUCCESS;
  85. CFILETIME cftLastTimeSet;
  86. TrkAssert( ulLowerRetryTime <= ulUpperRetryTime );
  87. TrkAssert( NO_RETRY == retrytype
  88. ||
  89. 0 != ulLowerRetryTime
  90. &&
  91. 0 != ulUpperRetryTime );
  92. TrkAssert( NO_RETRY != retrytype || 0 == ulMaxLifetime );
  93. // Initialize our critical section. _fIntitializeCalled is used to
  94. // indicate that this has been done.
  95. _cs.Initialize();
  96. _fInitializeCalled = TRUE;
  97. // Keep the parameters
  98. _pTimerCallback = pTimerCallback;
  99. _ptszName = ptszName;
  100. _ulTimerContext = ulTimerContext;
  101. _ulPeriodInSeconds = ulPeriodInSeconds;
  102. _RetryType = retrytype;
  103. _ulLowerRetryTime = ulLowerRetryTime;
  104. _ulUpperRetryTime = ulUpperRetryTime;
  105. _ulMaxLifetime = ulMaxLifetime;
  106. #if DBG
  107. // Set the workitem signature for use in debug outputs.
  108. _stprintf( _tszWorkItemSig, TEXT("CTimer:%p"), this );
  109. if( NULL != ptszName )
  110. {
  111. _tcscat( _tszWorkItemSig, TEXT("/") );
  112. _tcscat( _tszWorkItemSig, ptszName );
  113. }
  114. TrkAssert( _tcslen(_tszWorkItemSig) < ELEMENTS(_tszWorkItemSig) );
  115. #endif
  116. // Create the NT timer.
  117. Status = NtCreateTimer(
  118. &_hTimer,
  119. TIMER_ALL_ACCESS,
  120. NULL,
  121. SynchronizationTimer ); // this sort of timer becomes un-signalled
  122. // when a wait is satisfied
  123. if (!NT_SUCCESS(Status))
  124. {
  125. _hTimer = NULL;
  126. TrkRaiseNtStatus(Status);
  127. }
  128. // If this is a persistent timer, load the persisted state from the
  129. // registry.
  130. LoadFromRegistry();
  131. // Register a work item with the Win32 thread pool.
  132. _hRegisterWaitForSingleObjectEx
  133. = TrkRegisterWaitForSingleObjectEx( _hTimer, ThreadPoolCallbackFunction,
  134. static_cast<PWorkItem*>(this), INFINITE,
  135. WT_EXECUTELONGFUNCTION | WT_EXECUTEDEFAULT );
  136. if( NULL == _hRegisterWaitForSingleObjectEx )
  137. {
  138. TrkLog(( TRKDBG_ERROR, TEXT("Failed RegisterWaitForSingleObjectEx in CNewTimer::Initialize (%lu) for %s)"),
  139. GetLastError(), GetTimerName() ));
  140. TrkRaiseLastError();
  141. }
  142. else
  143. TrkLog(( TRKDBG_TIMER, TEXT("Registered timer %s with thread pool (%p/%p)"),
  144. GetTimerName(), this, *reinterpret_cast<UINT_PTR*>(this) ));
  145. } // CNewTimer::Initialize
  146. //+----------------------------------------------------------------------------
  147. //
  148. // Method: CNewTimer::SetTimer
  149. //
  150. // Synopsis: Start the timer. If _cftDue isn't already set, we'll use
  151. // _ulPeriodInSeconds (or _ulCurrentRetryTime) to set it. If this
  152. // is a persistent timer, the registry is updated.
  153. //
  154. //+----------------------------------------------------------------------------
  155. void
  156. CNewTimer::SetTimer()
  157. {
  158. NTSTATUS Status;
  159. CFILETIME cftNow, cftMax(0);
  160. TrkAssert(NULL != _hTimer);
  161. TrkAssert(sizeof(LARGE_INTEGER) == sizeof(_cftDue));
  162. // If we're already running and not in retry mode, then there's nothing
  163. // to be done.
  164. if( _fRunning && 0 == _ulCurrentRetryTime )
  165. return;
  166. // Has the due time already been determined?
  167. if( 0 == _cftDue )
  168. {
  169. // Are we in retry mode?
  170. if( 0 != _ulCurrentRetryTime )
  171. {
  172. _cftDue = cftNow;
  173. _cftDue.IncrementSeconds( _ulCurrentRetryTime );
  174. }
  175. else
  176. {
  177. _cftDue = cftNow;
  178. _cftDue.IncrementSeconds( _ulPeriodInSeconds );
  179. _cftSet = cftNow;
  180. }
  181. }
  182. // We already have a non-zero due time. That doesn't mean that we're running,
  183. // though, it might be a persistent timer that's been initialized but not
  184. // started.
  185. else if( _fRunning )
  186. {
  187. TrkAssert( 0 != _ulCurrentRetryTime );
  188. // This timer was in retry mode but is now being started again.
  189. // Restart as if it was being started for the first time.
  190. _ulCurrentRetryTime = 0;
  191. _cftDue = _cftSet = cftNow;
  192. _cftDue.IncrementSeconds( _ulPeriodInSeconds );
  193. }
  194. if( 0 < _ulMaxLifetime )
  195. {
  196. cftMax = _cftSet;
  197. cftMax.IncrementSeconds( _ulMaxLifetime );
  198. if( cftNow > cftMax )
  199. {
  200. TrkLog(( TRKDBG_TIMER, TEXT("Stopping timer %s/%p due to liftime limit"),
  201. (NULL == _ptszName) ? TEXT("") : _ptszName,
  202. this ));
  203. Cancel();
  204. return;
  205. }
  206. else if( _cftDue > cftMax )
  207. {
  208. TrkLog(( TRKDBG_TIMER, TEXT("Shortening timer %s/%p due to lifetime limit"),
  209. (NULL == _ptszName) ? TEXT("") : _ptszName,
  210. this ));
  211. _cftDue = cftMax;
  212. }
  213. }
  214. SaveToRegistry();
  215. // Set the timer, but not if it's currently firing. When the timer
  216. // fires, the DoWork method is called, but that method doesn't hold
  217. // the lock while it calls the Timer callback. Thus, if
  218. // _fTimerSignalInProgress is true, some other thread is currently
  219. // in the callback. When it complets, it will set this timer.
  220. if( !_fTimerSignalInProgress )
  221. {
  222. Status = NtSetTimer ( _hTimer, //IN HANDLE TimerHandle,
  223. (LARGE_INTEGER*) &_cftDue, //IN PLARGE_INTEGER DueTime,
  224. NULL, //IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
  225. NULL, //IN PVOID TimerContext OPTIONAL,
  226. FALSE, //IN BOOLEAN ResumeTimer,
  227. 0, //IN LONG Period OPTIONAL,
  228. NULL ); //OUT PBOOLEAN PreviousState OPTIONAL
  229. TrkAssert(NT_SUCCESS(Status));
  230. }
  231. _fRunning = TRUE;
  232. } // CNewTimer::SetTimer
  233. //+----------------------------------------------------------------------------
  234. //
  235. // Method: CNewTimer::Cancel
  236. //
  237. // Synopsis: Cancel the timer and remove its persistent state from the
  238. // registry (if it's a persistent timer).
  239. //
  240. //+----------------------------------------------------------------------------
  241. void
  242. CNewTimer::Cancel()
  243. {
  244. NTSTATUS Status;
  245. TrkAssert( _fInitializeCalled );
  246. Lock();
  247. __try
  248. {
  249. Status = NtCancelTimer(_hTimer, NULL);
  250. TrkAssert(NT_SUCCESS(Status));
  251. _fRunning = FALSE;
  252. _ulCurrentRetryTime = 0;
  253. _cftDue = _cftSet = 0;
  254. RemoveFromRegistry();
  255. }
  256. __finally
  257. {
  258. Unlock();
  259. }
  260. }
  261. //+----------------------------------------------------------------------------
  262. //
  263. // Method: CNewTimer::DebugStringize
  264. //
  265. // Synopsis: Stringize the current state of the timer.
  266. //
  267. //+----------------------------------------------------------------------------
  268. #if DBG
  269. void
  270. CNewTimer::DebugStringize( ULONG cch, TCHAR *ptsz ) const
  271. {
  272. ULONG cchUsed = 0;
  273. TCHAR *ptszTimerState;
  274. TrkAssert( _fInitializeCalled );
  275. Lock();
  276. __try
  277. {
  278. if( _fRunning )
  279. {
  280. if( 0 != _ulCurrentRetryTime )
  281. ptszTimerState = TEXT("retrying");
  282. else
  283. ptszTimerState = TEXT("running");
  284. }
  285. else
  286. ptszTimerState = TEXT("stopped");
  287. cchUsed = _stprintf( ptsz, TEXT("Timer %s/%p is %s "),
  288. NULL == _ptszName ? TEXT("") : _ptszName,
  289. this, ptszTimerState );
  290. if( _fRunning )
  291. {
  292. LONGLONG llDelta;
  293. llDelta = static_cast<LONGLONG>(_cftDue - CFILETIME()) / (10*1000*1000);
  294. if( 0 <= llDelta && 120 >= llDelta )
  295. cchUsed += _stprintf( &ptsz[cchUsed], TEXT("(expires in %I64i seconds)"), llDelta );
  296. else
  297. {
  298. cchUsed += _stprintf( &ptsz[cchUsed], TEXT("(expires on ") );
  299. _cftDue.Stringize( cch-cchUsed, &ptsz[cchUsed] );
  300. cchUsed += _tcslen( &ptsz[cchUsed] );
  301. cchUsed += _stprintf( &ptsz[cchUsed], TEXT(" UTC)") );
  302. }
  303. }
  304. TrkAssert( cch >= cchUsed );
  305. }
  306. __finally
  307. {
  308. Unlock();
  309. }
  310. }
  311. #endif
  312. //+----------------------------------------------------------------------------
  313. //
  314. // Method: CNewTimer::DoWork
  315. //
  316. // Synopsis: This method is called by the work manager when the NT timer
  317. // is signaled. We call pTimerCallback->Timer so that the timer
  318. // event can be handled. That Timer method returns a status
  319. // that tells us what we should do next. The returned status
  320. // is a TimerContinuation, that can be Continue (causes
  321. // a recurring timer to be set again), Break (causes a recurring
  322. // timer to be stopped), or Retry.
  323. //
  324. //+----------------------------------------------------------------------------
  325. void
  326. CNewTimer::DoWork()
  327. {
  328. TrkAssert( _fInitializeCalled );
  329. PTimerCallback::TimerContinuation continuation;
  330. Lock();
  331. {
  332. // We were obviously running recently, or we wouldn't have been called.
  333. // But if we're not running now, we must have been canceled after the
  334. // timer object was signaled (waking the WaitForMultiple) but before
  335. // entry into this routine.
  336. if( !_fRunning )
  337. {
  338. TrkLog(( TRKDBG_ERROR, TEXT("Timer %s/%p stopped while firing"),
  339. NULL == _ptszName ? TEXT("") : _ptszName,
  340. this ));
  341. Unlock();
  342. return;
  343. }
  344. // For now, we're no longer running. If someone calls Set* while we're in
  345. // the Timer callback below, this will be set true.
  346. _fRunning = FALSE;
  347. _cftDue = 0;
  348. // Show that the timer has fired and is being processed. This is used
  349. // by SetTimer so that it knows that we're in a call to the Timer
  350. // callback.
  351. _fTimerSignalInProgress = TRUE;
  352. }
  353. TrkAssert( 1 == GetLockCount() );
  354. Unlock();
  355. // Call the timer handler. On return, it tells us how we should
  356. // proceed.
  357. continuation = _pTimerCallback->Timer( _ulTimerContext );
  358. // continuation : {Break, Continue, Retry}
  359. Lock();
  360. __try // __except
  361. {
  362. // We're no longer in the timer callback.
  363. _fTimerSignalInProgress = FALSE;
  364. // If, while we were in the Timer callback, another thread came along
  365. // and set the timer, then that takes priority over the
  366. // continuation that was just returned. In such an case, _fRunning
  367. // will have been set to TRUE.
  368. if( _fRunning )
  369. {
  370. TrkAssert( 0 != _cftDue );
  371. TrkAssert( NULL != _hTimer );
  372. // Show that we're not in retry mode
  373. _ulCurrentRetryTime = 0;
  374. NTSTATUS Status;
  375. // _cftDue was set in the SetTimer call already
  376. Status = NtSetTimer ( _hTimer, //IN HANDLE TimerHandle,
  377. (LARGE_INTEGER*) &_cftDue, //IN PLARGE_INTEGER DueTime,
  378. NULL, //IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
  379. NULL, //IN PVOID TimerContext OPTIONAL,
  380. FALSE, //IN BOOLEAN ResumeTimer,
  381. 0, //IN LONG Period OPTIONAL,
  382. NULL ); //OUT PBOOLEAN PreviousState OPTIONAL
  383. TrkAssert(NT_SUCCESS(Status));
  384. }
  385. else if( PTimerCallback::BREAK_TIMER == continuation )
  386. {
  387. // Break out of this timer; stop it even if it's a recurring timer.
  388. Cancel();
  389. }
  390. else if( PTimerCallback::CONTINUE_TIMER == continuation )
  391. {
  392. // Continue with this timer; stop it if it's a single shot, set it again
  393. // if it's recurring.
  394. _ulCurrentRetryTime = 0; // If we were retrying, we aren't any longer
  395. if( _fRecurring )
  396. SetTimer();
  397. else
  398. Cancel();
  399. }
  400. else // RETRY_TIMER
  401. {
  402. TrkAssert( PTimerCallback::RETRY_TIMER == continuation );
  403. TrkAssert( _ulLowerRetryTime <= _ulUpperRetryTime );
  404. if( 0 == _ulUpperRetryTime || NO_RETRY == _RetryType )
  405. {
  406. TrkAssert( !TEXT("Attempted to retry a timer with no retry times set") );
  407. Cancel();
  408. }
  409. if( RETRY_WITH_BACKOFF == _RetryType )
  410. {
  411. if( 0 == _ulCurrentRetryTime )
  412. _ulCurrentRetryTime = _ulLowerRetryTime;
  413. else if( (MAXULONG/2) < _ulCurrentRetryTime )
  414. {
  415. TrkLog(( TRKDBG_ERROR, TEXT("Questionable retry time") ));
  416. TrkAssert( FALSE );
  417. _ulCurrentRetryTime = MAXULONG;
  418. }
  419. else
  420. _ulCurrentRetryTime *= 2;
  421. if( _ulCurrentRetryTime > _ulUpperRetryTime )
  422. _ulCurrentRetryTime = _ulUpperRetryTime;
  423. }
  424. else // PTimerCallback::RETRY_RANDOMLY == _RetryType
  425. {
  426. CFILETIME cftNow;
  427. _ulCurrentRetryTime = _ulLowerRetryTime
  428. +
  429. ( QuasiRandomDword() % (_ulUpperRetryTime - _ulLowerRetryTime) );
  430. }
  431. TrkLog(( TRKDBG_TIMER, TEXT("Retrying timer %s/%p for %d seconds"),
  432. (NULL == _ptszName) ? TEXT("") : _ptszName,
  433. this,
  434. _ulCurrentRetryTime ));
  435. // Set the timer with the just-calculated retry period
  436. SetTimer();
  437. } // else if( CONTINUE_TIMER == continuation ) ... else
  438. }
  439. __except( BreakOnDebuggableException() )
  440. {
  441. // The exception may have been in the timer, but more likely
  442. // was in the PTimerCallback::Timer routine. As a cure-all,
  443. // just reset the timer to an arbitrary value (we don't want
  444. // to re-use _ulPeriodInSeconds, because it may be zero).
  445. TrkLog(( TRKDBG_ERROR, TEXT("Unexpected timer exception") ));
  446. TrkAssert( FALSE );
  447. __try
  448. {
  449. _ulPeriodInSeconds = TRKDAY;
  450. Cancel();
  451. SetTimer();
  452. }
  453. __except( BreakOnDebuggableException() )
  454. {
  455. }
  456. }
  457. Unlock();
  458. } // CNewTimer::DoWork
  459. //+----------------------------------------------------------------------------
  460. //
  461. // Method: CNewTimer::SaveToRegistry
  462. //
  463. // Synopsis: Save the timer's state to the registry, using _ptszName
  464. // as a value name.
  465. //
  466. //+----------------------------------------------------------------------------
  467. void
  468. CNewTimer::SaveToRegistry()
  469. {
  470. LONG lErr = ERROR_SUCCESS;
  471. HKEY hk = NULL;
  472. // If this isn't a persistent timer, then there's nothing to do.
  473. if( NULL == _ptszName )
  474. return;
  475. Lock();
  476. __try
  477. {
  478. lErr = RegOpenKey( HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, &hk );
  479. PersistentState persist;
  480. persist.cftSet = _cftSet;
  481. persist.cftDue = _cftDue;
  482. persist.ulCurrentRetryTime = _ulCurrentRetryTime;
  483. if ( lErr == ERROR_SUCCESS )
  484. {
  485. lErr = RegSetValueEx( hk,
  486. _ptszName,
  487. 0,
  488. REG_BINARY,
  489. (CONST BYTE *)&persist,
  490. sizeof(persist) );
  491. RegCloseKey(hk);
  492. }
  493. }
  494. __finally
  495. {
  496. Unlock();
  497. }
  498. TrkAssert( lErr == ERROR_SUCCESS
  499. ||
  500. lErr == ERROR_NOT_ENOUGH_MEMORY
  501. ||
  502. lErr == ERROR_NO_SYSTEM_RESOURCES );
  503. }
  504. //+----------------------------------------------------------------------------
  505. //
  506. // Method: CNewTimer::LoadFromRegistry
  507. //
  508. // Synopsis: Load this timer's previously persisted state from the
  509. // registry.
  510. //
  511. //+----------------------------------------------------------------------------
  512. void
  513. CNewTimer::LoadFromRegistry()
  514. {
  515. LONG l;
  516. HKEY hk = NULL;
  517. // If this isn't a persistent timer, then there's nothing to do.
  518. if( NULL == _ptszName )
  519. return;
  520. Lock();
  521. __try
  522. {
  523. // Open the main link-tracking key.
  524. l = RegCreateKey(HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, &hk);
  525. if (l != ERROR_SUCCESS)
  526. {
  527. hk = NULL;
  528. }
  529. else
  530. {
  531. // The main link-tracking key exists. See if we can open this
  532. // timer's value.
  533. PersistentState persist;
  534. DWORD cbData = sizeof(persist);
  535. DWORD dwType = 0;
  536. l = RegQueryValueEx( hk,
  537. _ptszName,
  538. NULL,
  539. &dwType,
  540. (BYTE *)&persist,
  541. &cbData );
  542. if (l == ERROR_SUCCESS)
  543. {
  544. if (dwType == REG_BINARY
  545. &&
  546. cbData == sizeof(persist))
  547. {
  548. // This timer has a persistent value in the registry. Override
  549. // the caller-provided timeout.
  550. _cftDue = persist.cftDue;
  551. _cftSet = persist.cftSet;
  552. _ulCurrentRetryTime = persist.ulCurrentRetryTime;
  553. }
  554. else
  555. {
  556. RegDeleteValue( hk, _ptszName );
  557. l = ERROR_FILE_NOT_FOUND;
  558. }
  559. } // if (l == ERROR_SUCCESS)
  560. } // if (l != ERROR_SUCCESS) ... else
  561. }
  562. __finally
  563. {
  564. if( NULL != hk )
  565. RegCloseKey(hk);
  566. Unlock();
  567. }
  568. if (l != ERROR_SUCCESS && l != ERROR_FILE_NOT_FOUND)
  569. {
  570. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring error %08x in timer %s LoadFromRegistry"),
  571. l, _ptszName ));
  572. }
  573. return;
  574. }
  575. //+----------------------------------------------------------------------------
  576. //
  577. // Method: CNewTimer::RemoveFromRegistry
  578. //
  579. // Synopsis: Remove this timer's persistent state from the registry.
  580. //
  581. //+----------------------------------------------------------------------------
  582. void
  583. CNewTimer::RemoveFromRegistry()
  584. {
  585. LONG lErr = ERROR_SUCCESS;
  586. HKEY hk = NULL;
  587. if( NULL == _ptszName )
  588. return;
  589. Lock();
  590. __try
  591. {
  592. lErr = RegOpenKey( HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, &hk );
  593. if ( lErr == ERROR_SUCCESS )
  594. {
  595. lErr = RegDeleteValue( hk, _ptszName );
  596. RegCloseKey(hk);
  597. if( ERROR_SUCCESS != lErr
  598. &&
  599. ERROR_PATH_NOT_FOUND != lErr
  600. &&
  601. ERROR_FILE_NOT_FOUND != lErr )
  602. {
  603. TrkLog((TRKDBG_ERROR, TEXT("Couldn't delete timer's static reg name (\"%s\", %08x)"),
  604. _ptszName, lErr ));
  605. }
  606. }
  607. }
  608. __finally
  609. {
  610. Unlock();
  611. }
  612. }
  613. //+----------------------------------------------------------------------------
  614. //
  615. // CNewTimer::UnInitialize
  616. //
  617. // Unregister the timer handle from the thread pool, cancel the timer,
  618. // and release it.
  619. //
  620. //+----------------------------------------------------------------------------
  621. void
  622. CNewTimer::UnInitialize()
  623. {
  624. if( _fInitializeCalled )
  625. {
  626. TrkLog(( TRKDBG_TIMER, TEXT("Uninitializing timer %s/%p"), GetTimerName(), this ));
  627. // Take the timer out of the thread pool, which must be
  628. // done before closing the timer.
  629. if( NULL != _hRegisterWaitForSingleObjectEx )
  630. {
  631. if( !TrkUnregisterWait( _hRegisterWaitForSingleObjectEx ))
  632. {
  633. TrkLog(( TRKDBG_ERROR, TEXT("Failed UnregisterWait for CNewTimer (%s/%p, %lu)"),
  634. NULL == _ptszName ? TEXT("") : _ptszName, this,
  635. GetLastError() ));
  636. }
  637. else
  638. {
  639. TrkLog(( TRKDBG_TIMER, TEXT("Unregistered wait for timer (%s/%p)"),
  640. NULL == _ptszName ? TEXT("") : _ptszName, this ));
  641. }
  642. _hRegisterWaitForSingleObjectEx = NULL;
  643. }
  644. // Close the timer handle
  645. TrkVerify( NT_SUCCESS( NtCancelTimer(_hTimer, NULL) ));
  646. NtClose(_hTimer);
  647. // Delete the CNewTimer critical section.
  648. _cs.UnInitialize();
  649. _fInitializeCalled = FALSE;
  650. }
  651. }