285 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			285 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | //------------------------------------------------------------------------------
 | ||
|  | // 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
 |