forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			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
 |