|
|
/* Copyright (c) 1998 Microsoft Corporation */ /*
* @DOC DMusic16 * * @MODULE EQueue.c - Event queue routines | * * These routines maintain queues of events. It is expected that other routines will operate * directly on the queue. The following invariants must be maintained: * * If the queue is empty, then the head and tail pointers must be NULL and the element count must be zero. * * The queue must not contain circular links. * * An event may only be on one queue. * * The element count must be equal to the number of events in the queue. */ #include <windows.h>
#include <mmsystem.h>
#include <memory.h>
#include "dmusic16.h"
#include "debug.h"
/* @func Initialize an event queue to be empty
* * @comm * * Any previous contents of the queue will be lost (NOT freed). */ VOID PASCAL QueueInit( NPEVENTQUEUE pQueue) /* @parm Pointer to the queue to initialize */ { DPF(4, "QueueInit(%04X)", (WORD)pQueue); pQueue->pHead = NULL; pQueue->pTail = NULL; pQueue->cEle = 0;
AssertQueueValid(pQueue); }
/* @func Append an event to the end of the queue
* */ VOID PASCAL QueueAppend( NPEVENTQUEUE pQueue, /* @parm Pointer to the queue */ LPEVENT pEvent) /* @parm Pointer to the event to tack on the end of the queue */ { DPF(4, "QueueAppend(%04X,%08lX)", (WORD)pQueue, (DWORD)pEvent); if (pQueue->cEle) { assert(pQueue->pHead); assert(pQueue->pTail);
pQueue->pTail->lpNext = pEvent; } else { assert(NULL == pQueue->pHead); assert(NULL == pQueue->pTail); pQueue->pHead = pEvent; } pEvent->lpNext = NULL; pQueue->pTail = pEvent; ++pQueue->cEle;
AssertQueueValid(pQueue); }
/* @func Concatenate two queues
* * @comm * * This function tacks the contents of <p pSrc> onto the end of <p pDest> in very short constant time. * <p pSrc> is left empty after the operation. */ VOID PASCAL QueueCat( NPEVENTQUEUE pDest, /* @parm The queue to receive new events */ NPEVENTQUEUE pSrc) /* @parm The queue which will lost all its events */ { DPF(4, "QueueCat(%04X,%04X)", (WORD)pDest, (WORD)pSrc); if (0 == pSrc->cEle) { assert(NULL == pSrc->pHead); assert(NULL == pSrc->pTail);
return; }
assert(pSrc->pHead); assert(pSrc->pTail);
if (0 != pDest->cEle) { assert(pDest->pHead); assert(pDest->pTail); pDest->cEle += pSrc->cEle; pDest->pTail->lpNext = pSrc->pHead; pDest->pTail = pSrc->pTail; } else { assert(NULL == pDest->pHead); assert(NULL == pDest->pTail);
*pDest = *pSrc; }
pSrc->pHead = NULL; pSrc->pTail = NULL; pSrc->cEle = 0; AssertQueueValid(pDest); AssertQueueValid(pSrc); }
/* @func Dequeue an element from the front of the queue
* * @rdesc Returns an event pointer or NULL if the queue is empty * */ LPEVENT PASCAL QueueRemoveFromFront( NPEVENTQUEUE pQueue) /* @parm The queue to dequeue from */ { LPEVENT pEvent; DPF(4, "QueueRemoveFromFront(%04X)", (WORD)pQueue);
if (0 == pQueue->cEle) { assert(NULL == pQueue->pHead); assert(NULL == pQueue->pTail); return NULL; }
assert(pQueue->pHead); assert(pQueue->pTail);
pEvent = pQueue->pHead;
if (0 != --pQueue->cEle) { assert(pQueue->pHead != pQueue->pTail); pQueue->pHead = pQueue->pHead->lpNext; } else { assert(pQueue->pHead == pQueue->pTail);
pQueue->pHead = NULL; pQueue->pTail = NULL; }
AssertQueueValid(pQueue);
return pEvent; }
/* @func Enumerate the events in a queue, possibly deleting some or all of them
* * @comm * * This function calls the function pointed to by <p pfnFilter> once for each event in * the queue, starting at the front and working towards the back. * * The function <p pfnFilter> may return one of two values: * @flag QUEUE_FILTER_KEEP | If the event is to be kept * @flag QUEUE_FILTER_REMOVE | If the event is to be removed from the queue */ VOID PASCAL QueueFilter( NPEVENTQUEUE pQueue, /* @parm The queue to enumerate */ DWORD dwInstance, /* @parm Instance data which will be passed to
<p pfnFilter> on each call. */ PFNQUEUEFILTER pfnFilter) /* @parm The function to call with each event */ { LPEVENT pPrev; LPEVENT pCurr; LPEVENT pNext;
DPF(4, "QueueFilter(%04X, %08lX, %08lX)", (WORD)pQueue, (DWORD)dwInstance, (DWORD)pfnFilter);
pPrev = NULL; pCurr = pQueue->pHead;
while (pCurr) { /* Callback is allowed to relink into another queue, so save the next
* pointer now. */ pNext = pCurr->lpNext;
switch((*pfnFilter)(pCurr, dwInstance)) { case QUEUE_FILTER_REMOVE: if (pPrev) { pPrev->lpNext = pNext; } else { pQueue->pHead = pNext; }
if (pNext == NULL) { pQueue->pTail = pPrev; }
--pQueue->cEle; AssertQueueValid(pQueue); pCurr = pNext; break;
case QUEUE_FILTER_KEEP: pPrev = pCurr; pCurr = pNext; break; default: assert(0); } }
AssertQueueValid(pQueue); }
/* @func Peek at the head of the event queue to see what's next
* * @comm * * Non-destructively return the first event in the queue * * @rdesc * Returns the event pointer or NULL if the queue is empty */ LPEVENT PASCAL QueuePeek( NPEVENTQUEUE pQueue) { DPF(4, "QueuePeek(%04X)", (WORD)pQueue); return pQueue->pHead; }
/* @func Look at the queue and make sure it's internally consistent.
* * @comm * * Walk the queue and make sure it isn't circularly linked. Also make sure the count * is correct. * * Asserts into debugger if queue is corrupt. * * Called by the AssertQueueValid macro in debug builds. * * Disables interrupts to avoid false reports of corruption based on the queue changing under * the routine. * */ #ifdef DEBUG
void PASCAL _AssertQueueValid( NPEVENTQUEUE pQueue, LPSTR szFile, UINT uLine) { LPEVENT pEventSlow; LPEVENT pEventFast; UINT cEle; WORD wIntStat; BOOL fTrace = FALSE;
wIntStat = DisableInterrupts(); if (!pQueue) { DPF(0, "_AssertQueueValid %s@%u: Passed NULL!", szFile, uLine); assert(FALSE); goto cleanup; }
pEventFast = pEventSlow = pQueue->pHead;
cEle = 0;
while (pEventSlow) { ++cEle; pEventSlow = pEventSlow->lpNext; if (pEventFast) { pEventFast = pEventFast->lpNext; }
if (pEventFast) { pEventFast = pEventFast->lpNext; } if (pEventSlow && pEventFast && pEventSlow == pEventFast) { DPF(0, "_AssertQueueValid %s@%u: Queue %04X is circularly linked!", szFile, uLine, (WORD)pQueue); assert(FALSE); fTrace = TRUE; break; } } if (cEle != pQueue->cEle) { DPF(0, "_AssertQueueValid %s@%u: Queue %04X has incorrect element count!", szFile, uLine, (WORD)pQueue); assert(FALSE); fTrace = TRUE; } if ((pQueue->pHead && !pQueue->pTail) || (pQueue->pTail && !pQueue->pHead)) { DPF(0, "_AssertQueueValid %s@%u: Queue %04X head XOR tail is NULL!", szFile, uLine, (WORD)pQueue); assert(FALSE); fTrace = TRUE; }
if (fTrace) { DPF(0, "Queue %04X: head %08lX tail %08lX count %u", (WORD)pQueue, (DWORD)pQueue->pHead, (DWORD)pQueue->pTail, (WORD)pQueue->cEle); for (pEventSlow = pQueue->pHead; pEventSlow; pEventSlow = pEventSlow->lpNext) { DPF(2, " Event %08lX: lpNext %08lX msTime %lu wFlags %04X cbEvent %04X", (DWORD)pEventSlow, (DWORD)pEventSlow->lpNext, (DWORD)pEventSlow->msTime, (WORD)pEventSlow->wFlags, (WORD)pEventSlow->cbEvent); } }
cleanup: RestoreInterrupts(wIntStat); } #endif //DEBUG
|