home *** CD-ROM | disk | FTP | other *** search
/ Isometric Game Programming with DirectX 7.0 / Isometric Game Programming.iso / directx / dxf / samples / multimedia / directshow / baseclasses / schedule.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-02  |  7.8 KB  |  285 lines

  1. //------------------------------------------------------------------------------
  2. // File: Schedule.cpp
  3. //
  4. // Desc: DirectShow base classes.
  5. //
  6. // Copyright (c) 1996 - 2000, Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include <streams.h>
  11.  
  12. // DbgLog values (all on LOG_TIMING):
  13. //
  14. // 2 for schedulting, firing and shunting of events
  15. // 3 for wait delays and wake-up times of event thread
  16. // 4 for details of whats on the list when the thread awakes
  17.  
  18. /* Construct & destructors */
  19.  
  20. CAMSchedule::CAMSchedule( HANDLE ev )
  21. : CBaseObject(TEXT("CAMSchedule"))
  22. , head(&z, 0), z(0, MAX_TIME)
  23. , m_dwNextCookie(0), m_dwAdviseCount(0)
  24. , m_pAdviseCache(0), m_dwCacheCount(0)
  25. , m_ev( ev )
  26. {
  27.     head.m_dwAdviseCookie = z.m_dwAdviseCookie = 0;
  28. }
  29.  
  30. CAMSchedule::~CAMSchedule()
  31. {
  32.     m_Serialize.Lock();
  33.  
  34.     // Delete cache
  35.     CAdvisePacket * p = m_pAdviseCache;
  36.     while (p)
  37.     {
  38.         CAdvisePacket *const p_next = p->m_next;
  39.         delete p;
  40.         p = p_next;
  41.     }
  42.  
  43.     ASSERT( m_dwAdviseCount == 0 );
  44.     // Better to be safe than sorry
  45.     if ( m_dwAdviseCount > 0 )
  46.     {
  47.         DumpLinkedList();
  48.         while ( !head.m_next->IsZ() )
  49.         {
  50.             head.DeleteNext();
  51.             --m_dwAdviseCount;
  52.         }
  53.     }
  54.  
  55.     // If, in the debug version, we assert twice, it means, not only
  56.     // did we have left over advises, but we have also let m_dwAdviseCount
  57.     // get out of sync. with the number of advises actually on the list.
  58.     ASSERT( m_dwAdviseCount == 0 );
  59.  
  60.     m_Serialize.Unlock();
  61. }
  62.  
  63. /* Public methods */
  64.  
  65. DWORD CAMSchedule::GetAdviseCount()
  66. {
  67.     // No need to lock, m_dwAdviseCount is 32bits & declared volatile
  68.     return m_dwAdviseCount;
  69. }
  70.  
  71. REFERENCE_TIME CAMSchedule::GetNextAdviseTime()
  72. {
  73.     CAutoLock lck(&m_Serialize); // Need to stop the linked list from changing
  74.     return head.m_next->m_rtEventTime;
  75. }
  76.  
  77. DWORD_PTR CAMSchedule::AddAdvisePacket
  78. ( const REFERENCE_TIME & time1
  79. , const REFERENCE_TIME & time2
  80. , HANDLE h, BOOL periodic
  81. )
  82. {
  83.     // Since we use MAX_TIME as a sentry, we can't afford to
  84.     // schedule a notification at MAX_TIME
  85.     ASSERT( time1 < MAX_TIME );
  86.     DWORD_PTR Result;
  87.     CAdvisePacket * p;
  88.  
  89.     m_Serialize.Lock();
  90.  
  91.     if (m_pAdviseCache)
  92.     {
  93.         p = m_pAdviseCache;
  94.         m_pAdviseCache = p->m_next;
  95.         --m_dwCacheCount;
  96.     }
  97.     else
  98.     {
  99.         p = new CAdvisePacket();
  100.     }
  101.     if (p)
  102.     {
  103.         p->m_rtEventTime = time1; p->m_rtPeriod = time2;
  104.         p->m_hNotify = h; p->m_bPeriodic = periodic;
  105.         Result = AddAdvisePacket( p );
  106.     }
  107.     else Result = 0;
  108.  
  109.     m_Serialize.Unlock();
  110.  
  111.     return Result;
  112. }
  113.  
  114. HRESULT CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie)
  115. {
  116.     HRESULT hr = S_FALSE;
  117.     CAdvisePacket * p_prev = &head;
  118.     CAdvisePacket * p_n;
  119.     m_Serialize.Lock();
  120.     while ( p_n = p_prev->Next() ) // The Next() method returns NULL when it hits z
  121.     {
  122.         if ( p_n->m_dwAdviseCookie == dwAdviseCookie )
  123.         {
  124.             Delete( p_prev->RemoveNext() );
  125.             --m_dwAdviseCount;
  126.             hr = S_OK;
  127.         // Having found one cookie that matches, there should be no more
  128.             #ifdef DEBUG
  129.            while (p_n = p_prev->Next())
  130.                {
  131.                    ASSERT(p_n->m_dwAdviseCookie != dwAdviseCookie);
  132.                    p_prev = p_n;
  133.                }
  134.             #endif
  135.             break;
  136.         }
  137.         p_prev = p_n;
  138.     };
  139.     m_Serialize.Unlock();
  140.     return hr;
  141. }
  142.  
  143. REFERENCE_TIME CAMSchedule::Advise( const REFERENCE_TIME & rtTime )
  144. {
  145.     REFERENCE_TIME  rtNextTime;
  146.     CAdvisePacket * pAdvise;
  147.  
  148.     DbgLog((LOG_TIMING, 2,
  149.         TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime / (UNITS / MILLISECONDS))));
  150.  
  151.     CAutoLock lck(&m_Serialize);
  152.  
  153.     #ifdef DEBUG
  154.         if (DbgCheckModuleLevel(LOG_TIMING, 4)) DumpLinkedList();
  155.     #endif
  156.  
  157.     //  Note - DON'T cache the difference, it might overflow 
  158.     while ( rtTime >= (rtNextTime = (pAdvise=head.m_next)->m_rtEventTime) &&
  159.             !pAdvise->IsZ() )
  160.     {
  161.         ASSERT(pAdvise->m_dwAdviseCookie); // If this is zero, its the head or the tail!!
  162.  
  163.         ASSERT(pAdvise->m_hNotify != INVALID_HANDLE_VALUE);
  164.  
  165.         if (pAdvise->m_bPeriodic == TRUE)
  166.         {
  167.             EXECUTE_ASSERT(ReleaseSemaphore(pAdvise->m_hNotify,1,NULL));
  168.             pAdvise->m_rtEventTime += pAdvise->m_rtPeriod;
  169.             ShuntHead();
  170.         }
  171.         else
  172.         {
  173.             ASSERT( pAdvise->m_bPeriodic == FALSE );
  174.             EXECUTE_ASSERT(SetEvent(pAdvise->m_hNotify));
  175.             --m_dwAdviseCount;
  176.             Delete( head.RemoveNext() );
  177.         }
  178.  
  179.     }
  180.  
  181.     DbgLog((LOG_TIMING, 3,
  182.             TEXT("CAMSchedule::Advise() Next time stamp: %lu ms, for advise %lu."),
  183.             DWORD(rtNextTime / (UNITS / MILLISECONDS)), pAdvise->m_dwAdviseCookie ));
  184.  
  185.     return rtNextTime;
  186. }
  187.  
  188. /* Private methods */
  189.  
  190. DWORD_PTR CAMSchedule::AddAdvisePacket( CAdvisePacket * pPacket )
  191. {
  192.     ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME);
  193.     ASSERT(CritCheckIn(&m_Serialize));
  194.  
  195.     CAdvisePacket * p_prev = &head;
  196.     CAdvisePacket * p_n;
  197.  
  198.     const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie;
  199.     // This relies on the fact that z is a sentry with a maximal m_rtEventTime
  200.     for(;;p_prev = p_n)
  201.     {
  202.         p_n = p_prev->m_next;
  203.         if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break;
  204.     }
  205.     p_prev->InsertAfter( pPacket );
  206.     ++m_dwAdviseCount;
  207.  
  208.     DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"),
  209.         pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
  210.  
  211.     // If packet added at the head, then clock needs to re-evaluate wait time.
  212.     if ( p_prev == &head ) SetEvent( m_ev );
  213.  
  214.     return Result;
  215. }
  216.  
  217. void CAMSchedule::Delete( CAdvisePacket * pPacket )
  218. {
  219.     if ( m_dwCacheCount >= dwCacheMax ) delete pPacket;
  220.     else
  221.     {
  222.         m_Serialize.Lock();
  223.         pPacket->m_next = m_pAdviseCache;
  224.         m_pAdviseCache = pPacket;
  225.         ++m_dwCacheCount;
  226.         m_Serialize.Unlock();
  227.     }
  228. }
  229.  
  230.  
  231. // Takes the head of the list & repositions it
  232. void CAMSchedule::ShuntHead()
  233. {
  234.     CAdvisePacket * p_prev = &head;
  235.     CAdvisePacket * p_n;
  236.  
  237.     m_Serialize.Lock();
  238.     CAdvisePacket *const pPacket = head.m_next;
  239.  
  240.     // This will catch both an empty list,
  241.     // and if somehow a MAX_TIME time gets into the list
  242.     // (which would also break this method).
  243.     ASSERT( pPacket->m_rtEventTime < MAX_TIME );
  244.  
  245.     // This relies on the fact that z is a sentry with a maximal m_rtEventTime
  246.     for(;;p_prev = p_n)
  247.     {
  248.         p_n = p_prev->m_next;
  249.         if ( p_n->m_rtEventTime > pPacket->m_rtEventTime ) break;
  250.     }
  251.     // If p_prev == pPacket then we're already in the right place
  252.     if (p_prev != pPacket)
  253.     {
  254.         head.m_next = pPacket->m_next;
  255.         (p_prev->m_next = pPacket)->m_next = p_n;
  256.     }
  257.     #ifdef DEBUG
  258.         DbgLog((LOG_TIMING, 2, TEXT("Periodic advise %lu, shunted to %lu"),
  259.             pPacket->m_dwAdviseCookie, (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) ));
  260.     #endif
  261.     m_Serialize.Unlock();
  262. }
  263.  
  264.  
  265. #ifdef DEBUG
  266. void CAMSchedule::DumpLinkedList()
  267. {
  268.     m_Serialize.Lock();
  269.     int i=0;
  270.     DbgLog((LOG_TIMING, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this));
  271.     for ( CAdvisePacket * p = &head
  272.         ; p
  273.         ; p = p->m_next         , i++
  274.         )    
  275.     {
  276.         DbgLog((LOG_TIMING, 1, TEXT("Advise List # %lu, Cookie %d,  RefTime %lu"),
  277.             i,
  278.         p->m_dwAdviseCookie,
  279.         p->m_rtEventTime / (UNITS / MILLISECONDS)
  280.             ));
  281.     }
  282.     m_Serialize.Unlock();
  283. }
  284. #endif
  285.