|
|
/*****************************************************************************
* * * CSTREAM.CPP * * * * Copyright (C) Microsoft Corporation 1990-1995 * * All Rights reserved. * * * *****************************************************************************/
#include "stdafx.h"
#include "cstream.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
#ifdef _DEBUG
static LONG hsemReadCount; static LONG hsemStartCount; #define PTR_SEM_READ_COUNT &hsemReadCount
#define PTR_SEM_START_COUNT &hsemStartCount
#else
#define PTR_SEM_READ_COUNT NULL
#define PTR_SEM_START_COUNT NULL
#endif
static HANDLE hsemRead; // semaphore used for dual-cpu processing
static HANDLE hsemStart; // semaphore used for dual-cpu processing
static CStream *pThis; static BOOL fReadAheadStarted; static BOOL fExitThread; static PBYTE pbuf1; // first buffer for dual-cpu processing
static PBYTE pbuf2; // second buffer for dual-cpu processing
/////////////////////// CStream implementation ////////////////////////////
// We use our own stream class instead of using the C runtime, because this
// change alone doubled the speed of the help compiler. I.e., the C runtime
// implementation of stream io is horribly slow. 26-Feb-1994 [ralphw]
CStream::CStream(PCSTR pszFileName) { // _fDualCPU = FALSE;
// Only one CStream can use the read-ahead thread
if (!pThis) { fDualCPU = _fDualCPU; pThis = this; } else fDualCPU = FALSE;
if ((hfile = _lopen(pszFileName, OF_READ)) == HFILE_ERROR) { VReportError(HCERR_CANNOT_OPEN, &errHpj, pszFileName); fInitialized = FALSE; return; } if (fDualCPU) { cbBuf = DUAL_INPUT_BUF_SIZE; if (!hsemRead) { hsemRead = CreateSemaphore(NULL, 1, 1, NULL); hsemStart = CreateSemaphore(NULL, 1, 1, NULL); pbuf2 = (PBYTE) lcMalloc(DUAL_INPUT_BUF_SIZE + 1); pbuf1 = (PBYTE) lcMalloc(DUAL_INPUT_BUF_SIZE + 1); } pbuf = pbuf1; } else { cbBuf = INPUT_BUF_SIZE; pbuf = (PBYTE) lcMalloc(INPUT_BUF_SIZE + 1); }
ConfirmOrDie(pbuf); fInitialized = TRUE;
int cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { _lclose(hfile); VReportError(HCERR_CANT_READ, &errHpj, pszFileName); fInitialized = FALSE; return; }
if (fDualCPU) { // Start reading the next buffer
cThrdRead = HFILE_NOTREAD; if (!fReadAheadStarted) { hthrd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &ReadAhead, NULL, 0, &idThrd); ConfirmOrDie(hthrd); fReadAheadStarted = TRUE; } else { ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
} }
pCurBuf = pbuf; pEndBuf = pbuf + cread; pEndBuf[1] = '\0'; lFilePos = cread; lFileBuf = 0; pszFile = lcStrDup(pszFileName); fInitialized = TRUE; }
CStream::~CStream() { if (fInitialized) { if (fDualCPU) { WaitForReadAhead(); } else lcFree(pbuf); lcFree(pszFile); _lclose(hfile); } pThis = NULL; }
/***************************************************************************
FUNCTION: CStream::ReadBuf
PURPOSE: Read the next block into buffer
PARAMETERS: void
RETURNS:
COMMENTS: Zero-terminates the buffer
MODIFICATION DATES: 13-Nov-1994 [ralphw]
***************************************************************************/
char CStream::ReadBuf(void) { int cread;
if (fDualCPU) { ASSERT(fReadAheadStarted); WaitForReadAhead(); if (pbuf == pbuf1) pbuf = pbuf2; else pbuf = pbuf1;
cread = cThrdRead; cThrdRead = HFILE_NOTREAD; ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT);
// Error-checking must occur AFTER we release the read semaphore
if (cread == HFILE_ERROR) { VReportError(HCERR_READ_FAILURE, &errHpj, pszFile); return chEOF; } ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
} else { cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { VReportError(HCERR_READ_FAILURE, &errHpj, pszFile); return chEOF; } }
lFileBuf = lFilePos; lFilePos += cread;
pCurBuf = pbuf; pEndBuf = pbuf + cread; pEndBuf[1] = '\0';
return (char) *pCurBuf++; }
BOOL STDCALL CStream::read(LPBYTE pbDest, int cbBytes) { while (cbBytes--) *pbDest++ = (BYTE) cget(); return (pbDest[-1] == chEOF) ? FALSE : TRUE; }
int STDCALL CStream::seek(int pos, SEEK_TYPE seek) { ConfirmOrDie(seek != SK_END); // we don't support this one
if (seek == SK_CUR) pos = lFileBuf + (pCurBuf - pbuf) + pos;
if (pos >= lFileBuf && pos < lFilePos) { pCurBuf = pbuf + (pos - lFileBuf); return lFileBuf + (pCurBuf - pbuf); } else { if (fDualCPU) { WaitForReadAhead(); } lFileBuf = _llseek(hfile, pos, SEEK_SET); int cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { VReportError(HCERR_READ_FAILURE, &errHpj, pszFile); return chEOF; } lFilePos = lFileBuf + cread; pCurBuf = pbuf; pEndBuf = pbuf + cread; if (fDualCPU) { cThrdRead = HFILE_NOTREAD; ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); if (fReadAheadStarted) ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
} return lFilePos; } }
void CStream::WaitForReadAhead(void) { for(;;) { WaitForSingleObject(hsemRead, INFINITE); if (cThrdRead == HFILE_NOTREAD) { ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); Sleep(1); // give read-ahead thread a chance to run
} else return; } }
/***************************************************************************
FUNCTION: ReadAhead
PURPOSE: Reads the next block of data from a file
PARAMETERS: pthis
RETURNS:
COMMENTS: Two semaphores control this thread: hsemStart: keeps the thread suspended while waiting for the caller to finish reading one of its blocks. hsemRead: used as a signal between the main thread and this thread as to when this thread has completed.
Because it is theoretically possible for a thread switch to occur between the time the hsemStart thread starts this thread and this thread's call to WaitForSingleObject(hsemRead), the caller also sets the read return value to HFILE_NOTREAD to ensure that it does not attempt to use this buffer until in fact the read has completed.
NOTE: this thread approach only makes sense on a system with more then one CPU.
MODIFICATION DATES: 05-Feb-1995 [ralphw]
***************************************************************************/
DWORD WINAPI ReadAhead(LPVOID pv) { /*
* Each time through the loop, we block on hsemStart, waiting for our * caller to release it. The hsemRead is used to block the caller until * we have completed our read. */
for (;;) { PBYTE pbReadBuf; WaitForSingleObject(hsemStart, INFINITE); if (fExitThread) break; WaitForSingleObject(hsemRead, INFINITE); pbReadBuf = (pThis->pbuf == pbuf1) ? pbuf2 : pbuf1; pThis->cThrdRead = _lread(pThis->hfile, pbReadBuf, pThis->cbBuf); ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT); } ExitThread(0); return 0; }
/***************************************************************************
FUNCTION: CStream::Cleanup
PURPOSE: Cleanup global variables
PARAMETERS: void
RETURNS:
COMMENTS:
MODIFICATION DATES: 05-Feb-1995 [ralphw]
***************************************************************************/
void CStream::Cleanup(void) { if (fReadAheadStarted) { fExitThread = TRUE; ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
Sleep(1); // Let the other thread run
CloseHandle(hsemStart); CloseHandle(hsemRead); lcFree(pbuf1); lcFree(pbuf2); hsemStart = hsemRead = pbuf1 = pbuf2 = NULL; } }
#ifdef _DEBUG
char CStream::cget() { if (pCurBuf < pEndBuf) return (char) *pCurBuf++; else if (pEndBuf < pbuf + cbBuf) return chEOF; else return ReadBuf(); }
#endif
|