535 lines
13 KiB
C
535 lines
13 KiB
C
|
/****************************************************************************
|
||
|
*
|
||
|
* disk32.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)
|
||
|
|
||
|
/*+ QueueInitalize
|
||
|
*
|
||
|
* Initalize the queue.
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
BOOL WINAPI QueueInitialize
|
||
|
(
|
||
|
PQHEAD pHead
|
||
|
)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*+ QueueDelete
|
||
|
*
|
||
|
* de-Initalize the queue
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
BOOL WINAPI QueueDelete
|
||
|
(
|
||
|
PQHEAD pHead
|
||
|
)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*+ QueueInsert
|
||
|
*
|
||
|
* insert an element in the queue. If a thread is waiting on the queue
|
||
|
* it will be awakened.
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
VOID WINAPI QueueInsert
|
||
|
(
|
||
|
PQHEAD pHead,
|
||
|
PQELM pqe
|
||
|
)
|
||
|
{
|
||
|
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);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*+ QueueRemove
|
||
|
*
|
||
|
* 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 WINAPI QueueRemove
|
||
|
(
|
||
|
PQHEAD pHead,
|
||
|
DWORD dwTimeout
|
||
|
)
|
||
|
{
|
||
|
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
|
||
|
|
||
|
/*+ QueueDump
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
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
|
||
|
|
||
|
/*+ SequentialIOThreadProc
|
||
|
*
|
||
|
* thread proc dos sequential writes to a file from buffers queued
|
||
|
* to it.
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
DWORD WINAPI AsyncIOThreadProc
|
||
|
(
|
||
|
LPQIO lpqio
|
||
|
)
|
||
|
{
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Once the write is done, but the buffer on the done queue
|
||
|
//
|
||
|
QueueInsert (&lpqio->queDone, (LPVOID)pqBuf);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*+ QioInitialize
|
||
|
*
|
||
|
* Open a file for queued sequential io.
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
BOOL WINAPI QioInitialize
|
||
|
(
|
||
|
LPQIO lpqio,
|
||
|
HANDLE hFile,
|
||
|
int nPrio
|
||
|
)
|
||
|
{
|
||
|
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 we fail creating the thread, cleanup and
|
||
|
// return error.
|
||
|
//
|
||
|
if ( ! lpqio->hThread)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*+ QioAdd
|
||
|
*
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*+ QioWait
|
||
|
*
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*+ QioCommit
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
BOOL WINAPI QioCommit
|
||
|
(
|
||
|
LPQIO lpqio
|
||
|
)
|
||
|
{
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/*+ QioShutdown
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
*-========================================================================*/
|
||
|
|
||
|
BOOL WINAPI QioShutdown
|
||
|
(
|
||
|
LPQIO lpqio
|
||
|
)
|
||
|
{
|
||
|
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;
|
||
|
}
|