|
|
/*****************************************************************************
* * * CSTREAM.CPP * * * * Copyright (C) Microsoft Corporation 1990-1997 * * All Rights reserved. * * * *****************************************************************************/
#include "header.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
#ifndef SEEK_CUR
#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_SET 0
#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]
// Also, this CStream will take advantage of a dual-CPU and will do
// read-aheads in a thread.
CStream::CStream(PCSTR pszFileName) { // Only one CStream can use the read-ahead thread
if (!pThis) { fDualCPU = g_fDualCPU==-1?FALSE:g_fDualCPU; pThis = this; } else fDualCPU = FALSE;
if ((hfile = _lopen(pszFileName, OF_READ)) == HFILE_ERROR) { fInitialized = FALSE; if (fDualCPU) pThis = NULL; return; } if (fDualCPU) { cbBuf = DUAL_CSTREAM_BUF_SIZE; if (!hsemRead) { hsemRead = CreateSemaphore(NULL, 1, 1, NULL); hsemStart = CreateSemaphore(NULL, 1, 1, NULL); pbuf2 = (PBYTE) lcMalloc(DUAL_CSTREAM_BUF_SIZE + 2); pbuf1 = (PBYTE) lcMalloc(DUAL_CSTREAM_BUF_SIZE + 2); } pbuf = pbuf1; } else { cbBuf = CSTREAM_BUF_SIZE;
// +2 because we add a zero just past the buffer in case anyone expects strings
pbuf = (PBYTE) lcMalloc(CSTREAM_BUF_SIZE + 2); }
ASSERT(pbuf); fInitialized = TRUE;
int cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { _lclose(hfile); fInitialized = FALSE; if (fDualCPU) pThis = NULL; return; }
if (fDualCPU) { // Start reading the next buffer
cThrdRead = HFILE_NOTREAD; if (!fReadAheadStarted) { hthrd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &ReadAhead, NULL, 0, &idThrd); ASSERT(hthrd); if (!hthrd) fDualCPU = FALSE; else 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; m_fEndOfFile = FALSE; }
CStream::~CStream() { if (fInitialized) { if (fDualCPU) { Cleanup(); WaitForReadAhead(); pThis = NULL; } else { lcFree(pbuf); } lcFree(pszFile); _lclose(hfile); } }
/***************************************************************************
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) { return chEOF; } ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
} else { cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { return chEOF; } }
lFileBuf = lFilePos; lFilePos += cread;
pCurBuf = pbuf; pEndBuf = pbuf + cread; pEndBuf[1] = '\0';
if (!cread) { m_fEndOfFile = TRUE; return chEOF; } return (char) *pCurBuf++; }
UINT STDCALL CStream::read(void* pbDest, int cbBytes) { if (pEndBuf - pbuf < cbBuf) if (pEndBuf - pCurBuf < cbBytes) cbBytes = (int)(pEndBuf - pCurBuf);
if (pCurBuf + cbBytes < pEndBuf) { memcpy(pbDest, pCurBuf, cbBytes); pCurBuf += cbBytes; return cbBytes; } PBYTE pbDst = (PBYTE) pbDest;
// If destination buffer is larger then our internal buffer, then
// recursively call until we have filled up the destination.
int cbRead = (int)(pEndBuf - pCurBuf); memcpy(pbDest, pCurBuf, cbRead); pbDst += cbRead; ReadBuf(); if (m_fEndOfFile) return cbRead; else pCurBuf--; // since ReadBuf incremented it
return read(pbDst, cbBytes - cbRead) + cbRead; }
int STDCALL CStream::seek(int pos, SEEK_TYPE seek) { ASSERT(seek != SK_END); // we don't support this one
if (seek == SK_CUR) pos = lFileBuf + (int)(pCurBuf - pbuf) + pos;
if (pos >= lFileBuf && pos < lFilePos) { pCurBuf = pbuf + (pos - lFileBuf); if (pCurBuf >= pEndBuf && pEndBuf < pbuf + cbBuf) m_fEndOfFile = TRUE; return lFileBuf + (int)(pCurBuf - pbuf); } else { if (fDualCPU) { WaitForReadAhead(); } lFileBuf = _llseek(hfile, pos, SEEK_SET); int cread = _lread(hfile, pbuf, cbBuf); if (cread == HFILE_ERROR) { m_fEndOfFile = TRUE; 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
} if (cread == 0) m_fEndOfFile = TRUE;
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-1997 [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-1997 [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) { m_fEndOfFile = TRUE; return chEOF; } else return ReadBuf(); }
#endif
|