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.

534 lines
13 KiB

  1. /****************************************************************************
  2. *
  3. * disk32.c
  4. *
  5. * routines do to queued, asynchrous disk I/O in Win32
  6. * NOTE: these routines exist because Chicago does not yet
  7. * support overlapped io.
  8. *
  9. ***************************************************************************/
  10. #include <windows.h>
  11. #include <windowsx.h>
  12. //#include <win32.h>
  13. #define _INC_MMDEBUG_CODE_ TRUE
  14. #include "mmdebug.h" // AuxDebug & assert macros
  15. #include "disk32.h"
  16. #define LockList(pHead) EnterCriticalSection (&pHead->csList)
  17. #define UnlockList(pHead) LeaveCriticalSection (&pHead->csList)
  18. /*+ QueueInitalize
  19. *
  20. * Initalize the queue.
  21. *
  22. *-========================================================================*/
  23. BOOL WINAPI QueueInitialize
  24. (
  25. PQHEAD pHead
  26. )
  27. {
  28. InitializeCriticalSection (&pHead->csList);
  29. if ( ! pHead->hEvtElms)
  30. pHead->hEvtElms = CreateEvent (NULL, TRUE, FALSE, NULL);
  31. // a queue being initalized SHOULD be empty. be sure of it.
  32. //
  33. assert (pHead->qe.pNext == NULL || pHead->qe.pNext == &pHead->qe);
  34. assert (pHead->qe.pPrev == NULL || pHead->qe.pNext == &pHead->qe);
  35. pHead->qe.pPrev = pHead->qe.pNext = &pHead->qe;
  36. return TRUE;
  37. }
  38. /*+ QueueDelete
  39. *
  40. * de-Initalize the queue
  41. *
  42. *-========================================================================*/
  43. BOOL WINAPI QueueDelete
  44. (
  45. PQHEAD pHead
  46. )
  47. {
  48. DeleteCriticalSection (&pHead->csList);
  49. if ( ! pHead->hEvtElms)
  50. {
  51. SetEvent (pHead->hEvtElms); // just in case.
  52. CloseHandle (pHead->hEvtElms);
  53. pHead->hEvtElms = NULL;
  54. }
  55. // a queue being Deleted SHOULD be empty. be sure of it.
  56. //
  57. assert (pHead->qe.pNext == &pHead->qe);
  58. assert (pHead->qe.pPrev == &pHead->qe);
  59. pHead->qe.pPrev = pHead->qe.pNext = &pHead->qe;
  60. return TRUE;
  61. }
  62. /*+ QueueInsert
  63. *
  64. * insert an element in the queue. If a thread is waiting on the queue
  65. * it will be awakened.
  66. *
  67. *-========================================================================*/
  68. VOID WINAPI QueueInsert
  69. (
  70. PQHEAD pHead,
  71. PQELM pqe
  72. )
  73. {
  74. PQELM pqeHead = &pHead->qe;
  75. // this can only happen if the queue has never been initialized
  76. //
  77. assert (pqeHead->pNext != NULL);
  78. assert (pqeHead->pPrev != NULL);
  79. LockList (pHead);
  80. // insert the new element into the list at the tail.
  81. //
  82. pqe->pNext = pqeHead;
  83. pqe->pPrev = pqeHead->pPrev;
  84. pqe->pPrev->pNext = pqe;
  85. pqeHead->pPrev = pqe;
  86. // if the element we just inserted at the tail of the list
  87. // is also at the head of the list. The list must have been
  88. // empty before. In this case we want to signal the Event
  89. // to wake any threads waiting on the queue.
  90. //
  91. if ((pqeHead->pNext == pqe) && pHead->hEvtElms)
  92. SetEvent (pHead->hEvtElms);
  93. UnlockList (pHead);
  94. return;
  95. }
  96. /*+ QueueRemove
  97. *
  98. * remove an element from the queue. if the queue is empty.
  99. * wait for an element to be inserted. A timeout of 0 can be
  100. * used to POLL the queue.
  101. *
  102. *-========================================================================*/
  103. PQELM WINAPI QueueRemove
  104. (
  105. PQHEAD pHead,
  106. DWORD dwTimeout
  107. )
  108. {
  109. PQELM pqe;
  110. LockList (pHead);
  111. // next & prev can only be null when a queue is un-initialized.
  112. //
  113. assert (pHead->qe.pNext != NULL);
  114. assert (pHead->qe.pPrev != NULL);
  115. // if the list is empty and the user specified a non-zero
  116. // timeout and we have a list semaphore available, wait
  117. // for the semaphore to be signalled.
  118. //
  119. pqe = pHead->qe.pNext;
  120. if ((pqe == &pHead->qe) && (dwTimeout != 0) && (pHead->hEvtElms != NULL))
  121. {
  122. // the queue is empty - so make sure that the event has
  123. // not been signalled.
  124. //
  125. ResetEvent (pHead->hEvtElms);
  126. // unlock the list before waiting so that we dont
  127. // deadlock the thread that is inserting things into
  128. // the list.
  129. //
  130. UnlockList (pHead);
  131. AuxDebugEx (3, DEBUGLINE "Waiting (%d) secs on queue %08x\r\n",
  132. dwTimeout, pHead);
  133. WaitForSingleObject (pHead->hEvtElms, dwTimeout);
  134. LockList (pHead);
  135. pqe = pHead->qe.pNext;
  136. }
  137. // if the queue is still empty, set pqe to NULL so that we will
  138. // return null. otherwise remove the head of the queue and return
  139. // it.
  140. //
  141. if (pqe == &pHead->qe)
  142. pqe = NULL;
  143. else
  144. {
  145. // remove the element from the list.
  146. //
  147. pHead->qe.pNext = pqe->pNext;
  148. pqe->pNext->pPrev = pqe->pPrev;
  149. // just to be careful, blank out the
  150. //
  151. pqe->pPrev = pqe->pNext = NULL;
  152. // if the queue is now empty, reset the event
  153. //
  154. if ((pHead->qe.pNext == &pHead->qe) && pHead->hEvtElms)
  155. ResetEvent (pHead->hEvtElms);
  156. }
  157. UnlockList (pHead);
  158. return pqe;
  159. }
  160. #ifdef DEBUG
  161. /*+ QueueDump
  162. *
  163. *-========================================================================*/
  164. void WINAPI QueueDump
  165. (
  166. PQHEAD pHead
  167. )
  168. {
  169. PQELM pqe;
  170. UINT nMax;
  171. LockList (pHead);
  172. AuxDebugEx (2, "Dumping Queue %08X\r\n", pHead);
  173. AuxDebugEx (2, "\telm %08x (next=%08x, prev=%08x)\r\n", &pHead->qe, pHead->qe);
  174. nMax = 10;
  175. pqe = pHead->qe.pNext;
  176. while (nMax && pqe != &pHead->qe)
  177. {
  178. pqe = pqe->pNext;
  179. --nMax;
  180. AuxDebugEx (2, "\telm %08x (next=%08x, prev=%08x)\r\n", pqe, *pqe);
  181. }
  182. UnlockList (pHead);
  183. }
  184. #endif // DEBUG
  185. /*+ SequentialIOThreadProc
  186. *
  187. * thread proc dos sequential writes to a file from buffers queued
  188. * to it.
  189. *
  190. *-========================================================================*/
  191. DWORD WINAPI AsyncIOThreadProc
  192. (
  193. LPQIO lpqio
  194. )
  195. {
  196. // we loop forever. exit from this loop is when
  197. // QueueRemove returns a qiobuf with cb == 0
  198. //
  199. for (;;)
  200. {
  201. PQIOBUF pqBuf;
  202. DWORD dwOff;
  203. // get the next buffer to be written. if we are running
  204. // down, then dont bother to wait for more buffers if
  205. // the queue is empty.
  206. //
  207. pqBuf = (LPVOID)QueueRemove (&lpqio->que, INFINITE);
  208. assert (!pqBuf || !IsBadWritePtr(pqBuf, sizeof(*pqBuf)));
  209. // if we got no buffer back from queue remove, this may be reasonable
  210. // in the case of two threads, just loop back and try again.
  211. //
  212. if ( ! pqBuf )
  213. continue;
  214. // break out of the loop when a -1 buffer pointer is queued
  215. //
  216. if ( pqBuf->lpv == (LPVOID)-1)
  217. {
  218. QueueInsert (&lpqio->queDone, (LPVOID)pqBuf);
  219. break;
  220. }
  221. AuxDebugEx (2, DEBUGLINE "tid %08X %s %X bytes at %08X into %08X\r\n",
  222. lpqio->tid,
  223. pqBuf->bWrite ? "Writing" : "Reading",
  224. pqBuf->cb, pqBuf->dwOffset, pqBuf->lpv);
  225. assert3 (!pqBuf->cb || HIWORD(pqBuf->lpv), "QioThread - invalid buffer %08X", pqBuf->lpv);
  226. if ( HIWORD(pqBuf->lpv) && pqBuf->cb )
  227. {
  228. assert (!IsBadReadPtr(pqBuf->lpv, pqBuf->cb));
  229. dwOff = SetFilePointer (lpqio->hFile, pqBuf->dwOffset, NULL, FILE_BEGIN);
  230. if (dwOff != pqBuf->dwOffset)
  231. {
  232. pqBuf->dwError = GetLastError();
  233. AuxDebug2 ("avifile32 seek error %d", pqBuf->dwError);
  234. }
  235. else
  236. {
  237. if (pqBuf->bWrite)
  238. {
  239. if ( ! WriteFile (lpqio->hFile, pqBuf->lpv, pqBuf->cb,
  240. &pqBuf->cbDone, NULL) ||
  241. (pqBuf->cb != pqBuf->cbDone))
  242. {
  243. pqBuf->dwError = GetLastError();
  244. AuxDebug2 ("avifile32 write error %d", pqBuf->dwError);
  245. }
  246. else
  247. pqBuf->dwError = 0;
  248. }
  249. else
  250. {
  251. if ( ! ReadFile (lpqio->hFile, pqBuf->lpv, pqBuf->cb,
  252. &pqBuf->cbDone, NULL) ||
  253. (pqBuf->cb != pqBuf->cbDone))
  254. {
  255. pqBuf->dwError = GetLastError();
  256. AuxDebug2 ("avifile32 read error %d", pqBuf->dwError);
  257. }
  258. else
  259. pqBuf->dwError = 0;
  260. }
  261. }
  262. }
  263. // Once the write is done, but the buffer on the done queue
  264. //
  265. QueueInsert (&lpqio->queDone, (LPVOID)pqBuf);
  266. }
  267. return 0;
  268. }
  269. /*+ QioInitialize
  270. *
  271. * Open a file for queued sequential io.
  272. *
  273. *-========================================================================*/
  274. BOOL WINAPI QioInitialize
  275. (
  276. LPQIO lpqio,
  277. HANDLE hFile,
  278. int nPrio
  279. )
  280. {
  281. DebugSetOutputLevel (GetProfileInt("debug", "avifil32", 0));
  282. AuxDebugEx (1, DEBUGLINE "QioInitialize (%08x, %d)\r\n",
  283. lpqio, nPrio);
  284. nPrio = max(nPrio, THREAD_PRIORITY_IDLE);
  285. nPrio = min(nPrio, THREAD_PRIORITY_TIME_CRITICAL);
  286. QueueInitialize (&lpqio->que);
  287. QueueInitialize (&lpqio->queDone);
  288. assert ( ! lpqio->hThread);
  289. lpqio->hFile = hFile;
  290. lpqio->nPrio = nPrio;
  291. lpqio->hThread = CreateThread (NULL, 0,
  292. AsyncIOThreadProc,
  293. lpqio,
  294. 0,
  295. &lpqio->tid);
  296. // if we fail creating the thread, cleanup and
  297. // return error.
  298. //
  299. if ( ! lpqio->hThread)
  300. {
  301. AuxDebugEx (1, DEBUGLINE "QioInitialize - CreateThread failed\r\n",
  302. lpqio, nPrio);
  303. QueueDelete (&lpqio->que);
  304. QueueDelete (&lpqio->queDone);
  305. ZeroMemory (lpqio, sizeof(*lpqio));
  306. return FALSE;
  307. }
  308. SetThreadPriority (lpqio->hThread, lpqio->nPrio);
  309. return TRUE;
  310. }
  311. /*+ QioAdd
  312. *
  313. *
  314. *-========================================================================*/
  315. BOOL WINAPI QioAdd
  316. (
  317. LPQIO lpqio,
  318. PQIOBUF pqBuf
  319. )
  320. {
  321. assert (lpqio);
  322. assert (pqBuf);
  323. assert (lpqio->que.qe.pNext != NULL);
  324. assert (lpqio->hThread);
  325. if (!lpqio->hThread)
  326. return FALSE;
  327. // the queue insert/remove code in this function make the
  328. // assumption that the queue pointers are the first element
  329. // of the pqBuf structure.
  330. //
  331. assert ((DWORD)&pqBuf->qe - (DWORD)pqBuf == 0);
  332. // not allowed to queue a buffer with no size or no pointer
  333. //
  334. assert (HIWORD(pqBuf->lpv));
  335. assert (pqBuf->cb);
  336. pqBuf->bPending = TRUE;
  337. QueueInsert (&lpqio->que, (LPVOID)pqBuf);
  338. return TRUE;
  339. }
  340. /*+ QioWait
  341. *
  342. *
  343. *-========================================================================*/
  344. BOOL WINAPI QioWait
  345. (
  346. LPQIO lpqio,
  347. PQIOBUF pqBufWait,
  348. BOOL bWait
  349. )
  350. {
  351. assert (lpqio);
  352. assert (pqBufWait);
  353. assert (lpqio->que.qe.pNext != NULL);
  354. assert (lpqio->hThread);
  355. // the queue insert/remove code in this function make the
  356. // assumption that the queue pointers are the first element
  357. // of the pqBuf structure.
  358. //
  359. assert ((DWORD)&pqBufWait->qe - (DWORD)pqBufWait == 0);
  360. if (pqBufWait->bPending)
  361. {
  362. PQIOBUF pqBufT;
  363. DWORD dwTimeout = bWait ? INFINITE : 0;
  364. do
  365. {
  366. pqBufT = (LPVOID) QueueRemove (&lpqio->queDone, dwTimeout);
  367. AuxDebugEx (4, DEBUGLINE "QioWait(%08X) - removed %08X\r\n", lpqio, pqBufT);
  368. assert (!pqBufT || !IsBadWritePtr(pqBufT, sizeof(*pqBufT)));
  369. if (!pqBufT)
  370. return FALSE;
  371. pqBufT->bPending = FALSE;
  372. } while (pqBufT != pqBufWait);
  373. }
  374. return TRUE;
  375. }
  376. /*+ QioCommit
  377. *
  378. * Waits for the Qio thread to complete any i/o that is in it's queue.
  379. * then causes the qio thread to exit and waits for it to do so.
  380. *
  381. *-========================================================================*/
  382. BOOL WINAPI QioCommit
  383. (
  384. LPQIO lpqio
  385. )
  386. {
  387. QIOBUF qb;
  388. assert (lpqio);
  389. AuxDebugEx (2, DEBUGLINE "QioCommit (%08X)\r\n", lpqio);
  390. // queue up a zero size buffer as a placeholder
  391. // and then wait for the placeholder to be moved
  392. // to the done queue.
  393. //
  394. ZeroMemory (&qb, sizeof(qb));
  395. qb.bPending = TRUE;
  396. QueueInsert (&lpqio->que, (LPVOID)&qb);
  397. return QioWait (lpqio, &qb, TRUE);
  398. }
  399. /*+ QioShutdown
  400. *
  401. * Waits for the Qio thread to complete any i/o that is in it's queue.
  402. * then causes the qio thread to exit and waits for it to do so.
  403. *
  404. *-========================================================================*/
  405. BOOL WINAPI QioShutdown
  406. (
  407. LPQIO lpqio
  408. )
  409. {
  410. QIOBUF qb;
  411. assert (lpqio);
  412. AuxDebugEx (1, DEBUGLINE "QioShutdown (%08X)\r\n", lpqio);
  413. if ( ! lpqio->hThread)
  414. {
  415. AuxDebugEx (1, DEBUGLINE "QioShutdown - nothing to do!\r\n");
  416. return TRUE;
  417. }
  418. assert (lpqio->hThread);
  419. // queue up a zero size buffer to tell the write thread to quit
  420. //
  421. ZeroMemory (&qb, sizeof(qb));
  422. qb.lpv = (LPVOID)-1;
  423. qb.bPending = TRUE;
  424. QueueInsert (&lpqio->que, (LPVOID)&qb);
  425. QioWait (lpqio, &qb, TRUE);
  426. // wait for the thread to shut down.
  427. //
  428. AuxDebugEx (1, DEBUGLINE "Waiting for QIO thread\r\n");
  429. WaitForSingleObject (lpqio->hThread, INFINITE);
  430. AuxDebugEx (1, DEBUGLINE "closeing thread handle\r\n");
  431. CloseHandle (lpqio->hThread), lpqio->hThread = NULL;
  432. //INLINE_BREAK;
  433. // finally, delete the queues
  434. //
  435. QueueDelete (&lpqio->que);
  436. QueueDelete (&lpqio->queDone);
  437. return TRUE;
  438. }