Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

389 lines
8.6 KiB

  1. /*****************************************************************************
  2. * *
  3. * CSTREAM.CPP *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1990-1997 *
  6. * All Rights reserved. *
  7. * *
  8. *****************************************************************************/
  9. #include "header.h"
  10. #include "cstream.h"
  11. #ifdef _DEBUG
  12. #undef THIS_FILE
  13. static char THIS_FILE[] = __FILE__;
  14. #endif
  15. #ifdef _DEBUG
  16. static LONG hsemReadCount;
  17. static LONG hsemStartCount;
  18. #define PTR_SEM_READ_COUNT &hsemReadCount
  19. #define PTR_SEM_START_COUNT &hsemStartCount
  20. #else
  21. #define PTR_SEM_READ_COUNT NULL
  22. #define PTR_SEM_START_COUNT NULL
  23. #endif
  24. #ifndef SEEK_CUR
  25. #define SEEK_CUR 1
  26. #define SEEK_END 2
  27. #define SEEK_SET 0
  28. #endif
  29. static HANDLE hsemRead; // semaphore used for dual-cpu processing
  30. static HANDLE hsemStart; // semaphore used for dual-cpu processing
  31. static CStream *pThis;
  32. static BOOL fReadAheadStarted;
  33. static BOOL fExitThread;
  34. static PBYTE pbuf1; // first buffer for dual-cpu processing
  35. static PBYTE pbuf2; // second buffer for dual-cpu processing
  36. /////////////////////// CStream implementation ////////////////////////////
  37. // We use our own stream class instead of using the C runtime, because this
  38. // change alone doubled the speed of the help compiler. I.e., the C runtime
  39. // implementation of stream io is horribly slow. 26-Feb-1994 [ralphw]
  40. // Also, this CStream will take advantage of a dual-CPU and will do
  41. // read-aheads in a thread.
  42. CStream::CStream(PCSTR pszFileName)
  43. {
  44. // Only one CStream can use the read-ahead thread
  45. if (!pThis) {
  46. fDualCPU = g_fDualCPU==-1?FALSE:g_fDualCPU;
  47. pThis = this;
  48. }
  49. else
  50. fDualCPU = FALSE;
  51. if ((hfile = _lopen(pszFileName, OF_READ)) == HFILE_ERROR) {
  52. fInitialized = FALSE;
  53. if (fDualCPU)
  54. pThis = NULL;
  55. return;
  56. }
  57. if (fDualCPU) {
  58. cbBuf = DUAL_CSTREAM_BUF_SIZE;
  59. if (!hsemRead) {
  60. hsemRead = CreateSemaphore(NULL, 1, 1, NULL);
  61. hsemStart = CreateSemaphore(NULL, 1, 1, NULL);
  62. pbuf2 = (PBYTE) lcMalloc(DUAL_CSTREAM_BUF_SIZE + 2);
  63. pbuf1 = (PBYTE) lcMalloc(DUAL_CSTREAM_BUF_SIZE + 2);
  64. }
  65. pbuf = pbuf1;
  66. }
  67. else {
  68. cbBuf = CSTREAM_BUF_SIZE;
  69. // +2 because we add a zero just past the buffer in case anyone expects strings
  70. pbuf = (PBYTE) lcMalloc(CSTREAM_BUF_SIZE + 2);
  71. }
  72. ASSERT(pbuf);
  73. fInitialized = TRUE;
  74. int cread = _lread(hfile, pbuf, cbBuf);
  75. if (cread == HFILE_ERROR) {
  76. _lclose(hfile);
  77. fInitialized = FALSE;
  78. if (fDualCPU)
  79. pThis = NULL;
  80. return;
  81. }
  82. if (fDualCPU) {
  83. // Start reading the next buffer
  84. cThrdRead = HFILE_NOTREAD;
  85. if (!fReadAheadStarted) {
  86. hthrd = CreateThread(NULL, 0,
  87. (LPTHREAD_START_ROUTINE) &ReadAhead, NULL,
  88. 0, &idThrd);
  89. ASSERT(hthrd);
  90. if (!hthrd)
  91. fDualCPU = FALSE;
  92. else
  93. fReadAheadStarted = TRUE;
  94. }
  95. else {
  96. ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT);
  97. ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
  98. }
  99. }
  100. pCurBuf = pbuf;
  101. pEndBuf = pbuf + cread;
  102. pEndBuf[1] = '\0';
  103. lFilePos = cread;
  104. lFileBuf = 0;
  105. pszFile = lcStrDup(pszFileName);
  106. fInitialized = TRUE;
  107. m_fEndOfFile = FALSE;
  108. }
  109. CStream::~CStream()
  110. {
  111. if (fInitialized) {
  112. if (fDualCPU) {
  113. Cleanup();
  114. WaitForReadAhead();
  115. pThis = NULL;
  116. }
  117. else
  118. {
  119. lcFree(pbuf);
  120. }
  121. lcFree(pszFile);
  122. _lclose(hfile);
  123. }
  124. }
  125. /***************************************************************************
  126. FUNCTION: CStream::ReadBuf
  127. PURPOSE: Read the next block into buffer
  128. PARAMETERS:
  129. void
  130. RETURNS:
  131. COMMENTS:
  132. Zero-terminates the buffer
  133. MODIFICATION DATES:
  134. 13-Nov-1994 [ralphw]
  135. ***************************************************************************/
  136. char CStream::ReadBuf(void)
  137. {
  138. int cread;
  139. if (fDualCPU) {
  140. ASSERT(fReadAheadStarted);
  141. WaitForReadAhead();
  142. if (pbuf == pbuf1)
  143. pbuf = pbuf2;
  144. else
  145. pbuf = pbuf1;
  146. cread = cThrdRead;
  147. cThrdRead = HFILE_NOTREAD;
  148. ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT);
  149. // Error-checking must occur AFTER we release the read semaphore
  150. if (cread == HFILE_ERROR) {
  151. return chEOF;
  152. }
  153. ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
  154. }
  155. else {
  156. cread = _lread(hfile, pbuf, cbBuf);
  157. if (cread == HFILE_ERROR) {
  158. return chEOF;
  159. }
  160. }
  161. lFileBuf = lFilePos;
  162. lFilePos += cread;
  163. pCurBuf = pbuf;
  164. pEndBuf = pbuf + cread;
  165. pEndBuf[1] = '\0';
  166. if (!cread) {
  167. m_fEndOfFile = TRUE;
  168. return chEOF;
  169. }
  170. return (char) *pCurBuf++;
  171. }
  172. UINT STDCALL CStream::read(void* pbDest, int cbBytes)
  173. {
  174. if (pEndBuf - pbuf < cbBuf)
  175. if (pEndBuf - pCurBuf < cbBytes)
  176. cbBytes = (int)(pEndBuf - pCurBuf);
  177. if (pCurBuf + cbBytes < pEndBuf) {
  178. memcpy(pbDest, pCurBuf, cbBytes);
  179. pCurBuf += cbBytes;
  180. return cbBytes;
  181. }
  182. PBYTE pbDst = (PBYTE) pbDest;
  183. // If destination buffer is larger then our internal buffer, then
  184. // recursively call until we have filled up the destination.
  185. int cbRead = (int)(pEndBuf - pCurBuf);
  186. memcpy(pbDest, pCurBuf, cbRead);
  187. pbDst += cbRead;
  188. ReadBuf();
  189. if (m_fEndOfFile)
  190. return cbRead;
  191. else
  192. pCurBuf--; // since ReadBuf incremented it
  193. return read(pbDst, cbBytes - cbRead) + cbRead;
  194. }
  195. int STDCALL CStream::seek(int pos, SEEK_TYPE seek)
  196. {
  197. ASSERT(seek != SK_END); // we don't support this one
  198. if (seek == SK_CUR)
  199. pos = lFileBuf + (int)(pCurBuf - pbuf) + pos;
  200. if (pos >= lFileBuf && pos < lFilePos) {
  201. pCurBuf = pbuf + (pos - lFileBuf);
  202. if (pCurBuf >= pEndBuf && pEndBuf < pbuf + cbBuf)
  203. m_fEndOfFile = TRUE;
  204. return lFileBuf + (int)(pCurBuf - pbuf);
  205. }
  206. else {
  207. if (fDualCPU) {
  208. WaitForReadAhead();
  209. }
  210. lFileBuf = _llseek(hfile, pos, SEEK_SET);
  211. int cread = _lread(hfile, pbuf, cbBuf);
  212. if (cread == HFILE_ERROR) {
  213. m_fEndOfFile = TRUE;
  214. return chEOF;
  215. }
  216. lFilePos = lFileBuf + cread;
  217. pCurBuf = pbuf;
  218. pEndBuf = pbuf + cread;
  219. if (fDualCPU) {
  220. cThrdRead = HFILE_NOTREAD;
  221. ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT);
  222. if (fReadAheadStarted)
  223. ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
  224. }
  225. if (cread == 0)
  226. m_fEndOfFile = TRUE;
  227. return lFilePos;
  228. }
  229. }
  230. void CStream::WaitForReadAhead(void)
  231. {
  232. for(;;) {
  233. WaitForSingleObject(hsemRead, INFINITE);
  234. if (cThrdRead == HFILE_NOTREAD) {
  235. ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT);
  236. Sleep(1); // give read-ahead thread a chance to run
  237. }
  238. else
  239. return;
  240. }
  241. }
  242. /***************************************************************************
  243. FUNCTION: ReadAhead
  244. PURPOSE: Reads the next block of data from a file
  245. PARAMETERS:
  246. pthis
  247. RETURNS:
  248. COMMENTS:
  249. Two semaphores control this thread:
  250. hsemStart: keeps the thread suspended while waiting for the
  251. caller to finish reading one of its blocks.
  252. hsemRead: used as a signal between the main thread and this
  253. thread as to when this thread has completed.
  254. Because it is theoretically possible for a thread switch to occur
  255. between the time the hsemStart thread starts this thread and this
  256. thread's call to WaitForSingleObject(hsemRead), the caller also sets
  257. the read return value to HFILE_NOTREAD to ensure that it does not
  258. attempt to use this buffer until in fact the read has completed.
  259. NOTE: this thread approach only makes sense on a system with more
  260. then one CPU.
  261. MODIFICATION DATES:
  262. 05-Feb-1997 [ralphw]
  263. ***************************************************************************/
  264. DWORD WINAPI ReadAhead(LPVOID pv)
  265. {
  266. /*
  267. * Each time through the loop, we block on hsemStart, waiting for our
  268. * caller to release it. The hsemRead is used to block the caller until
  269. * we have completed our read.
  270. */
  271. for (;;) {
  272. PBYTE pbReadBuf;
  273. WaitForSingleObject(hsemStart, INFINITE);
  274. if (fExitThread)
  275. break;
  276. WaitForSingleObject(hsemRead, INFINITE);
  277. pbReadBuf = (pThis->pbuf == pbuf1) ? pbuf2 : pbuf1;
  278. pThis->cThrdRead = _lread(pThis->hfile, pbReadBuf, pThis->cbBuf);
  279. ReleaseSemaphore(hsemRead, 1, PTR_SEM_READ_COUNT);
  280. }
  281. ExitThread(0);
  282. return 0;
  283. }
  284. /***************************************************************************
  285. FUNCTION: CStream::Cleanup
  286. PURPOSE: Cleanup global variables
  287. PARAMETERS:
  288. void
  289. RETURNS:
  290. COMMENTS:
  291. MODIFICATION DATES:
  292. 05-Feb-1997 [ralphw]
  293. ***************************************************************************/
  294. void CStream::Cleanup(void)
  295. {
  296. if (fReadAheadStarted) {
  297. fExitThread = TRUE;
  298. ReleaseSemaphore(hsemStart, 1, PTR_SEM_START_COUNT); // start read-ahead thread
  299. Sleep(1); // Let the other thread run
  300. CloseHandle(hsemStart);
  301. CloseHandle(hsemRead);
  302. lcFree(pbuf1);
  303. lcFree(pbuf2);
  304. hsemStart = hsemRead = pbuf1 = pbuf2 = NULL;
  305. }
  306. }
  307. #ifdef _DEBUG
  308. char CStream::cget()
  309. {
  310. if (pCurBuf < pEndBuf)
  311. return (char) *pCurBuf++;
  312. else if (pEndBuf < pbuf + cbBuf) {
  313. m_fEndOfFile = TRUE;
  314. return chEOF;
  315. }
  316. else
  317. return ReadBuf();
  318. }
  319. #endif