//------------------------------------------------------------------------------ // File: Schedule.cpp // // Desc: DirectShow base classes. // // Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include // 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( 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( 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