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

//=================================================================
//
// 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 ;
}