| //------------------------------------------------------------------------------ | |
| // 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 |