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.

578 lines
12 KiB

  1. //=================================================================
  2. //
  3. // TimerQueue.cpp
  4. //
  5. // Copyright (c) 1999-2001 Microsoft Corporation, All Rights Reserved
  6. //
  7. //=================================================================
  8. #include "precomp.h"
  9. #include <time.h>
  10. #include "TimerQueue.h"
  11. #include <cautolock.h>
  12. CTimerEvent :: CTimerEvent (
  13. DWORD dwTimeOut,
  14. BOOL fRepeat
  15. ) : m_dwMilliseconds ( dwTimeOut ) ,
  16. m_bRepeating ( fRepeat )
  17. {
  18. m_bEnabled = FALSE ;
  19. /* if ( a_Enable )
  20. {
  21. Enable ();
  22. }
  23. */
  24. }
  25. CTimerEvent :: CTimerEvent (
  26. const CTimerEvent &rTimerEvent
  27. ) : //m_bEnabled ( rTimerEvent.m_bEnabled ) ,
  28. m_dwMilliseconds ( rTimerEvent.m_dwMilliseconds ) ,
  29. m_bRepeating ( rTimerEvent.m_bRepeating )
  30. {
  31. // if ( m_bEnabled )
  32. // {
  33. // Enable () ;
  34. // }
  35. // if ( m_Rule )
  36. // m_Rule->AddRef () ;
  37. }
  38. void CTimerEvent :: Enable ()
  39. {
  40. // we might not be able to enable timer
  41. m_bEnabled = CTimerQueue :: s_TimerQueue.QueueTimer ( this ) ;
  42. }
  43. void CTimerEvent :: Disable ()
  44. {
  45. // enabled is oposite of returned
  46. m_bEnabled = !( CTimerQueue :: s_TimerQueue.DeQueueTimer ( this ) );
  47. }
  48. BOOL CTimerEvent :: Enabled ()
  49. {
  50. return m_bEnabled ;
  51. }
  52. BOOL CTimerEvent :: Repeating ()
  53. {
  54. return m_bRepeating ;
  55. }
  56. DWORD CTimerEvent :: GetMilliSeconds ()
  57. {
  58. return m_dwMilliseconds ;
  59. }
  60. /*
  61. CRuleTimerEvent :: CRuleTimerEvent (
  62. CRule *a_Rule ,
  63. BOOL a_Enable ,
  64. DWORD dwTimeOut,
  65. BOOL fRepeat ,
  66. BOOL bMarkedForDequeue
  67. ) : CTimerEvent ( a_Enable , dwTimeOut , fRepeat, bMarkedForDequeue ) ,
  68. m_Rule ( a_Rule )
  69. {
  70. if ( m_Rule )
  71. {
  72. m_Rule->AddRef () ;
  73. }
  74. }
  75. CRuleTimerEvent :: CRuleTimerEvent (
  76. const CRuleTimerEvent &rTimerEvent
  77. ) : CTimerEvent ( rTimerEvent ) ,
  78. m_Rule ( rTimerEvent.m_Rule )
  79. {
  80. if ( m_Rule )
  81. {
  82. m_Rule->AddRef () ;
  83. }
  84. }
  85. CRuleTimerEvent :: ~CRuleTimerEvent ()
  86. {
  87. if ( m_Rule )
  88. {
  89. m_Rule->Release () ;
  90. }
  91. }
  92. */
  93. // CTimerQueue construction creates the worker thread and a event handle
  94. CTimerQueue::CTimerQueue() : m_hInitEvent(NULL)
  95. {
  96. m_fShutDown = FALSE;
  97. m_bInit = FALSE;
  98. m_hScheduleEvent = NULL;
  99. // Scheduler thread
  100. m_hSchedulerHandle = NULL;
  101. // when this event has not created there is very very small possibility of
  102. // having crash when shutdown is in progress and we step into init function
  103. m_hInitEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
  104. }
  105. void CTimerQueue::Init()
  106. {
  107. // every thread may try to get it initialized couple times
  108. DWORD dwTry = 0L;
  109. EnterCriticalSection ( &m_oCS );
  110. while ( !m_bInit && dwTry < 3 )
  111. {
  112. if (m_fShutDown)
  113. {
  114. LeaveCriticalSection ( &m_oCS );
  115. if ( m_hInitEvent )
  116. {
  117. WaitForSingleObjectEx ( m_hInitEvent, INFINITE, FALSE );
  118. }
  119. EnterCriticalSection ( &m_oCS );
  120. }
  121. else
  122. {
  123. try
  124. {
  125. if ( ! m_hScheduleEvent )
  126. {
  127. m_hScheduleEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  128. }
  129. if ( m_hScheduleEvent )
  130. {
  131. if ( ! m_hSchedulerHandle )
  132. {
  133. // Scheduler thread
  134. LogMessage ( L"CreateThread for Scheduler called" );
  135. m_hSchedulerHandle = CreateThread(
  136. NULL, // pointer to security attributes
  137. 0L, // initial thread stack size
  138. dwThreadProc, // pointer to thread function
  139. this, // argument for new thread
  140. 0L, // creation flags
  141. &m_dwThreadID);
  142. }
  143. if ( m_hSchedulerHandle )
  144. {
  145. m_bInit = TRUE;
  146. }
  147. }
  148. dwTry++;
  149. }
  150. catch(...)
  151. {
  152. // not much we can do in here
  153. if( m_hSchedulerHandle )
  154. {
  155. CloseHandle( m_hSchedulerHandle );
  156. m_hSchedulerHandle = NULL;
  157. }
  158. if( m_hScheduleEvent )
  159. {
  160. CloseHandle( m_hScheduleEvent );
  161. m_hScheduleEvent = NULL;
  162. }
  163. LeaveCriticalSection ( &m_oCS );
  164. throw;
  165. }
  166. }
  167. }
  168. LeaveCriticalSection ( &m_oCS );
  169. }
  170. //
  171. CTimerQueue::~CTimerQueue()
  172. {
  173. LogMessage ( L"Entering ~CTimerQueue" ) ;
  174. if (m_hInitEvent)
  175. {
  176. CloseHandle(m_hInitEvent);
  177. m_hInitEvent = NULL;
  178. }
  179. LogMessage ( L"Leaving ~CTimerQueue" ) ;
  180. }
  181. // worker thread pump
  182. DWORD WINAPI CTimerQueue::dwThreadProc( LPVOID lpParameter )
  183. {
  184. CTimerQueue* pThis = (CTimerQueue*)lpParameter;
  185. BOOL bTerminateShutdown = FALSE;
  186. try
  187. {
  188. while( !bTerminateShutdown )
  189. {
  190. DWORD dwWaitResult = WAIT_OBJECT_0;
  191. dwWaitResult = WaitForSingleObjectEx( pThis->m_hScheduleEvent, pThis->dwProcessSchedule(), 0L );
  192. switch ( dwWaitResult )
  193. {
  194. case WAIT_OBJECT_0:
  195. {
  196. if( pThis->ShutDown() )
  197. {
  198. bTerminateShutdown = TRUE;
  199. LogMessage ( L"Scheduler thread exiting" ) ;
  200. }
  201. }
  202. break;
  203. case WAIT_ABANDONED:
  204. {
  205. // we are probably not initialized properly
  206. bTerminateShutdown = TRUE;
  207. }
  208. break;
  209. }
  210. }
  211. }
  212. catch(...)
  213. {
  214. }
  215. return bTerminateShutdown;
  216. }
  217. // signals for a pump cycle, checking the updated queue
  218. void CTimerQueue::vUpdateScheduler()
  219. {
  220. SetEvent ( m_hScheduleEvent );
  221. }
  222. // Public function: Queues a timer entry for a scheduled callback
  223. BOOL CTimerQueue::QueueTimer( CTimerEvent* pTimerEvent )
  224. {
  225. BOOL fRc = FALSE;
  226. /*
  227. * Init the scheduler thread if it's not there . The thread should not be created if we're
  228. * in the middle of shutdown as this may cause a deadlock if one resource caches another resource pointer.
  229. */
  230. CAutoLock cal(m_oCS);
  231. if ( !m_fShutDown )
  232. {
  233. if( !m_bInit )
  234. {
  235. Init() ;
  236. }
  237. if( m_bInit )
  238. {
  239. fRc = fScheduleEvent( pTimerEvent );
  240. }
  241. }
  242. return fRc;
  243. }
  244. // Public function: Dequeues a timer event
  245. BOOL CTimerQueue::DeQueueTimer( CTimerEvent* pTimerEvent )
  246. {
  247. BOOL fRemoved = FALSE;
  248. CTimerEvent* pTE = pTimerEvent;
  249. //scope of critsec locked
  250. {
  251. CAutoLock cal(m_oCS);
  252. Timer_Ptr_Queue::iterator pQueueElement;
  253. for( pQueueElement = m_oTimerQueue.begin();
  254. pQueueElement != m_oTimerQueue.end();
  255. pQueueElement++) {
  256. if(pTE == *pQueueElement)
  257. {
  258. m_oTimerQueue.erase( pQueueElement );
  259. pTE->Release ();
  260. fRemoved = TRUE;
  261. break ;
  262. }
  263. }
  264. }
  265. if( fRemoved )
  266. {
  267. vUpdateScheduler();
  268. }
  269. return fRemoved;
  270. }
  271. //
  272. BOOL CTimerQueue::fScheduleEvent( CTimerEvent* pNewTE )
  273. {
  274. // system clock offset
  275. pNewTE->int64Time = int64Clock() + pNewTE->GetMilliSeconds () ;
  276. // slot the event into the ordered list, scope for CS
  277. {
  278. CAutoLock cal(m_oCS);
  279. BOOL fInserted = FALSE;
  280. Timer_Ptr_Queue::iterator pQueueElement;
  281. for( pQueueElement = m_oTimerQueue.begin();
  282. pQueueElement != m_oTimerQueue.end();
  283. pQueueElement++) {
  284. if( pNewTE->int64Time < (*pQueueElement)->int64Time )
  285. {
  286. m_oTimerQueue.insert( pQueueElement, pNewTE );
  287. fInserted = TRUE;
  288. pNewTE->AddRef () ;
  289. break;
  290. }
  291. }
  292. if( !fInserted )
  293. {
  294. m_oTimerQueue.push_back( pNewTE );
  295. pNewTE->AddRef () ;
  296. }
  297. }
  298. vUpdateScheduler();
  299. return TRUE;
  300. }
  301. // This work is done on the Scheduler thread
  302. DWORD CTimerQueue::dwProcessSchedule()
  303. {
  304. CTimerEvent* pTE;
  305. LogMessage ( L"Entering CTimerQueue::dwProcessSchedule" ) ;
  306. while( pTE = pGetNextTimerEvent() )
  307. {
  308. // process the request
  309. LogMessage ( L"CTimerEvent::OnTimer called" ) ;
  310. pTE->OnTimer () ;
  311. LogMessage ( L"CTimerEvent::OnTimer returned" ) ;
  312. // reschedule a repeatable event
  313. if( pTE->Repeating() && pTE->Enabled() && fScheduleEvent( pTE ) )
  314. {
  315. }
  316. pTE->Release () ;
  317. }
  318. return dwNextTimerEvent();
  319. }
  320. // returns the time for the next scheduled event in milliseconds
  321. DWORD CTimerQueue::dwNextTimerEvent()
  322. {
  323. DWORD dwNextEvent = INFINITE;
  324. //scope of CS
  325. {
  326. CAutoLock cal(m_oCS);
  327. if( m_fShutDown )
  328. {
  329. return 0;
  330. }
  331. if( !m_oTimerQueue.empty() )
  332. {
  333. CTimerEvent* pTE = m_oTimerQueue.front();
  334. dwNextEvent = max((DWORD)(pTE->int64Time - int64Clock()), 0);
  335. }
  336. }
  337. LogMessage ( L"Leaving CTimerQueue::dwNextTimerEvent" ) ;
  338. return dwNextEvent;
  339. }
  340. // Returns the next scheduled and ready timer event (from an ordered list) or NULL
  341. CTimerEvent* CTimerQueue::pGetNextTimerEvent()
  342. {
  343. CAutoLock cal(m_oCS);
  344. if( m_fShutDown )
  345. {
  346. return NULL;
  347. }
  348. CTimerEvent* pTE = NULL;
  349. if( !m_oTimerQueue.empty() )
  350. {
  351. pTE = m_oTimerQueue.front();
  352. if( int64Clock() >= pTE->int64Time )
  353. m_oTimerQueue.pop_front();
  354. else
  355. pTE = NULL;
  356. }
  357. return pTE;
  358. }
  359. BOOL CTimerQueue::ShutDown()
  360. {
  361. CAutoLock cal(m_oCS);
  362. BOOL retVal = m_fShutDown;
  363. return retVal;
  364. }
  365. //
  366. void CTimerQueue::OnShutDown()
  367. {
  368. LogMessage ( L"Entering CTimerQueue::OnShutDown" ) ;
  369. EnterCriticalSection(&m_oCS);
  370. if( m_bInit )
  371. {
  372. if ( m_hInitEvent )
  373. {
  374. ResetEvent ( m_hInitEvent );
  375. }
  376. m_fShutDown = TRUE;
  377. m_bInit = FALSE;
  378. // unguarded section ---
  379. // No TimerQueue global is modified in this frame block.
  380. //
  381. // To avoid a deadlock we unnest this CS from the
  382. // embedded CResourceList mutex accessed through
  383. // vEmptyList(). This avoids the situation where a
  384. // a normal resource request locks the list then locking
  385. // the TimerQueue to schedule a timed resource release.
  386. //
  387. LeaveCriticalSection(&m_oCS);
  388. {
  389. if ( m_hSchedulerHandle )
  390. {
  391. DWORD t_dwExitCode = 0 ;
  392. BOOL t_bRet = GetExitCodeThread ( m_hSchedulerHandle, // handle to the thread
  393. &t_dwExitCode // address to receive termination status
  394. );
  395. /*
  396. * If the worker thread has not exited , we've to wait till it exits
  397. */
  398. if ( t_bRet && t_dwExitCode == STILL_ACTIVE )
  399. {
  400. /*
  401. //error logging starts here...delete this after finding the cause of shutdown crash
  402. CHString chsMsg ;
  403. chsMsg.Format ( L"Threadid=%x ThreadHandle = %x", GetCurrentThreadId (), GetCurrentThread () ) ;
  404. LogMessage ( CHString ( "TimerQueue Current Thread: " ) +chsMsg ) ;
  405. chsMsg.Format ( L"Threadid=%x ThreadHandle = %x", m_dwThreadID, m_hSchedulerHandle ) ;
  406. LogMessage ( CHString ( "TimerQueue Waiting on Thread: " ) +chsMsg ) ;
  407. //error logging stops here
  408. */
  409. vUpdateScheduler();
  410. // wait for the Scheduler thread handle itself
  411. WaitForSingleObjectEx( m_hSchedulerHandle, INFINITE, 0L );
  412. }
  413. vEmptyList() ;
  414. }
  415. }
  416. EnterCriticalSection(&m_oCS);
  417. if( m_hSchedulerHandle )
  418. {
  419. CloseHandle( m_hSchedulerHandle );
  420. m_hSchedulerHandle = NULL;
  421. }
  422. if( m_hScheduleEvent )
  423. {
  424. CloseHandle( m_hScheduleEvent );
  425. m_hScheduleEvent = NULL;
  426. }
  427. m_fShutDown = FALSE;
  428. if ( m_hInitEvent )
  429. {
  430. SetEvent(m_hInitEvent);
  431. }
  432. }
  433. LeaveCriticalSection(&m_oCS);
  434. LogMessage ( L"Leaving CTimerQueue::OnShutDown" ) ;
  435. }
  436. //
  437. void CTimerQueue::vEmptyList()
  438. {
  439. EnterCriticalSection(&m_oCS);
  440. BOOL t_fCS = TRUE ;
  441. {
  442. try
  443. {
  444. while(!m_oTimerQueue.empty())
  445. {
  446. CTimerEvent* pTE = m_oTimerQueue.front() ;
  447. m_oTimerQueue.pop_front();
  448. LeaveCriticalSection(&m_oCS);
  449. t_fCS = FALSE ;
  450. LogMessage ( L"CTimerQueue::vEmptyList--->CTimerEvent::OnTimer called" ) ;
  451. pTE->OnTimer () ;
  452. LogMessage ( L"CTimerQueue::vEmptyList--->CTimerEvent::OnTimer returned" ) ;
  453. pTE->Release();
  454. EnterCriticalSection(&m_oCS);
  455. t_fCS = TRUE ;
  456. }
  457. }
  458. catch( ... )
  459. {
  460. if( t_fCS )
  461. {
  462. LeaveCriticalSection(&m_oCS);
  463. }
  464. throw ;
  465. }
  466. }
  467. LeaveCriticalSection(&m_oCS);
  468. }
  469. __int64 CTimerQueue::int64Clock()
  470. {
  471. FILETIME t_FileTime ;
  472. __int64 t_i64Tmp ;
  473. GetSystemTimeAsFileTime ( &t_FileTime ) ;
  474. t_i64Tmp = t_FileTime.dwHighDateTime ;
  475. t_i64Tmp = ( t_i64Tmp << 32 ) | t_FileTime.dwLowDateTime ;
  476. /*
  477. * Convert the FILETIME ( in units of 100 ns ) into milliseconds
  478. */
  479. return t_i64Tmp / 10000 ;
  480. }