// Copyright (c) 1998-1999 Microsoft Corporation // queue.cpp #include "debug.h" #define ASSERT assert #include "dmime.h" #include "dmperf.h" CPMsgQueue::CPMsgQueue() { m_pTop = NULL; m_pLastAccessed = NULL; } CPMsgQueue::~CPMsgQueue() { } static PRIV_PMSG * sortevents( PRIV_PMSG * pEvents, long lLen ) { PRIV_PMSG * pLeft; PRIV_PMSG * pRight ; long lLeft; long lRight ; PRIV_PMSG * pTop ; if( lLen < 3 ) { if( !pEvents ) return( 0 ) ; if( lLen == 1 ) return( pEvents ) ; pLeft = pEvents ; pRight = pEvents->pNext ; if( !pRight ) return( pLeft ) ; if( pLeft->rtTime > pRight->rtTime ) { pLeft->pNext = NULL ; pRight->pNext = pLeft ; return( pRight ) ; } return( pLeft ) ; } lLeft = lLen >> 1 ; lRight = lLen - lLeft; pLeft = pEvents ; for (;lLeft > 1;pEvents = pEvents->pNext) lLeft--; pRight = sortevents( pEvents->pNext, lRight ) ; pEvents->pNext = NULL ; pLeft = sortevents( pLeft, lLen - lRight ) ; pTop = NULL ; for( ; pLeft && pRight ; ) { if( pLeft->rtTime < pRight->rtTime ) { if( !pTop ) pTop = pLeft ; else pEvents->pNext = pLeft ; pEvents = pLeft ; pLeft = pEvents->pNext ; } else { if( !pTop ) pTop = pRight ; else pEvents->pNext = pRight ; pEvents = pRight ; pRight = pEvents->pNext ; } } if( pLeft ) pEvents->pNext = pLeft ; else pEvents->pNext = pRight ; return( pTop ) ; } void CPMsgQueue::Sort() { m_pTop = sortevents(m_pTop, GetCount()) ; m_pLastAccessed = NULL; } long CPMsgQueue::GetCount() { long lCount = 0; PRIV_PMSG *pScan = GetHead(); for (;pScan;pScan = pScan->pNext) { lCount++; } return lCount; } void CPMsgQueue::Enqueue(PRIV_PMSG *pItem) { if (!pItem) { TraceI(0, "ENQUEUE: Attempt to enqueue a NULL pItem!\n"); return; } // Ensure not already queued... if (pItem->dwPrivFlags & PRIV_FLAG_QUEUED) { TraceI(0,"ENQUEUE: Item thinks it is still in a queue!\n"); return; } pItem->dwPrivFlags |= PRIV_FLAG_QUEUED; PRIV_PMSG *pScan; #ifdef DBG // Verify robustness of list. Check that the event is not already in the list // and that the time stamps are all in order. REFERENCE_TIME rtTime = 0; for (pScan = m_pTop;pScan;pScan = pScan->pNext) { if (pScan == pItem) { TraceI(0,"ENQUEUE: Item is already in the queue!\n"); return; } // this must queue events in time sorted order if (pScan->rtTime < rtTime) { TraceI(0,"ENQUEUE: Queue is not in time order!\n"); pScan->rtTime = rtTime; } else if (pScan->rtTime > rtTime) { rtTime = pScan->rtTime; } } #endif if ( !(pItem->dwFlags & DMUS_PMSGF_REFTIME) ) // sorting on reftime, so this must be valid { TraceI(0, "ENQUEUE: Attempt to enqueue a pItem with a bogus RefTime!\n"); return; } if (m_pLastAccessed && (m_pLastAccessed->rtTime <= pItem->rtTime)) { pScan = m_pLastAccessed; } else { pScan = m_pTop; } if ( pScan && ( pScan->rtTime <= pItem->rtTime ) ) { for (;pScan->pNext; pScan = pScan->pNext ) { if( pScan->pNext->rtTime > pItem->rtTime ) { break; } } pItem->pNext = pScan->pNext; pScan->pNext = pItem; } else { pItem->pNext = m_pTop; m_pTop = pItem; } m_pLastAccessed = pItem; } /* Remove the oldest event before time rtTime, making sure that there is still at minimum one event prior to that time stamp. This ensures that there is a sufficiently old event, but gets rid of old stale events. This is used by the timesig and tempomap lists. */ PRIV_PMSG *CPMsgQueue::FlushOldest(REFERENCE_TIME rtTime) { PRIV_PMSG *pNext; if (m_pTop && (pNext = m_pTop->pNext)) { if (pNext->rtTime < rtTime) { PRIV_PMSG *pDelete = m_pTop; if (m_pLastAccessed == m_pTop) { m_pLastAccessed = pNext; } m_pTop = pNext; pDelete->dwPrivFlags &= ~PRIV_FLAG_QUEUED; pDelete->pNext = NULL; return pDelete; } } return NULL; } PRIV_PMSG *CPMsgQueue::Dequeue() { PRIV_PMSG *pItem = m_pTop; if (pItem != NULL) { m_pTop = pItem->pNext; pItem->dwPrivFlags &= ~PRIV_FLAG_QUEUED; pItem->pNext = NULL; if (m_pLastAccessed == pItem) { m_pLastAccessed = m_pTop; } } return pItem; } PRIV_PMSG *CPMsgQueue::Dequeue(PRIV_PMSG *pItem) { ASSERT(pItem); if (pItem == m_pTop) { return Dequeue(); } PRIV_PMSG *pScan; PRIV_PMSG *pNext; if (m_pLastAccessed && (m_pLastAccessed->rtTime < pItem->rtTime)) { pScan = m_pLastAccessed; } else { pScan = m_pTop; } for (;pScan;pScan = pNext) { pNext = pScan->pNext; if (pNext == pItem) { pScan->pNext = pItem->pNext; pItem->pNext = NULL; pItem->dwPrivFlags &= ~PRIV_FLAG_QUEUED; if (m_pLastAccessed == pItem) { m_pLastAccessed = pScan; } return pItem; } } if (m_pLastAccessed) { // This happens every now and then as a result of a curve setting rtTime to 0 // in the middle of FlushEventQueue. // This should be fixed, but this patch will work for now. m_pLastAccessed = NULL; return Dequeue(pItem); } return NULL; } // queue Segment nodes in time order. pItem must be in the same // time base as all items in ppList (RefTime or Music Time.) void CSegStateList::Insert(CSegState* pItem) { CSegState *pScan = GetHead(); CSegState *pNext; pItem->SetNext(NULL); if (pScan) { if( pItem->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) { ASSERT( pScan->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ); // Avoid putting circularities in the list if (pItem == pScan) { TraceI(0, "ENQUEUE (SEGMENT RT): NODE IS ALREADY IN AT THE HEAD OF LIST\n"); } else if( pItem->m_rtGivenStart < pScan->m_rtGivenStart ) { AddHead(pItem); } else { while( pNext = pScan->GetNext() ) { ASSERT( pScan->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ); // Am I trying to insert something that's already in the list? if (pItem == pScan) { break; } // Check for the queue getting corrupted (happens on multiproc machines at 400mhz) if ( ( pNext->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) && pScan->m_rtGivenStart > pNext->m_rtGivenStart ) { TraceI(0, "ENQUEUE (SEGMENT RT): LOOP CONDITION VIOLATED\n"); // get rid of the potential circularity in the list. Note that this // (or actually the creation of the circularity) could cause memory leaks. pScan->SetNext(NULL); break; } if( pItem->m_rtGivenStart < pNext->m_rtGivenStart ) { break; } pScan = pNext; } if (pItem != pScan) { pItem->SetNext(pScan->GetNext()); pScan->SetNext(pItem); } else { TraceI(0, "ENQUEUE (SEGMENT RT): NODE IS ALREADY IN LIST\n"); } } } else { ASSERT( !( pScan->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) ); // Avoid putting circularities in the list if (pItem == pScan) { TraceI(0, "ENQUEUE (SEGMENT MT): NODE IS ALREADY IN AT THE HEAD OF LIST\n"); } else if( pItem->m_mtResolvedStart < pScan->m_mtResolvedStart ) { AddHead(pItem); } else { while( pNext = pScan->GetNext() ) { ASSERT( !( pScan->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) ); // Am I trying to insert something that's already in the list? if (pItem == pScan) { break; } // Check for the queue getting corrupted (happens on multiproc machines at 400mhz) if ( !( pNext->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) && pScan->m_mtResolvedStart > pNext->m_mtResolvedStart ) { TraceI(0, "ENQUEUE (SEGMENT MT): LOOP CONDITION VIOLATED\n"); // get rid of the potential circularity in the list. Note that this // (or actually the creation of the circularity) could cause memory leaks. pScan->SetNext(NULL); break; } if( pItem->m_mtResolvedStart < pNext->m_mtResolvedStart ) { break; } pScan = pNext; } if (pItem != pScan) { pItem->SetNext(pScan->GetNext()); pScan->SetNext(pItem); } else { TraceI(0, "ENQUEUE (SEGMENT MT): NODE IS ALREADY IN LIST\n"); } } } } else { m_pHead = pItem; } } /* void enqueue(CSegState **ppList, CSegState *pItem) { CSegState *li = *ppList; if (li) { if( pItem->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) { ASSERT( li->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ); // Avoid putting circularities in the list if (pItem == *ppList) { TraceI(0, "ENQUEUE (SEGMENT RT): NODE IS ALREADY IN AT THE HEAD OF LIST\n"); } else if( pItem->m_rtGivenStart < li->m_rtGivenStart ) { pItem->pNext = li; *ppList = pItem; } else { while( li->pNext ) { ASSERT( li->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ); // Am I trying to insert something that's already in the list? if (pItem == li) { break; } // Check for the queue getting corrupted (happens on multiproc machines at 400mhz) if ( ( li->pNext->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) && li->m_rtGivenStart > li->pNext->m_rtGivenStart ) { TraceI(0, "ENQUEUE (SEGMENT RT): LOOP CONDITION VIOLATED\n"); // get rid of the potential circularity in the list. Note that this // (or actually the creation of the circularity) could cause memory leaks. li->pNext = NULL; break; } if( pItem->m_rtGivenStart < li->pNext->m_rtGivenStart ) { break; } li = li->pNext; } if (pItem != li) { pItem->pNext = li->pNext; li->pNext = pItem; } else { TraceI(0, "ENQUEUE (SEGMENT RT): NODE IS ALREADY IN LIST\n"); } } } else { ASSERT( !( li->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) ); // Avoid putting circularities in the list if (pItem == *ppList) { TraceI(0, "ENQUEUE (SEGMENT MT): NODE IS ALREADY IN AT THE HEAD OF LIST\n"); } else if( pItem->m_mtResolvedStart < li->m_mtResolvedStart ) { pItem->pNext = li; *ppList = pItem; } else { while( li->pNext ) { ASSERT( !( li->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) ); // Am I trying to insert something that's already in the list? if (pItem == li) { break; } // Check for the queue getting corrupted (happens on multiproc machines at 400mhz) if ( !( li->pNext->m_dwPlaySegFlags & DMUS_SEGF_REFTIME ) && li->m_mtResolvedStart > li->pNext->m_mtResolvedStart ) { TraceI(0, "ENQUEUE (SEGMENT MT): LOOP CONDITION VIOLATED\n"); // get rid of the potential circularity in the list. Note that this // (or actually the creation of the circularity) could cause memory leaks. li->pNext = NULL; break; } if( pItem->m_mtResolvedStart < li->pNext->m_mtResolvedStart ) { break; } li = li->pNext; } if (pItem != li) { pItem->pNext = li->pNext; li->pNext = pItem; } else { TraceI(0, "ENQUEUE (SEGMENT MT): NODE IS ALREADY IN LIST\n"); } } } } else { *ppList = pItem; } }*/