Windows2000/private/windows/media/avi/avifile/disk32.c
2020-09-30 17:12:32 +02:00

397 lines
11 KiB
C

/*
* routines do to queued, asynchrous disk I/O in Win32
* NOTE: these routines exist because Chicago does not yet support overlapped io.
*/
#include <windows.h>
#include <windowsx.h>
//#include <win32.h>
#define _INC_MMDEBUG_CODE_ TRUE
#include "mmdebug.h" // AuxDebug & assert macros
#include "disk32.h"
#define LockList(pHead) EnterCriticalSection (&pHead->csList)
#define UnlockList(pHead) LeaveCriticalSection (&pHead->csList)
BOOL WINAPI QueueInitialize(PQHEAD pHead)
/*
* Initalize the queue.
*/
{
InitializeCriticalSection (&pHead->csList);
if ( ! pHead->hEvtElms)
pHead->hEvtElms = CreateEvent (NULL, TRUE, FALSE, NULL);
// a queue being initalized SHOULD be empty. be sure of it.
assert (pHead->qe.pNext == NULL || pHead->qe.pNext == &pHead->qe);
assert (pHead->qe.pPrev == NULL || pHead->qe.pNext == &pHead->qe);
pHead->qe.pPrev = pHead->qe.pNext = &pHead->qe;
return TRUE;
}
BOOL WINAPI QueueDelete(PQHEAD pHead)
/*
* de-Initalize the queue
*/
{
DeleteCriticalSection (&pHead->csList);
if ( ! pHead->hEvtElms)
{
SetEvent (pHead->hEvtElms); // just in case.
CloseHandle (pHead->hEvtElms);
pHead->hEvtElms = NULL;
}
// a queue being Deleted SHOULD be empty. be sure of it.
assert (pHead->qe.pNext == &pHead->qe);
assert (pHead->qe.pPrev == &pHead->qe);
pHead->qe.pPrev = pHead->qe.pNext = &pHead->qe;
return TRUE;
}
VOID WINAPI QueueInsert(PQHEAD pHead, PQELM pqe)
/*
* insert an element in the queue. If a thread is waiting on the queue it will be awakened.
*/
{
PQELM pqeHead = &pHead->qe;
// this can only happen if the queue has never been initialized
assert (pqeHead->pNext != NULL);
assert (pqeHead->pPrev != NULL);
LockList (pHead);
// insert the new element into the list at the tail.
pqe->pNext = pqeHead;
pqe->pPrev = pqeHead->pPrev;
pqe->pPrev->pNext = pqe;
pqeHead->pPrev = pqe;
// if the element we just inserted at the tail of the list is also at the head of the list.
// The list must have been empty before.
// In this case we want to signal the Event to wake any threads waiting on the queue.
if ((pqeHead->pNext == pqe) && pHead->hEvtElms)
SetEvent (pHead->hEvtElms);
UnlockList (pHead);
}
PQELM WINAPI QueueRemove(PQHEAD pHead, DWORD dwTimeout)
/*
* remove an element from the queue. if the queue is empty.
* wait for an element to be inserted.
* A timeout of 0 can be used to POLL the queue.
*/
{
PQELM pqe;
LockList (pHead);
// next & prev can only be null when a queue is un-initialized.
assert (pHead->qe.pNext != NULL);
assert (pHead->qe.pPrev != NULL);
// if the list is empty and the user specified a non-zero
// timeout and we have a list semaphore available, wait for the semaphore to be signalled.
pqe = pHead->qe.pNext;
if ((pqe == &pHead->qe) && (dwTimeout != 0) && (pHead->hEvtElms != NULL))
{
// the queue is empty - so make sure that the event has not been signalled.
ResetEvent (pHead->hEvtElms);
// unlock the list before waiting so that we dont deadlock the thread that is inserting things into the list.
UnlockList (pHead);
AuxDebugEx (3, DEBUGLINE "Waiting (%d) secs on queue %08x\r\n", dwTimeout, pHead);
WaitForSingleObject (pHead->hEvtElms, dwTimeout);
LockList (pHead);
pqe = pHead->qe.pNext;
}
// if the queue is still empty, set pqe to NULL so that we will return null.
// otherwise remove the head of the queue and return it.
if (pqe == &pHead->qe)
pqe = NULL;
else
{
// remove the element from the list.
pHead->qe.pNext = pqe->pNext;
pqe->pNext->pPrev = pqe->pPrev;
// just to be careful, blank out the
pqe->pPrev = pqe->pNext = NULL;
// if the queue is now empty, reset the event
if ((pHead->qe.pNext == &pHead->qe) && pHead->hEvtElms)
ResetEvent (pHead->hEvtElms);
}
UnlockList (pHead);
return pqe;
}
#ifdef DEBUG
void WINAPI QueueDump(PQHEAD pHead)
{
PQELM pqe;
UINT nMax;
LockList (pHead);
AuxDebugEx (2, "Dumping Queue %08X\r\n", pHead);
AuxDebugEx (2, "\telm %08x (next=%08x, prev=%08x)\r\n", &pHead->qe, pHead->qe);
nMax = 10;
pqe = pHead->qe.pNext;
while (nMax && pqe != &pHead->qe)
{
pqe = pqe->pNext;
--nMax;
AuxDebugEx (2, "\telm %08x (next=%08x, prev=%08x)\r\n", pqe, *pqe);
}
UnlockList (pHead);
}
#endif // DEBUG
DWORD WINAPI AsyncIOThreadProc(LPQIO lpqio)
/*
* thread proc dos sequential writes to a file from buffers queued to it.
*/
{
// we loop forever. exit from this loop is when QueueRemove returns a qiobuf with cb == 0
for (;;)
{
PQIOBUF pqBuf;
DWORD dwOff;
// get the next buffer to be written.
// if we are running down, then dont bother to wait for more buffers if the queue is empty.
pqBuf = (LPVOID)QueueRemove (&lpqio->que, INFINITE);
assert (!pqBuf || !IsBadWritePtr(pqBuf, sizeof(*pqBuf)));
// if we got no buffer back from queue remove, this may be reasonable in the case of two threads, just loop back and try again.
if ( ! pqBuf )
continue;
// break out of the loop when a -1 buffer pointer is queued
if ( pqBuf->lpv == (LPVOID)-1)
{
QueueInsert (&lpqio->queDone, (LPVOID)pqBuf);
break;
}
AuxDebugEx (2, DEBUGLINE "tid %08X %s %X bytes at %08X into %08X\r\n",
lpqio->tid,
pqBuf->bWrite ? "Writing" : "Reading",
pqBuf->cb, pqBuf->dwOffset, pqBuf->lpv);
assert3 (!pqBuf->cb || HIWORD(pqBuf->lpv), "QioThread - invalid buffer %08X", pqBuf->lpv);
if ( HIWORD(pqBuf->lpv) && pqBuf->cb )
{
assert (!IsBadReadPtr(pqBuf->lpv, pqBuf->cb));
dwOff = SetFilePointer (lpqio->hFile, pqBuf->dwOffset, NULL, FILE_BEGIN);
if (dwOff != pqBuf->dwOffset)
{
pqBuf->dwError = GetLastError();
AuxDebug2 ("avifile32 seek error %d", pqBuf->dwError);
}
else
{
if (pqBuf->bWrite)
{
if ( ! WriteFile (lpqio->hFile, pqBuf->lpv, pqBuf->cb, &pqBuf->cbDone, NULL) || (pqBuf->cb != pqBuf->cbDone))
{
pqBuf->dwError = GetLastError();
AuxDebug2 ("avifile32 write error %d", pqBuf->dwError);
}
else
pqBuf->dwError = 0;
}
else
{
if ( ! ReadFile (lpqio->hFile, pqBuf->lpv, pqBuf->cb, &pqBuf->cbDone, NULL) || (pqBuf->cb != pqBuf->cbDone))
{
pqBuf->dwError = GetLastError();
AuxDebug2 ("avifile32 read error %d", pqBuf->dwError);
}
else
pqBuf->dwError = 0;
}
}
}
QueueInsert (&lpqio->queDone, (LPVOID)pqBuf);// Once the write is done, but the buffer on the done queue
}
return 0;
}
BOOL WINAPI QioInitialize(LPQIO lpqio, HANDLE hFile, int nPrio)
/*
* Open a file for queued sequential io.
*/
{
DebugSetOutputLevel (GetProfileInt("debug", "avifil32", 0));
AuxDebugEx (1, DEBUGLINE "QioInitialize (%08x, %d)\r\n", lpqio, nPrio);
nPrio = max(nPrio, THREAD_PRIORITY_IDLE);
nPrio = min(nPrio, THREAD_PRIORITY_TIME_CRITICAL);
QueueInitialize (&lpqio->que);
QueueInitialize (&lpqio->queDone);
assert ( ! lpqio->hThread);
lpqio->hFile = hFile;
lpqio->nPrio = nPrio;
lpqio->hThread = CreateThread (NULL, 0, AsyncIOThreadProc, lpqio, 0, &lpqio->tid);
if ( ! lpqio->hThread)// if we fail creating the thread, cleanup and return error.
{
AuxDebugEx (1, DEBUGLINE "QioInitialize - CreateThread failed\r\n", lpqio, nPrio);
QueueDelete (&lpqio->que);
QueueDelete (&lpqio->queDone);
ZeroMemory (lpqio, sizeof(*lpqio));
return FALSE;
}
SetThreadPriority (lpqio->hThread, lpqio->nPrio);
return TRUE;
}
BOOL WINAPI QioAdd(LPQIO lpqio, PQIOBUF pqBuf)
{
assert (lpqio);
assert (pqBuf);
assert (lpqio->que.qe.pNext != NULL);
assert (lpqio->hThread);
if (!lpqio->hThread)
return FALSE;
// the queue insert/remove code in this function make the assumption that the queue pointers are the first element of the pqBuf structure.
assert ((DWORD)&pqBuf->qe - (DWORD)pqBuf == 0);
// not allowed to queue a buffer with no size or no pointer
assert (HIWORD(pqBuf->lpv));
assert (pqBuf->cb);
pqBuf->bPending = TRUE;
QueueInsert (&lpqio->que, (LPVOID)pqBuf);
return TRUE;
}
BOOL WINAPI QioWait(LPQIO lpqio, PQIOBUF pqBufWait, BOOL bWait)
{
assert (lpqio);
assert (pqBufWait);
assert (lpqio->que.qe.pNext != NULL);
assert (lpqio->hThread);
// the queue insert/remove code in this function make the
// assumption that the queue pointers are the first element of the pqBuf structure.
assert ((DWORD)&pqBufWait->qe - (DWORD)pqBufWait == 0);
if (pqBufWait->bPending)
{
PQIOBUF pqBufT;
DWORD dwTimeout = bWait ? INFINITE : 0;
do
{
pqBufT = (LPVOID) QueueRemove (&lpqio->queDone, dwTimeout);
AuxDebugEx (4, DEBUGLINE "QioWait(%08X) - removed %08X\r\n", lpqio, pqBufT);
assert (!pqBufT || !IsBadWritePtr(pqBufT, sizeof(*pqBufT)));
if (!pqBufT)
return FALSE;
pqBufT->bPending = FALSE;
} while (pqBufT != pqBufWait);
}
return TRUE;
}
BOOL WINAPI QioCommit(LPQIO lpqio)
/*
* Waits for the Qio thread to complete any i/o that is in it's queue.
* then causes the qio thread to exit and waits for it to do so.
*/
{
QIOBUF qb;
assert (lpqio);
AuxDebugEx (2, DEBUGLINE "QioCommit (%08X)\r\n", lpqio);
// queue up a zero size buffer as a placeholder and then wait for the placeholder to be moved to the done queue.
ZeroMemory (&qb, sizeof(qb));
qb.bPending = TRUE;
QueueInsert (&lpqio->que, (LPVOID)&qb);
return QioWait (lpqio, &qb, TRUE);
}
BOOL WINAPI QioShutdown(LPQIO lpqio)
/*
* Waits for the Qio thread to complete any i/o that is in it's queue.
* then causes the qio thread to exit and waits for it to do so.
*/
{
QIOBUF qb;
assert (lpqio);
AuxDebugEx (1, DEBUGLINE "QioShutdown (%08X)\r\n", lpqio);
if ( ! lpqio->hThread)
{
AuxDebugEx (1, DEBUGLINE "QioShutdown - nothing to do!\r\n");
return TRUE;
}
assert (lpqio->hThread);
// queue up a zero size buffer to tell the write thread to quit
ZeroMemory (&qb, sizeof(qb));
qb.lpv = (LPVOID)-1;
qb.bPending = TRUE;
QueueInsert (&lpqio->que, (LPVOID)&qb);
QioWait (lpqio, &qb, TRUE);
// wait for the thread to shut down.
AuxDebugEx (1, DEBUGLINE "Waiting for QIO thread\r\n");
WaitForSingleObject (lpqio->hThread, INFINITE);
AuxDebugEx (1, DEBUGLINE "closeing thread handle\r\n");
CloseHandle (lpqio->hThread), lpqio->hThread = NULL;
//INLINE_BREAK;
// finally, delete the queues
QueueDelete (&lpqio->que);
QueueDelete (&lpqio->queDone);
return TRUE;
}