summaryrefslogtreecommitdiff
path: root/third_party/BaseClasses/schedule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/BaseClasses/schedule.cpp')
-rw-r--r--third_party/BaseClasses/schedule.cpp284
1 files changed, 284 insertions, 0 deletions
diff --git a/third_party/BaseClasses/schedule.cpp b/third_party/BaseClasses/schedule.cpp
new file mode 100644
index 00000000..7d798306
--- /dev/null
+++ b/third_party/BaseClasses/schedule.cpp
@@ -0,0 +1,284 @@
+//------------------------------------------------------------------------------
+// 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