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
578 lines
12 KiB
//=================================================================
|
|
|
|
//
|
|
|
|
// TimerQueue.cpp
|
|
|
|
//
|
|
|
|
// Copyright (c) 1999-2001 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
//=================================================================
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <time.h>
|
|
#include "TimerQueue.h"
|
|
#include <cautolock.h>
|
|
|
|
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 ;
|
|
}
|