//------------------------------------------------------------------------------ | |
// File: Schedule.cpp | |
// | |
// Desc: DirectShow base classes. | |
// | |
// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. | |
//------------------------------------------------------------------------------ | |
#include <streams.h> | |
// DbgLog values (all on LOG_TIMING): | |
// | |
// 2 for schedulting, firing and shunting of events | |
// 3 for wait delays and wake-up times of event thread | |
// 4 for details of whats on the list when the thread awakes | |
/* Construct & destructors */ | |
CAMSchedule::CAMSchedule( HANDLE ev ) | |
: CBaseObject(TEXT("CAMSchedule")) | |
, head(&z, 0), z(0, MAX_TIME) | |
, m_dwNextCookie(0), m_dwAdviseCount(0) | |
, m_pAdviseCache(0), m_dwCacheCount(0) | |
, m_ev( ev ) | |
{ | |
head.m_dwAdviseCookie = z.m_dwAdviseCookie = 0; | |
} | |
CAMSchedule::~CAMSchedule() | |
{ | |
m_Serialize.Lock(); | |
// Delete cache | |
CAdvisePacket * p = m_pAdviseCache; | |
while (p) | |
{ | |
CAdvisePacket *const p_next = p->m_next; | |
delete p; | |
p = p_next; | |
} | |
ASSERT( m_dwAdviseCount == 0 ); | |
// Better to be safe than sorry | |
if ( m_dwAdviseCount > 0 ) | |
{ | |
DumpLinkedList(); | |
while ( !head.m_next->IsZ() ) | |
{ | |
head.DeleteNext(); | |
--m_dwAdviseCount; | |
} | |
} | |
// If, in the debug version, we assert twice, it means, not only | |
// did we have left over advises, but we have also let m_dwAdviseCount | |
// get out of sync. with the number of advises actually on the list. | |
ASSERT( m_dwAdviseCount == 0 ); | |
m_Serialize.Unlock(); | |
} | |
/* Public methods */ | |
DWORD CAMSchedule::GetAdviseCount() | |
{ | |
// No need to lock, m_dwAdviseCount is 32bits & declared volatile | |
return m_dwAdviseCount; | |
} | |
REFERENCE_TIME CAMSchedule::GetNextAdviseTime() | |
{ | |
CAutoLock lck(&m_Serialize); // Need to stop the linked list from changing | |
return head.m_next->m_rtEventTime; | |
} | |
DWORD_PTR CAMSchedule::AddAdvisePacket | |
( const REFERENCE_TIME & time1 | |
, const REFERENCE_TIME & time2 | |
, HANDLE h, BOOL periodic | |
) | |
{ | |
// Since we use MAX_TIME as a sentry, we can't afford to | |
// schedule a notification at MAX_TIME | |
ASSERT( time1 < MAX_TIME ); | |
DWORD_PTR Result; | |
CAdvisePacket * p; | |
m_Serialize.Lock(); | |
if (m_pAdviseCache) | |
{ | |
p = m_pAdviseCache; | |
m_pAdviseCache = p->m_next; | |
--m_dwCacheCount; | |
} | |
else | |
{ | |
p = new CAdvisePacket(); | |
} | |
if (p) | |
{ | |
p->m_rtEventTime = time1; p->m_rtPeriod = time2; | |
p->m_hNotify = h; p->m_bPeriodic = periodic; | |
Result = AddAdvisePacket( p ); | |
} | |
else Result = 0; | |
m_Serialize.Unlock(); | |
return Result; | |
} | |
HRESULT CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie) | |
{ | |
HRESULT hr = S_FALSE; | |
CAdvisePacket * p_prev = &head; | |
CAdvisePacket * p_n; | |
m_Serialize.Lock(); | |
while ( p_n = p_prev->Next() ) // The Next() method returns NULL when it hits z | |
{ | |
if ( p_n->m_dwAdviseCookie == dwAdviseCookie ) | |
{ | |
Delete( p_prev->RemoveNext() ); | |
--m_dwAdviseCount; | |
hr = S_OK; | |
// Having found one cookie that matches, there should be no more | |
#ifdef DEBUG | |
while (p_n = p_prev->Next()) | |
{ | |
ASSERT(p_n->m_dwAdviseCookie != dwAdviseCookie); | |
p_prev = p_n; | |
} | |
#endif | |
break; | |
} | |
p_prev = p_n; | |
}; | |
m_Serialize.Unlock(); | |
return hr; | |
} | |
REFERENCE_TIME CAMSchedule::Advise( const REFERENCE_TIME & rtTime ) | |
{ | |
REFERENCE_TIME rtNextTime; | |
CAdvisePacket * pAdvise; | |
DbgLog((LOG_TIMING, 2, | |
TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime / (UNITS / MILLISECONDS)))); | |
CAutoLock lck(&m_Serialize); | |
#ifdef DEBUG | |
if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList(); | |
#endif | |
// Note - DON'T cache the difference, it might overflow | |
while ( rtTime >= (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime) && | |
!pAdvise->IsZ() ) | |
{ | |
ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!! | |
ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE); | |
if (pAdvise->m_bPeriodic == TRUE) | |
{ | |
ReleaseSemaphore(pAdvise->m_hNotify,1,NULL); | |
pAdvise->m_rtEventTime += pAdvise->m_rtPeriod; | |
ShuntHead(); | |
} | |
else | |
{ | |
ASSERT( pAdvise->m_bPeriodic == FALSE ); | |
EXECUTE_ASSERT(SetEvent(pAdvise->m_hNotify)); | |
--m_dwAdviseCount; | |
Delete( head.RemoveNext() ); | |
} | |
} | |
DbgLog((LOG_TIMING, 3, | |
TEXT("CAMSchedule::Advise() Next time stamp: %lu ms, for advise %lu."), | |
DWORD(rtNextTime / (UNITS / MILLISECONDS)), pAdvise->m_dwAdviseCookie )); | |
return rtNextTime; | |
} | |
/* Private methods */ | |
DWORD_PTR CAMSchedule::AddAdvisePacket( __inout CAdvisePacket * pPacket ) | |
{ | |
ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME); | |
ASSERT(CritCheckIn(&m_Serialize)); | |
CAdvisePacket * p_prev = &head; | |
CAdvisePacket * p_n; | |
const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie; | |
// This relies on the fact that z is a sentry with a maximal m_rtEventTime | |
for(;;p_prev = p_n) | |
{ | |
p_n = p_prev->m_next; | |
if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break; | |
} | |
p_prev->InsertAfter( pPacket ); | |
++m_dwAdviseCount; | |
DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"), | |
pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) )); | |
// If packet added at the head, then clock needs to re-evaluate wait time. | |
if ( p_prev == &head ) SetEvent( m_ev ); | |
return Result; | |
} | |
void CAMSchedule::Delete( __inout CAdvisePacket * pPacket ) | |
{ | |
if ( m_dwCacheCount >= dwCacheMax ) delete pPacket; | |
else | |
{ | |
m_Serialize.Lock(); | |
pPacket->m_next = m_pAdviseCache; | |
m_pAdviseCache = pPacket; | |
++m_dwCacheCount; | |
m_Serialize.Unlock(); | |
} | |
} | |
// Takes the head of the list & repositions it | |
void CAMSchedule::ShuntHead() | |
{ | |
CAdvisePacket * p_prev = &head; | |
CAdvisePacket * p_n; | |
m_Serialize.Lock(); | |
CAdvisePacket *const pPacket = head.m_next; | |
// This will catch both an empty list, | |
// and if somehow a MAX_TIME time gets into the list | |
// (which would also break this method). | |
ASSERT( pPacket->m_rtEventTime < MAX_TIME ); | |
// This relies on the fact that z is a sentry with a maximal m_rtEventTime | |
for(;;p_prev = p_n) | |
{ | |
p_n = p_prev->m_next; | |
if ( p_n->m_rtEventTime > pPacket->m_rtEventTime ) break; | |
} | |
// If p_prev == pPacket then we're already in the right place | |
if (p_prev != pPacket) | |
{ | |
head.m_next = pPacket->m_next; | |
(p_prev->m_next = pPacket)->m_next = p_n; | |
} | |
#ifdef DEBUG | |
DbgLog((LOG_TIMING, 2, TEXT("Periodic advise %lu, shunted to %lu"), | |
pPacket->m_dwAdviseCookie, (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) )); | |
#endif | |
m_Serialize.Unlock(); | |
} | |
#ifdef DEBUG | |
void CAMSchedule::DumpLinkedList() | |
{ | |
m_Serialize.Lock(); | |
int i=0; | |
DbgLog((LOG_TIMING, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this)); | |
for ( CAdvisePacket * p = &head | |
; p | |
; p = p->m_next , i++ | |
) | |
{ | |
DbgLog((LOG_TIMING, 1, TEXT("Advise List # %lu, Cookie %d, RefTime %lu"), | |
i, | |
p->m_dwAdviseCookie, | |
p->m_rtEventTime / (UNITS / MILLISECONDS) | |
)); | |
} | |
m_Serialize.Unlock(); | |
} | |
#endif |