1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes.
6 // Copyright (c) 1996-2002 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
12 // DbgLog values (all on LOG_TIMING):
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
18 /* Construct & destructors */
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)
27 head
.m_dwAdviseCookie
= z
.m_dwAdviseCookie
= 0;
30 CAMSchedule::~CAMSchedule()
35 CAdvisePacket
* p
= m_pAdviseCache
;
38 CAdvisePacket
*const p_next
= p
->m_next
;
43 ASSERT( m_dwAdviseCount
== 0 );
44 // Better to be safe than sorry
45 if ( m_dwAdviseCount
> 0 )
48 while ( !head
.m_next
->IsZ() )
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 );
65 DWORD
CAMSchedule::GetAdviseCount()
67 // No need to lock, m_dwAdviseCount is 32bits & declared volatile
68 return m_dwAdviseCount
;
71 REFERENCE_TIME
CAMSchedule::GetNextAdviseTime()
73 CAutoLock
lck(&m_Serialize
); // Need to stop the linked list from changing
74 return head
.m_next
->m_rtEventTime
;
77 DWORD_PTR
CAMSchedule::AddAdvisePacket
78 ( const REFERENCE_TIME
& time1
79 , const REFERENCE_TIME
& time2
80 , HANDLE h
, BOOL periodic
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
);
94 m_pAdviseCache
= p
->m_next
;
99 p
= new CAdvisePacket();
103 p
->m_rtEventTime
= time1
; p
->m_rtPeriod
= time2
;
104 p
->m_hNotify
= h
; p
->m_bPeriodic
= periodic
;
105 Result
= AddAdvisePacket( p
);
109 m_Serialize
.Unlock();
114 HRESULT
CAMSchedule::Unadvise(DWORD_PTR dwAdviseCookie
)
116 HRESULT hr
= S_FALSE
;
117 CAdvisePacket
* p_prev
= &head
;
120 while ( p_n
= p_prev
->Next() ) // The Next() method returns NULL when it hits z
122 if ( p_n
->m_dwAdviseCookie
== dwAdviseCookie
)
124 Delete( p_prev
->RemoveNext() );
127 // Having found one cookie that matches, there should be no more
129 while (p_n
= p_prev
->Next())
131 ASSERT(p_n
->m_dwAdviseCookie
!= dwAdviseCookie
);
139 m_Serialize
.Unlock();
143 REFERENCE_TIME
CAMSchedule::Advise( const REFERENCE_TIME
& rtTime
)
145 REFERENCE_TIME rtNextTime
;
146 CAdvisePacket
* pAdvise
;
148 DbgLog((LOG_TIMING
, 2,
149 TEXT("CAMSchedule::Advise( %lu ms )"), ULONG(rtTime
/ (UNITS
/ MILLISECONDS
))));
151 CAutoLock
lck(&m_Serialize
);
154 if (DbgCheckModuleLevel(LOG_TIMING
, 4)) DumpLinkedList();
157 // Note - DON'T cache the difference, it might overflow
158 while ( rtTime
>= (rtNextTime
= (pAdvise
=head
.m_next
)->m_rtEventTime
) &&
161 ASSERT(pAdvise
->m_dwAdviseCookie
); // If this is zero, its the head or the tail!!
163 ASSERT(pAdvise
->m_hNotify
!= INVALID_HANDLE_VALUE
);
165 if (pAdvise
->m_bPeriodic
== TRUE
)
167 ReleaseSemaphore(pAdvise
->m_hNotify
,1,NULL
);
168 pAdvise
->m_rtEventTime
+= pAdvise
->m_rtPeriod
;
173 ASSERT( pAdvise
->m_bPeriodic
== FALSE
);
174 EXECUTE_ASSERT(SetEvent(pAdvise
->m_hNotify
));
176 Delete( head
.RemoveNext() );
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
));
188 /* Private methods */
190 DWORD_PTR
CAMSchedule::AddAdvisePacket( CAdvisePacket
* pPacket
)
192 ASSERT(pPacket
->m_rtEventTime
>= 0 && pPacket
->m_rtEventTime
< MAX_TIME
);
193 ASSERT(CritCheckIn(&m_Serialize
));
195 CAdvisePacket
* p_prev
= &head
;
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
202 p_n
= p_prev
->m_next
;
203 if ( p_n
->m_rtEventTime
>= pPacket
->m_rtEventTime
) break;
205 p_prev
->InsertAfter( pPacket
);
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
)) ));
211 // If packet added at the head, then clock needs to re-evaluate wait time.
212 if ( p_prev
== &head
) SetEvent( m_ev
);
217 void CAMSchedule::Delete( CAdvisePacket
* pPacket
)
219 if ( m_dwCacheCount
>= dwCacheMax
) delete pPacket
;
223 pPacket
->m_next
= m_pAdviseCache
;
224 m_pAdviseCache
= pPacket
;
226 m_Serialize
.Unlock();
231 // Takes the head of the list & repositions it
232 void CAMSchedule::ShuntHead()
234 CAdvisePacket
* p_prev
= &head
;
238 CAdvisePacket
*const pPacket
= head
.m_next
;
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
);
245 // This relies on the fact that z is a sentry with a maximal m_rtEventTime
248 p_n
= p_prev
->m_next
;
249 if ( p_n
->m_rtEventTime
> pPacket
->m_rtEventTime
) break;
251 // If p_prev == pPacket then we're already in the right place
252 if (p_prev
!= pPacket
)
254 head
.m_next
= pPacket
->m_next
;
255 (p_prev
->m_next
= pPacket
)->m_next
= p_n
;
258 DbgLog((LOG_TIMING
, 2, TEXT("Periodic advise %lu, shunted to %lu"),
259 pPacket
->m_dwAdviseCookie
, (pPacket
->m_rtEventTime
/ (UNITS
/ MILLISECONDS
)) ));
261 m_Serialize
.Unlock();
266 void CAMSchedule::DumpLinkedList()
270 DbgLog((LOG_TIMING
, 1, TEXT("CAMSchedule::DumpLinkedList() this = 0x%p"), this));
271 for ( CAdvisePacket
* p
= &head
273 ; p
= p
->m_next
, i
++
276 DbgLog((LOG_TIMING
, 1, TEXT("Advise List # %lu, Cookie %d, RefTime %lu"),
279 p
->m_rtEventTime
/ (UNITS
/ MILLISECONDS
)
282 m_Serialize
.Unlock();