topical media & game development

talk show tell print

hush-src-multi-BaseClasses-schedule.cpp / cpp



  //------------------------------------------------------------------------------
  // File: Schedule.cpp
  //
  // Desc: DirectShow base classes.
  //
  // Copyright (c) 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( 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
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.