397 lines
11 KiB
C
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;
|
|
} |