Leaked source code of windows server 2003
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.

573 lines
17 KiB

  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: io.cpp
  6. *
  7. * Content: DirectPlay Thread Pool I/O functions.
  8. *
  9. * History:
  10. * Date By Reason
  11. * ======== ======== =========
  12. * 10/31/01 VanceO Created.
  13. *
  14. ******************************************************************************/
  15. #include "dpnthreadpooli.h"
  16. // Overlapped I/O is not supported on Windows CE.
  17. #ifndef WINCE
  18. //=============================================================================
  19. // Macros
  20. //=============================================================================
  21. #if ((defined(_XBOX)) && (! defined(XBOX_ON_DESKTOP)))
  22. #define DNHasOverlappedIoCompleted(pOverlapped) ((pOverlapped)->OffsetHigh != HRESULT_FROM_WIN32(ERROR_IO_PENDING))
  23. #else // ! _XBOX or XBOX_ON_DESKTOP
  24. #define DNHasOverlappedIoCompleted(pOverlapped) HasOverlappedIoCompleted(pOverlapped)
  25. #endif // ! _XBOX or XBOX_ON_DESKTOP
  26. //=============================================================================
  27. // Globals
  28. //=============================================================================
  29. CFixedPool g_TrackedFilePool;
  30. #undef DPF_MODNAME
  31. #define DPF_MODNAME "InitializeWorkQueueIoInfo"
  32. //=============================================================================
  33. // InitializeWorkQueueIoInfo
  34. //-----------------------------------------------------------------------------
  35. //
  36. // Description: Initializes the I/O info for the given work queue.
  37. //
  38. // Arguments:
  39. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to initialize.
  40. //
  41. // Returns: HRESULT
  42. // DPN_OK - Successfully initialized the work queue object's
  43. // I/O information.
  44. // DPNERR_OUTOFMEMORY - Failed to allocate memory while initializing.
  45. //=============================================================================
  46. HRESULT InitializeWorkQueueIoInfo(DPTPWORKQUEUE * const pWorkQueue)
  47. {
  48. HRESULT hr = DPN_OK;
  49. DNInitializeSListHead(&pWorkQueue->SlistOutstandingIO);
  50. pWorkQueue->blTrackedFiles.Initialize();
  51. return hr;
  52. } // InitializeWorkQueueIoInfo
  53. #undef DPF_MODNAME
  54. #define DPF_MODNAME "DeinitializeWorkQueueIoInfo"
  55. //=============================================================================
  56. // DeinitializeWorkQueueIoInfo
  57. //-----------------------------------------------------------------------------
  58. //
  59. // Description: Cleans up work queue I/O info.
  60. //
  61. // Arguments:
  62. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to initialize.
  63. //
  64. // Returns: Nothing.
  65. //=============================================================================
  66. void DeinitializeWorkQueueIoInfo(DPTPWORKQUEUE * const pWorkQueue)
  67. {
  68. DNASSERT(DNInterlockedFlushSList(&pWorkQueue->SlistOutstandingIO) == NULL);
  69. DNASSERT(pWorkQueue->blTrackedFiles.IsEmpty());
  70. } // DeinitializeWorkQueueIoInfo
  71. #undef DPF_MODNAME
  72. #define DPF_MODNAME "StartTrackingFileIo"
  73. //=============================================================================
  74. // StartTrackingFileIo
  75. //-----------------------------------------------------------------------------
  76. //
  77. // Description: Starts tracking overlapped I/O for a given file handle on
  78. // the specified work queue. The handle is not duplicated
  79. // and it should remain valid until StopTrackingFileIo is called.
  80. //
  81. // Arguments:
  82. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
  83. // HANDLE hFile - Handle of file to track.
  84. //
  85. // Returns: HRESULT
  86. // DPN_OK - Starting tracking for the file was successful.
  87. // DPNERR_ALREADYREGISTERED - The specified file handle is already being
  88. // tracked.
  89. // DPNERR_OUTOFMEMORY - Not enough memory to track the file.
  90. //=============================================================================
  91. HRESULT StartTrackingFileIo(DPTPWORKQUEUE * const pWorkQueue,
  92. const HANDLE hFile)
  93. {
  94. HRESULT hr;
  95. CTrackedFile * pTrackedFile;
  96. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  97. HANDLE hIoCompletionPort;
  98. #endif // DPNBUILD_USEIOCOMPLETIONPORTS
  99. #ifdef DBG
  100. CBilink * pBilink;
  101. CTrackedFile * pTrackedFileTemp;
  102. #endif // DBG
  103. //
  104. // Get a tracking container from the pool.
  105. //
  106. pTrackedFile = (CTrackedFile*) g_TrackedFilePool.Get(pWorkQueue);
  107. if (pTrackedFile == NULL)
  108. {
  109. DPFX(DPFPREP, 0, "Couldn't get item for tracking file 0x%p!",
  110. hFile);
  111. hr = DPNERR_OUTOFMEMORY;
  112. goto Failure;
  113. }
  114. pTrackedFile->m_hFile = MAKE_DNHANDLE(hFile);
  115. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  116. //
  117. // Associate the file with the I/O completion port.
  118. //
  119. hIoCompletionPort = CreateIoCompletionPort(hFile,
  120. HANDLE_FROM_DNHANDLE(pWorkQueue->hIoCompletionPort),
  121. 0,
  122. 1);
  123. if (hIoCompletionPort != HANDLE_FROM_DNHANDLE(pWorkQueue->hIoCompletionPort))
  124. {
  125. #ifdef DBG
  126. DWORD dwError;
  127. dwError = GetLastError();
  128. DPFX(DPFPREP, 0, "Couldn't associate file 0x%p with I/O completion port 0x%p (err = %u)!",
  129. hFile, pWorkQueue->hIoCompletionPort, dwError);
  130. #endif // DBG
  131. #pragma BUGBUG(vanceo, "Can't fail because of our hack")
  132. //hr = DPNERR_GENERIC;
  133. //goto Failure;
  134. }
  135. #endif // DPNBUILD_USEIOCOMPLETIONPORTS
  136. DPFX(DPFPREP, 7, "Work queue 0x%p starting to tracking I/O for file 0x%p using object 0x%p.",
  137. pWorkQueue, hFile, pTrackedFile);
  138. //
  139. // Add the item to the list.
  140. //
  141. DNEnterCriticalSection(&pWorkQueue->csListLock);
  142. #ifdef DBG
  143. //
  144. // Assert that the handle isn't already being tracked.
  145. //
  146. pBilink = pWorkQueue->blTrackedFiles.GetNext();
  147. while (pBilink != &pWorkQueue->blTrackedFiles)
  148. {
  149. pTrackedFileTemp = CONTAINING_OBJECT(pBilink, CTrackedFile, m_blList);
  150. DNASSERT(pTrackedFileTemp->IsValid());
  151. DNASSERT(HANDLE_FROM_DNHANDLE(pTrackedFileTemp->m_hFile) != hFile);
  152. pBilink = pBilink->GetNext();
  153. }
  154. #endif // DBG
  155. pTrackedFile->m_blList.InsertBefore(&pWorkQueue->blTrackedFiles);
  156. DNLeaveCriticalSection(&pWorkQueue->csListLock);
  157. hr = DPN_OK;
  158. Exit:
  159. return hr;
  160. Failure:
  161. if (pTrackedFile != NULL)
  162. {
  163. g_TrackedFilePool.Release(pTrackedFile);
  164. pTrackedFile = NULL;
  165. }
  166. goto Exit;
  167. } // StartTrackingFileIo
  168. #undef DPF_MODNAME
  169. #define DPF_MODNAME "StopTrackingFileIo"
  170. //=============================================================================
  171. // StopTrackingFileIo
  172. //-----------------------------------------------------------------------------
  173. //
  174. // Description: Stops tracking overlapped I/O for a given file handle on
  175. // the specified work queue.
  176. //
  177. // Arguments:
  178. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
  179. // HANDLE hFile - Handle of file to stop tracking.
  180. //
  181. // Returns: HRESULT
  182. // DPN_OK - Stopping tracking for the file was successful.
  183. // DPNERR_INVALIDHANDLE - File handle was not being tracked.
  184. //=============================================================================
  185. HRESULT StopTrackingFileIo(DPTPWORKQUEUE * const pWorkQueue,
  186. const HANDLE hFile)
  187. {
  188. HRESULT hr = DPNERR_INVALIDHANDLE;
  189. CBilink * pBilink;
  190. CTrackedFile * pTrackedFile;
  191. DNEnterCriticalSection(&pWorkQueue->csListLock);
  192. pBilink = pWorkQueue->blTrackedFiles.GetNext();
  193. while (pBilink != &pWorkQueue->blTrackedFiles)
  194. {
  195. pTrackedFile = CONTAINING_OBJECT(pBilink, CTrackedFile, m_blList);
  196. DNASSERT(pTrackedFile->IsValid());
  197. pBilink = pBilink->GetNext();
  198. if (HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile) == hFile)
  199. {
  200. DPFX(DPFPREP, 7, "Work queue 0x%p no longer tracking I/O for file 0x%p under object 0x%p.",
  201. pWorkQueue, hFile, pTrackedFile);
  202. REMOVE_DNHANDLE(pTrackedFile->m_hFile);
  203. pTrackedFile->m_blList.RemoveFromList();
  204. g_TrackedFilePool.Release(pTrackedFile);
  205. pTrackedFile = NULL;
  206. hr = DPN_OK;
  207. break;
  208. }
  209. }
  210. DNLeaveCriticalSection(&pWorkQueue->csListLock);
  211. return hr;
  212. } // StopTrackingFileIo
  213. #undef DPF_MODNAME
  214. #define DPF_MODNAME "CancelIoForThisThread"
  215. //=============================================================================
  216. // CancelIoForThisThread
  217. //-----------------------------------------------------------------------------
  218. //
  219. // Description: Cancels asynchronous I/O operations submitted by this thread
  220. // for all tracked files.
  221. //
  222. // Arguments:
  223. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object owning this
  224. // thread.
  225. //
  226. // Returns: None.
  227. //=============================================================================
  228. void CancelIoForThisThread(DPTPWORKQUEUE * const pWorkQueue)
  229. {
  230. CBilink * pBilink;
  231. CTrackedFile * pTrackedFile;
  232. BOOL fResult;
  233. DNEnterCriticalSection(&pWorkQueue->csListLock);
  234. pBilink = pWorkQueue->blTrackedFiles.GetNext();
  235. while (pBilink != &pWorkQueue->blTrackedFiles)
  236. {
  237. pTrackedFile = CONTAINING_OBJECT(pBilink, CTrackedFile, m_blList);
  238. DNASSERT(pTrackedFile->IsValid());
  239. DPFX(DPFPREP, 3, "Cancelling file 0x%p I/O for this thread (queue = 0x%p).",
  240. HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile), pWorkQueue);
  241. fResult = CancelIo(HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile));
  242. if (! fResult)
  243. {
  244. #ifdef DBG
  245. DWORD dwError;
  246. dwError = GetLastError();
  247. DPFX(DPFPREP, 0, "Couldn't cancel file 0x%p I/O for this thread (err = %u)!",
  248. HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile), dwError);
  249. #endif // DBG
  250. //
  251. // Continue...
  252. //
  253. }
  254. pBilink = pBilink->GetNext();
  255. }
  256. DNLeaveCriticalSection(&pWorkQueue->csListLock);
  257. } // CancelIoForThisThread
  258. #undef DPF_MODNAME
  259. #define DPF_MODNAME "CreateOverlappedIoWorkItem"
  260. //=============================================================================
  261. // CreateOverlappedIoWorkItem
  262. //-----------------------------------------------------------------------------
  263. //
  264. // Description: Creates a new asynchronous I/O operation work item for the
  265. // work queue.
  266. //
  267. // Arguments:
  268. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
  269. // PFNDPTNWORKCALLBACK pfnWorkCallback - Callback to execute when operation
  270. // completes.
  271. // PVOID pvCallbackContext - User specified context to pass to
  272. // callback.
  273. //
  274. // Returns: Pointer to work item, or NULL if couldn't allocate memory.
  275. //=============================================================================
  276. CWorkItem * CreateOverlappedIoWorkItem(DPTPWORKQUEUE * const pWorkQueue,
  277. const PFNDPTNWORKCALLBACK pfnWorkCallback,
  278. PVOID const pvCallbackContext)
  279. {
  280. CWorkItem * pWorkItem;
  281. //
  282. // Get an entry from the pool.
  283. //
  284. pWorkItem = (CWorkItem*) pWorkQueue->pWorkItemPool->Get(pWorkQueue);
  285. if (pWorkItem != NULL)
  286. {
  287. //
  288. // Initialize the work item.
  289. //
  290. pWorkItem->m_pfnWorkCallback = pfnWorkCallback;
  291. pWorkItem->m_pvCallbackContext = pvCallbackContext;
  292. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  293. pWorkItem->m_Overlapped.hEvent = NULL;
  294. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  295. pWorkItem->m_Overlapped.hEvent = HANDLE_FROM_DNHANDLE(pWorkQueue->hAlertEvent);
  296. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  297. #ifdef DBG
  298. pWorkItem->m_fCancelledOrCompleting = TRUE;
  299. #endif // DBG
  300. DPFX(DPFPREP, 7, "New work item = 0x%p, overlapped = 0x%p, queue = 0x%p.",
  301. pWorkItem, &pWorkItem->m_Overlapped, pWorkQueue);
  302. ThreadpoolStatsCreate(pWorkItem);
  303. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  304. ThreadpoolStatsQueue(pWorkItem); // we can't tell when completion port I/O gets queued
  305. #endif // DPNBUILD_USEIOCOMPLETIONPORTS
  306. }
  307. return pWorkItem;
  308. } // CreateOverlappedIoWorkItem
  309. #undef DPF_MODNAME
  310. #define DPF_MODNAME "ReleaseOverlappedIoWorkItem"
  311. //=============================================================================
  312. // ReleaseOverlappedIoWorkItem
  313. //-----------------------------------------------------------------------------
  314. //
  315. // Description: Returns an unused asynchronous I/O operation work item back
  316. // to the pool.
  317. //
  318. // Arguments:
  319. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
  320. // CWorkItem * pWorkItem - Pointer to work item with overlapped
  321. // structure that is no longer needed.
  322. //
  323. // Returns: None.
  324. //=============================================================================
  325. void ReleaseOverlappedIoWorkItem(DPTPWORKQUEUE * const pWorkQueue,
  326. CWorkItem * const pWorkItem)
  327. {
  328. DPFX(DPFPREP, 7, "Returning work item = 0x%p, overlapped = 0x%p, queue = 0x%p.",
  329. pWorkItem, &pWorkItem->m_Overlapped, pWorkQueue);
  330. pWorkQueue->pWorkItemPool->Release(pWorkItem);
  331. } // ReleaseOverlappedIoWorkItem
  332. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  333. #undef DPF_MODNAME
  334. #define DPF_MODNAME "SubmitIoOperation"
  335. //=============================================================================
  336. // SubmitIoOperation
  337. //-----------------------------------------------------------------------------
  338. //
  339. // Description: Submits a new asynchronous I/O operation work item to the
  340. // work queue to be monitored for completion.
  341. //
  342. // This is only necessary when not using I/O completion ports.
  343. //
  344. // Arguments:
  345. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
  346. // CWorkItem * pWorkItem - Pointer to work item with overlapped
  347. // structure used by OS and completion
  348. // callback information.
  349. //
  350. // Returns: None.
  351. //=============================================================================
  352. void SubmitIoOperation(DPTPWORKQUEUE * const pWorkQueue,
  353. CWorkItem * const pWorkItem)
  354. {
  355. //
  356. // The caller must have pre-populated the overlapped structure's hEvent
  357. // field with the work queue's alert event.
  358. //
  359. DNASSERT(pWorkItem != NULL);
  360. DNASSERT(pWorkItem->m_Overlapped.hEvent == HANDLE_FROM_DNHANDLE(pWorkQueue->hAlertEvent));
  361. DNASSERT(pWorkItem->m_fCancelledOrCompleting);
  362. DPFX(DPFPREP, 5, "Submitting I/O work item 0x%p (context = 0x%p, fn = 0x%p, queue = 0x%p).",
  363. pWorkItem, pWorkItem->m_pvCallbackContext, pWorkItem->m_pfnWorkCallback,
  364. pWorkQueue);
  365. //
  366. // Push this I/O onto the watch list.
  367. //
  368. DNInterlockedPushEntrySList(&pWorkQueue->SlistOutstandingIO,
  369. &pWorkItem->m_SlistEntry);
  370. } // SubmitIoOperation
  371. #undef DPF_MODNAME
  372. #define DPF_MODNAME "ProcessIo"
  373. //=============================================================================
  374. // ProcessIo
  375. //-----------------------------------------------------------------------------
  376. //
  377. // Description: Queues any completed I/O operations as work items in the
  378. // passed in list pointers. The new work items are added without
  379. // using Interlocked functions
  380. //
  381. // This is only necessary when not using I/O completion ports.
  382. //
  383. // Arguments:
  384. // DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
  385. // DNSLIST_ENTRY ** ppHead - Pointer to initial list head pointer, and
  386. // place to store new head pointer.
  387. // DNSLIST_ENTRY ** ppTail - Pointer to existing list tail pointer, or
  388. // place to store new tail pointer.
  389. // USHORT * pusCount - Pointer to existing list count, it will be
  390. // updated to reflect new items.
  391. //
  392. // Returns: Nothing.
  393. //=============================================================================
  394. void ProcessIo(DPTPWORKQUEUE * const pWorkQueue,
  395. DNSLIST_ENTRY ** const ppHead,
  396. DNSLIST_ENTRY ** const ppTail,
  397. USHORT * const pusCount)
  398. {
  399. DNSLIST_ENTRY * pSlistEntryHeadNotComplete = NULL;
  400. USHORT usCountNotComplete = 0;
  401. DNSLIST_ENTRY * pSlistEntryTailNotComplete = NULL;
  402. DNSLIST_ENTRY * pSlistEntry;
  403. CWorkItem * pWorkItem;
  404. //
  405. // Pop off the entire list of I/O, and check it for completions.
  406. //
  407. pSlistEntry = DNInterlockedFlushSList(&pWorkQueue->SlistOutstandingIO);
  408. while (pSlistEntry != NULL)
  409. {
  410. pWorkItem = CONTAINING_OBJECT(pSlistEntry, CWorkItem, m_SlistEntry);
  411. pSlistEntry = pSlistEntry->Next;
  412. //
  413. // If the I/O operation is complete, then queue it as a work item.
  414. // Otherwise, put it back in the list.
  415. //
  416. if (DNHasOverlappedIoCompleted(&pWorkItem->m_Overlapped))
  417. {
  418. DPFX(DPFPREP, 5, "Queueing I/O work item 0x%p for completion on queue 0x%p, (Internal = 0x%x, InternalHigh = 0x%x, Offset = 0x%x, OffsetHigh = 0x%x).",
  419. pWorkItem, pWorkQueue,
  420. pWorkItem->m_Overlapped.Internal,
  421. pWorkItem->m_Overlapped.InternalHigh,
  422. pWorkItem->m_Overlapped.Offset,
  423. pWorkItem->m_Overlapped.OffsetHigh);
  424. ThreadpoolStatsQueue(pWorkItem);
  425. //
  426. // Add it to the caller's list.
  427. //
  428. if ((*ppHead) == NULL)
  429. {
  430. *ppTail = &pWorkItem->m_SlistEntry;
  431. }
  432. pWorkItem->m_SlistEntry.Next = *ppHead;
  433. *ppHead = &pWorkItem->m_SlistEntry;
  434. *pusCount = (*pusCount) + 1;
  435. }
  436. else
  437. {
  438. //DPFX(DPFPREP, 9, "Putting I/O work item 0x%p back into list.", pWorkItem);
  439. //
  440. // Add it to our local "not complete" list.
  441. //
  442. if (pSlistEntryHeadNotComplete == NULL)
  443. {
  444. pSlistEntryTailNotComplete = &pWorkItem->m_SlistEntry;
  445. }
  446. pWorkItem->m_SlistEntry.Next = pSlistEntryHeadNotComplete;
  447. pSlistEntryHeadNotComplete = &pWorkItem->m_SlistEntry;
  448. usCountNotComplete++;
  449. }
  450. }
  451. //
  452. // If we encountered any I/O that hadn't completed, put it all back on the
  453. // list in one fell swoop.
  454. //
  455. if (pSlistEntryHeadNotComplete != NULL)
  456. {
  457. DNInterlockedPushListSList(&pWorkQueue->SlistOutstandingIO,
  458. pSlistEntryHeadNotComplete,
  459. pSlistEntryTailNotComplete,
  460. usCountNotComplete);
  461. }
  462. } // ProcessIo
  463. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  464. #endif // ! WINCE