//================================================================= // // TimerQueue.cpp // // Copyright (c) 1999-2001 Microsoft Corporation, All Rights Reserved // //================================================================= #include "precomp.h" #include #include "TimerQueue.h" #include CTimerEvent :: CTimerEvent ( DWORD dwTimeOut, BOOL fRepeat ) : m_dwMilliseconds ( dwTimeOut ) , m_bRepeating ( fRepeat ) { m_bEnabled = FALSE ; /* if ( a_Enable ) { Enable (); } */ } CTimerEvent :: CTimerEvent ( const CTimerEvent &rTimerEvent ) : //m_bEnabled ( rTimerEvent.m_bEnabled ) , m_dwMilliseconds ( rTimerEvent.m_dwMilliseconds ) , m_bRepeating ( rTimerEvent.m_bRepeating ) { // if ( m_bEnabled ) // { // Enable () ; // } // if ( m_Rule ) // m_Rule->AddRef () ; } void CTimerEvent :: Enable () { // we might not be able to enable timer m_bEnabled = CTimerQueue :: s_TimerQueue.QueueTimer ( this ) ; } void CTimerEvent :: Disable () { // enabled is oposite of returned m_bEnabled = !( CTimerQueue :: s_TimerQueue.DeQueueTimer ( this ) ); } BOOL CTimerEvent :: Enabled () { return m_bEnabled ; } BOOL CTimerEvent :: Repeating () { return m_bRepeating ; } DWORD CTimerEvent :: GetMilliSeconds () { return m_dwMilliseconds ; } /* CRuleTimerEvent :: CRuleTimerEvent ( CRule *a_Rule , BOOL a_Enable , DWORD dwTimeOut, BOOL fRepeat , BOOL bMarkedForDequeue ) : CTimerEvent ( a_Enable , dwTimeOut , fRepeat, bMarkedForDequeue ) , m_Rule ( a_Rule ) { if ( m_Rule ) { m_Rule->AddRef () ; } } CRuleTimerEvent :: CRuleTimerEvent ( const CRuleTimerEvent &rTimerEvent ) : CTimerEvent ( rTimerEvent ) , m_Rule ( rTimerEvent.m_Rule ) { if ( m_Rule ) { m_Rule->AddRef () ; } } CRuleTimerEvent :: ~CRuleTimerEvent () { if ( m_Rule ) { m_Rule->Release () ; } } */ // CTimerQueue construction creates the worker thread and a event handle CTimerQueue::CTimerQueue() : m_hInitEvent(NULL) { m_fShutDown = FALSE; m_bInit = FALSE; m_hScheduleEvent = NULL; // Scheduler thread m_hSchedulerHandle = NULL; // when this event has not created there is very very small possibility of // having crash when shutdown is in progress and we step into init function m_hInitEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); } void CTimerQueue::Init() { // every thread may try to get it initialized couple times DWORD dwTry = 0L; EnterCriticalSection ( &m_oCS ); while ( !m_bInit && dwTry < 3 ) { if (m_fShutDown) { LeaveCriticalSection ( &m_oCS ); if ( m_hInitEvent ) { WaitForSingleObjectEx ( m_hInitEvent, INFINITE, FALSE ); } EnterCriticalSection ( &m_oCS ); } else { try { if ( ! m_hScheduleEvent ) { m_hScheduleEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); } if ( m_hScheduleEvent ) { if ( ! m_hSchedulerHandle ) { // Scheduler thread LogMessage ( L"CreateThread for Scheduler called" ); m_hSchedulerHandle = CreateThread( NULL, // pointer to security attributes 0L, // initial thread stack size dwThreadProc, // pointer to thread function this, // argument for new thread 0L, // creation flags &m_dwThreadID); } if ( m_hSchedulerHandle ) { m_bInit = TRUE; } } dwTry++; } catch(...) { // not much we can do in here if( m_hSchedulerHandle ) { CloseHandle( m_hSchedulerHandle ); m_hSchedulerHandle = NULL; } if( m_hScheduleEvent ) { CloseHandle( m_hScheduleEvent ); m_hScheduleEvent = NULL; } LeaveCriticalSection ( &m_oCS ); throw; } } } LeaveCriticalSection ( &m_oCS ); } // CTimerQueue::~CTimerQueue() { LogMessage ( L"Entering ~CTimerQueue" ) ; if (m_hInitEvent) { CloseHandle(m_hInitEvent); m_hInitEvent = NULL; } LogMessage ( L"Leaving ~CTimerQueue" ) ; } // worker thread pump DWORD WINAPI CTimerQueue::dwThreadProc( LPVOID lpParameter ) { CTimerQueue* pThis = (CTimerQueue*)lpParameter; BOOL bTerminateShutdown = FALSE; try { while( !bTerminateShutdown ) { DWORD dwWaitResult = WAIT_OBJECT_0; dwWaitResult = WaitForSingleObjectEx( pThis->m_hScheduleEvent, pThis->dwProcessSchedule(), 0L ); switch ( dwWaitResult ) { case WAIT_OBJECT_0: { if( pThis->ShutDown() ) { bTerminateShutdown = TRUE; LogMessage ( L"Scheduler thread exiting" ) ; } } break; case WAIT_ABANDONED: { // we are probably not initialized properly bTerminateShutdown = TRUE; } break; } } } catch(...) { } return bTerminateShutdown; } // signals for a pump cycle, checking the updated queue void CTimerQueue::vUpdateScheduler() { SetEvent ( m_hScheduleEvent ); } // Public function: Queues a timer entry for a scheduled callback BOOL CTimerQueue::QueueTimer( CTimerEvent* pTimerEvent ) { BOOL fRc = FALSE; /* * Init the scheduler thread if it's not there . The thread should not be created if we're * in the middle of shutdown as this may cause a deadlock if one resource caches another resource pointer. */ CAutoLock cal(m_oCS); if ( !m_fShutDown ) { if( !m_bInit ) { Init() ; } if( m_bInit ) { fRc = fScheduleEvent( pTimerEvent ); } } return fRc; } // Public function: Dequeues a timer event BOOL CTimerQueue::DeQueueTimer( CTimerEvent* pTimerEvent ) { BOOL fRemoved = FALSE; CTimerEvent* pTE = pTimerEvent; //scope of critsec locked { CAutoLock cal(m_oCS); Timer_Ptr_Queue::iterator pQueueElement; for( pQueueElement = m_oTimerQueue.begin(); pQueueElement != m_oTimerQueue.end(); pQueueElement++) { if(pTE == *pQueueElement) { m_oTimerQueue.erase( pQueueElement ); pTE->Release (); fRemoved = TRUE; break ; } } } if( fRemoved ) { vUpdateScheduler(); } return fRemoved; } // BOOL CTimerQueue::fScheduleEvent( CTimerEvent* pNewTE ) { // system clock offset pNewTE->int64Time = int64Clock() + pNewTE->GetMilliSeconds () ; // slot the event into the ordered list, scope for CS { CAutoLock cal(m_oCS); BOOL fInserted = FALSE; Timer_Ptr_Queue::iterator pQueueElement; for( pQueueElement = m_oTimerQueue.begin(); pQueueElement != m_oTimerQueue.end(); pQueueElement++) { if( pNewTE->int64Time < (*pQueueElement)->int64Time ) { m_oTimerQueue.insert( pQueueElement, pNewTE ); fInserted = TRUE; pNewTE->AddRef () ; break; } } if( !fInserted ) { m_oTimerQueue.push_back( pNewTE ); pNewTE->AddRef () ; } } vUpdateScheduler(); return TRUE; } // This work is done on the Scheduler thread DWORD CTimerQueue::dwProcessSchedule() { CTimerEvent* pTE; LogMessage ( L"Entering CTimerQueue::dwProcessSchedule" ) ; while( pTE = pGetNextTimerEvent() ) { // process the request LogMessage ( L"CTimerEvent::OnTimer called" ) ; pTE->OnTimer () ; LogMessage ( L"CTimerEvent::OnTimer returned" ) ; // reschedule a repeatable event if( pTE->Repeating() && pTE->Enabled() && fScheduleEvent( pTE ) ) { } pTE->Release () ; } return dwNextTimerEvent(); } // returns the time for the next scheduled event in milliseconds DWORD CTimerQueue::dwNextTimerEvent() { DWORD dwNextEvent = INFINITE; //scope of CS { CAutoLock cal(m_oCS); if( m_fShutDown ) { return 0; } if( !m_oTimerQueue.empty() ) { CTimerEvent* pTE = m_oTimerQueue.front(); dwNextEvent = max((DWORD)(pTE->int64Time - int64Clock()), 0); } } LogMessage ( L"Leaving CTimerQueue::dwNextTimerEvent" ) ; return dwNextEvent; } // Returns the next scheduled and ready timer event (from an ordered list) or NULL CTimerEvent* CTimerQueue::pGetNextTimerEvent() { CAutoLock cal(m_oCS); if( m_fShutDown ) { return NULL; } CTimerEvent* pTE = NULL; if( !m_oTimerQueue.empty() ) { pTE = m_oTimerQueue.front(); if( int64Clock() >= pTE->int64Time ) m_oTimerQueue.pop_front(); else pTE = NULL; } return pTE; } BOOL CTimerQueue::ShutDown() { CAutoLock cal(m_oCS); BOOL retVal = m_fShutDown; return retVal; } // void CTimerQueue::OnShutDown() { LogMessage ( L"Entering CTimerQueue::OnShutDown" ) ; EnterCriticalSection(&m_oCS); if( m_bInit ) { if ( m_hInitEvent ) { ResetEvent ( m_hInitEvent ); } m_fShutDown = TRUE; m_bInit = FALSE; // unguarded section --- // No TimerQueue global is modified in this frame block. // // To avoid a deadlock we unnest this CS from the // embedded CResourceList mutex accessed through // vEmptyList(). This avoids the situation where a // a normal resource request locks the list then locking // the TimerQueue to schedule a timed resource release. // LeaveCriticalSection(&m_oCS); { if ( m_hSchedulerHandle ) { DWORD t_dwExitCode = 0 ; BOOL t_bRet = GetExitCodeThread ( m_hSchedulerHandle, // handle to the thread &t_dwExitCode // address to receive termination status ); /* * If the worker thread has not exited , we've to wait till it exits */ if ( t_bRet && t_dwExitCode == STILL_ACTIVE ) { /* //error logging starts here...delete this after finding the cause of shutdown crash CHString chsMsg ; chsMsg.Format ( L"Threadid=%x ThreadHandle = %x", GetCurrentThreadId (), GetCurrentThread () ) ; LogMessage ( CHString ( "TimerQueue Current Thread: " ) +chsMsg ) ; chsMsg.Format ( L"Threadid=%x ThreadHandle = %x", m_dwThreadID, m_hSchedulerHandle ) ; LogMessage ( CHString ( "TimerQueue Waiting on Thread: " ) +chsMsg ) ; //error logging stops here */ vUpdateScheduler(); // wait for the Scheduler thread handle itself WaitForSingleObjectEx( m_hSchedulerHandle, INFINITE, 0L ); } vEmptyList() ; } } EnterCriticalSection(&m_oCS); if( m_hSchedulerHandle ) { CloseHandle( m_hSchedulerHandle ); m_hSchedulerHandle = NULL; } if( m_hScheduleEvent ) { CloseHandle( m_hScheduleEvent ); m_hScheduleEvent = NULL; } m_fShutDown = FALSE; if ( m_hInitEvent ) { SetEvent(m_hInitEvent); } } LeaveCriticalSection(&m_oCS); LogMessage ( L"Leaving CTimerQueue::OnShutDown" ) ; } // void CTimerQueue::vEmptyList() { EnterCriticalSection(&m_oCS); BOOL t_fCS = TRUE ; { try { while(!m_oTimerQueue.empty()) { CTimerEvent* pTE = m_oTimerQueue.front() ; m_oTimerQueue.pop_front(); LeaveCriticalSection(&m_oCS); t_fCS = FALSE ; LogMessage ( L"CTimerQueue::vEmptyList--->CTimerEvent::OnTimer called" ) ; pTE->OnTimer () ; LogMessage ( L"CTimerQueue::vEmptyList--->CTimerEvent::OnTimer returned" ) ; pTE->Release(); EnterCriticalSection(&m_oCS); t_fCS = TRUE ; } } catch( ... ) { if( t_fCS ) { LeaveCriticalSection(&m_oCS); } throw ; } } LeaveCriticalSection(&m_oCS); } __int64 CTimerQueue::int64Clock() { FILETIME t_FileTime ; __int64 t_i64Tmp ; GetSystemTimeAsFileTime ( &t_FileTime ) ; t_i64Tmp = t_FileTime.dwHighDateTime ; t_i64Tmp = ( t_i64Tmp << 32 ) | t_FileTime.dwLowDateTime ; /* * Convert the FILETIME ( in units of 100 ns ) into milliseconds */ return t_i64Tmp / 10000 ; }