|
|
/******************************************************************************
* * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved. * * File: io.cpp * * Content: DirectPlay Thread Pool I/O functions. * * History: * Date By Reason * ======== ======== ========= * 10/31/01 VanceO Created. * ******************************************************************************/
#include "dpnthreadpooli.h"
// Overlapped I/O is not supported on Windows CE.
#ifndef WINCE
//=============================================================================
// Macros
//=============================================================================
#if ((defined(_XBOX)) && (! defined(XBOX_ON_DESKTOP)))
#define DNHasOverlappedIoCompleted(pOverlapped) ((pOverlapped)->OffsetHigh != HRESULT_FROM_WIN32(ERROR_IO_PENDING))
#else // ! _XBOX or XBOX_ON_DESKTOP
#define DNHasOverlappedIoCompleted(pOverlapped) HasOverlappedIoCompleted(pOverlapped)
#endif // ! _XBOX or XBOX_ON_DESKTOP
//=============================================================================
// Globals
//=============================================================================
CFixedPool g_TrackedFilePool;
#undef DPF_MODNAME
#define DPF_MODNAME "InitializeWorkQueueIoInfo"
//=============================================================================
// InitializeWorkQueueIoInfo
//-----------------------------------------------------------------------------
//
// Description: Initializes the I/O info for the given work queue.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to initialize.
//
// Returns: HRESULT
// DPN_OK - Successfully initialized the work queue object's
// I/O information.
// DPNERR_OUTOFMEMORY - Failed to allocate memory while initializing.
//=============================================================================
HRESULT InitializeWorkQueueIoInfo(DPTPWORKQUEUE * const pWorkQueue) { HRESULT hr = DPN_OK;
DNInitializeSListHead(&pWorkQueue->SlistOutstandingIO); pWorkQueue->blTrackedFiles.Initialize();
return hr; } // InitializeWorkQueueIoInfo
#undef DPF_MODNAME
#define DPF_MODNAME "DeinitializeWorkQueueIoInfo"
//=============================================================================
// DeinitializeWorkQueueIoInfo
//-----------------------------------------------------------------------------
//
// Description: Cleans up work queue I/O info.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to initialize.
//
// Returns: Nothing.
//=============================================================================
void DeinitializeWorkQueueIoInfo(DPTPWORKQUEUE * const pWorkQueue) { DNASSERT(DNInterlockedFlushSList(&pWorkQueue->SlistOutstandingIO) == NULL); DNASSERT(pWorkQueue->blTrackedFiles.IsEmpty()); } // DeinitializeWorkQueueIoInfo
#undef DPF_MODNAME
#define DPF_MODNAME "StartTrackingFileIo"
//=============================================================================
// StartTrackingFileIo
//-----------------------------------------------------------------------------
//
// Description: Starts tracking overlapped I/O for a given file handle on
// the specified work queue. The handle is not duplicated
// and it should remain valid until StopTrackingFileIo is called.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
// HANDLE hFile - Handle of file to track.
//
// Returns: HRESULT
// DPN_OK - Starting tracking for the file was successful.
// DPNERR_ALREADYREGISTERED - The specified file handle is already being
// tracked.
// DPNERR_OUTOFMEMORY - Not enough memory to track the file.
//=============================================================================
HRESULT StartTrackingFileIo(DPTPWORKQUEUE * const pWorkQueue, const HANDLE hFile) { HRESULT hr; CTrackedFile * pTrackedFile; #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
HANDLE hIoCompletionPort; #endif // DPNBUILD_USEIOCOMPLETIONPORTS
#ifdef DBG
CBilink * pBilink; CTrackedFile * pTrackedFileTemp; #endif // DBG
//
// Get a tracking container from the pool.
//
pTrackedFile = (CTrackedFile*) g_TrackedFilePool.Get(pWorkQueue); if (pTrackedFile == NULL) { DPFX(DPFPREP, 0, "Couldn't get item for tracking file 0x%p!", hFile); hr = DPNERR_OUTOFMEMORY; goto Failure; }
pTrackedFile->m_hFile = MAKE_DNHANDLE(hFile);
#ifdef DPNBUILD_USEIOCOMPLETIONPORTS
//
// Associate the file with the I/O completion port.
//
hIoCompletionPort = CreateIoCompletionPort(hFile, HANDLE_FROM_DNHANDLE(pWorkQueue->hIoCompletionPort), 0, 1); if (hIoCompletionPort != HANDLE_FROM_DNHANDLE(pWorkQueue->hIoCompletionPort)) { #ifdef DBG
DWORD dwError;
dwError = GetLastError(); DPFX(DPFPREP, 0, "Couldn't associate file 0x%p with I/O completion port 0x%p (err = %u)!", hFile, pWorkQueue->hIoCompletionPort, dwError); #endif // DBG
#pragma BUGBUG(vanceo, "Can't fail because of our hack")
//hr = DPNERR_GENERIC;
//goto Failure;
} #endif // DPNBUILD_USEIOCOMPLETIONPORTS
DPFX(DPFPREP, 7, "Work queue 0x%p starting to tracking I/O for file 0x%p using object 0x%p.", pWorkQueue, hFile, pTrackedFile);
//
// Add the item to the list.
//
DNEnterCriticalSection(&pWorkQueue->csListLock);
#ifdef DBG
//
// Assert that the handle isn't already being tracked.
//
pBilink = pWorkQueue->blTrackedFiles.GetNext(); while (pBilink != &pWorkQueue->blTrackedFiles) { pTrackedFileTemp = CONTAINING_OBJECT(pBilink, CTrackedFile, m_blList); DNASSERT(pTrackedFileTemp->IsValid()); DNASSERT(HANDLE_FROM_DNHANDLE(pTrackedFileTemp->m_hFile) != hFile); pBilink = pBilink->GetNext(); } #endif // DBG
pTrackedFile->m_blList.InsertBefore(&pWorkQueue->blTrackedFiles); DNLeaveCriticalSection(&pWorkQueue->csListLock);
hr = DPN_OK;
Exit:
return hr;
Failure:
if (pTrackedFile != NULL) { g_TrackedFilePool.Release(pTrackedFile); pTrackedFile = NULL; }
goto Exit; } // StartTrackingFileIo
#undef DPF_MODNAME
#define DPF_MODNAME "StopTrackingFileIo"
//=============================================================================
// StopTrackingFileIo
//-----------------------------------------------------------------------------
//
// Description: Stops tracking overlapped I/O for a given file handle on
// the specified work queue.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
// HANDLE hFile - Handle of file to stop tracking.
//
// Returns: HRESULT
// DPN_OK - Stopping tracking for the file was successful.
// DPNERR_INVALIDHANDLE - File handle was not being tracked.
//=============================================================================
HRESULT StopTrackingFileIo(DPTPWORKQUEUE * const pWorkQueue, const HANDLE hFile) { HRESULT hr = DPNERR_INVALIDHANDLE; CBilink * pBilink; CTrackedFile * pTrackedFile;
DNEnterCriticalSection(&pWorkQueue->csListLock); pBilink = pWorkQueue->blTrackedFiles.GetNext(); while (pBilink != &pWorkQueue->blTrackedFiles) { pTrackedFile = CONTAINING_OBJECT(pBilink, CTrackedFile, m_blList); DNASSERT(pTrackedFile->IsValid()); pBilink = pBilink->GetNext();
if (HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile) == hFile) { DPFX(DPFPREP, 7, "Work queue 0x%p no longer tracking I/O for file 0x%p under object 0x%p.", pWorkQueue, hFile, pTrackedFile); REMOVE_DNHANDLE(pTrackedFile->m_hFile); pTrackedFile->m_blList.RemoveFromList(); g_TrackedFilePool.Release(pTrackedFile); pTrackedFile = NULL; hr = DPN_OK; break; } } DNLeaveCriticalSection(&pWorkQueue->csListLock);
return hr; } // StopTrackingFileIo
#undef DPF_MODNAME
#define DPF_MODNAME "CancelIoForThisThread"
//=============================================================================
// CancelIoForThisThread
//-----------------------------------------------------------------------------
//
// Description: Cancels asynchronous I/O operations submitted by this thread
// for all tracked files.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object owning this
// thread.
//
// Returns: None.
//=============================================================================
void CancelIoForThisThread(DPTPWORKQUEUE * const pWorkQueue) { CBilink * pBilink; CTrackedFile * pTrackedFile; BOOL fResult;
DNEnterCriticalSection(&pWorkQueue->csListLock); pBilink = pWorkQueue->blTrackedFiles.GetNext(); while (pBilink != &pWorkQueue->blTrackedFiles) { pTrackedFile = CONTAINING_OBJECT(pBilink, CTrackedFile, m_blList); DNASSERT(pTrackedFile->IsValid());
DPFX(DPFPREP, 3, "Cancelling file 0x%p I/O for this thread (queue = 0x%p).", HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile), pWorkQueue);
fResult = CancelIo(HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile)); if (! fResult) { #ifdef DBG
DWORD dwError;
dwError = GetLastError(); DPFX(DPFPREP, 0, "Couldn't cancel file 0x%p I/O for this thread (err = %u)!", HANDLE_FROM_DNHANDLE(pTrackedFile->m_hFile), dwError); #endif // DBG
//
// Continue...
//
}
pBilink = pBilink->GetNext(); } DNLeaveCriticalSection(&pWorkQueue->csListLock); } // CancelIoForThisThread
#undef DPF_MODNAME
#define DPF_MODNAME "CreateOverlappedIoWorkItem"
//=============================================================================
// CreateOverlappedIoWorkItem
//-----------------------------------------------------------------------------
//
// Description: Creates a new asynchronous I/O operation work item for the
// work queue.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
// PFNDPTNWORKCALLBACK pfnWorkCallback - Callback to execute when operation
// completes.
// PVOID pvCallbackContext - User specified context to pass to
// callback.
//
// Returns: Pointer to work item, or NULL if couldn't allocate memory.
//=============================================================================
CWorkItem * CreateOverlappedIoWorkItem(DPTPWORKQUEUE * const pWorkQueue, const PFNDPTNWORKCALLBACK pfnWorkCallback, PVOID const pvCallbackContext) { CWorkItem * pWorkItem;
//
// Get an entry from the pool.
//
pWorkItem = (CWorkItem*) pWorkQueue->pWorkItemPool->Get(pWorkQueue); if (pWorkItem != NULL) { //
// Initialize the work item.
//
pWorkItem->m_pfnWorkCallback = pfnWorkCallback; pWorkItem->m_pvCallbackContext = pvCallbackContext;
#ifdef DPNBUILD_USEIOCOMPLETIONPORTS
pWorkItem->m_Overlapped.hEvent = NULL; #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
pWorkItem->m_Overlapped.hEvent = HANDLE_FROM_DNHANDLE(pWorkQueue->hAlertEvent); #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
#ifdef DBG
pWorkItem->m_fCancelledOrCompleting = TRUE; #endif // DBG
DPFX(DPFPREP, 7, "New work item = 0x%p, overlapped = 0x%p, queue = 0x%p.", pWorkItem, &pWorkItem->m_Overlapped, pWorkQueue);
ThreadpoolStatsCreate(pWorkItem); #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
ThreadpoolStatsQueue(pWorkItem); // we can't tell when completion port I/O gets queued
#endif // DPNBUILD_USEIOCOMPLETIONPORTS
}
return pWorkItem; } // CreateOverlappedIoWorkItem
#undef DPF_MODNAME
#define DPF_MODNAME "ReleaseOverlappedIoWorkItem"
//=============================================================================
// ReleaseOverlappedIoWorkItem
//-----------------------------------------------------------------------------
//
// Description: Returns an unused asynchronous I/O operation work item back
// to the pool.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
// CWorkItem * pWorkItem - Pointer to work item with overlapped
// structure that is no longer needed.
//
// Returns: None.
//=============================================================================
void ReleaseOverlappedIoWorkItem(DPTPWORKQUEUE * const pWorkQueue, CWorkItem * const pWorkItem) { DPFX(DPFPREP, 7, "Returning work item = 0x%p, overlapped = 0x%p, queue = 0x%p.", pWorkItem, &pWorkItem->m_Overlapped, pWorkQueue);
pWorkQueue->pWorkItemPool->Release(pWorkItem); } // ReleaseOverlappedIoWorkItem
#ifndef DPNBUILD_USEIOCOMPLETIONPORTS
#undef DPF_MODNAME
#define DPF_MODNAME "SubmitIoOperation"
//=============================================================================
// SubmitIoOperation
//-----------------------------------------------------------------------------
//
// Description: Submits a new asynchronous I/O operation work item to the
// work queue to be monitored for completion.
//
// This is only necessary when not using I/O completion ports.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
// CWorkItem * pWorkItem - Pointer to work item with overlapped
// structure used by OS and completion
// callback information.
//
// Returns: None.
//=============================================================================
void SubmitIoOperation(DPTPWORKQUEUE * const pWorkQueue, CWorkItem * const pWorkItem) { //
// The caller must have pre-populated the overlapped structure's hEvent
// field with the work queue's alert event.
//
DNASSERT(pWorkItem != NULL); DNASSERT(pWorkItem->m_Overlapped.hEvent == HANDLE_FROM_DNHANDLE(pWorkQueue->hAlertEvent));
DNASSERT(pWorkItem->m_fCancelledOrCompleting);
DPFX(DPFPREP, 5, "Submitting I/O work item 0x%p (context = 0x%p, fn = 0x%p, queue = 0x%p).", pWorkItem, pWorkItem->m_pvCallbackContext, pWorkItem->m_pfnWorkCallback, pWorkQueue);
//
// Push this I/O onto the watch list.
//
DNInterlockedPushEntrySList(&pWorkQueue->SlistOutstandingIO, &pWorkItem->m_SlistEntry); } // SubmitIoOperation
#undef DPF_MODNAME
#define DPF_MODNAME "ProcessIo"
//=============================================================================
// ProcessIo
//-----------------------------------------------------------------------------
//
// Description: Queues any completed I/O operations as work items in the
// passed in list pointers. The new work items are added without
// using Interlocked functions
//
// This is only necessary when not using I/O completion ports.
//
// Arguments:
// DPTPWORKQUEUE * pWorkQueue - Pointer to work queue object to use.
// DNSLIST_ENTRY ** ppHead - Pointer to initial list head pointer, and
// place to store new head pointer.
// DNSLIST_ENTRY ** ppTail - Pointer to existing list tail pointer, or
// place to store new tail pointer.
// USHORT * pusCount - Pointer to existing list count, it will be
// updated to reflect new items.
//
// Returns: Nothing.
//=============================================================================
void ProcessIo(DPTPWORKQUEUE * const pWorkQueue, DNSLIST_ENTRY ** const ppHead, DNSLIST_ENTRY ** const ppTail, USHORT * const pusCount) { DNSLIST_ENTRY * pSlistEntryHeadNotComplete = NULL; USHORT usCountNotComplete = 0; DNSLIST_ENTRY * pSlistEntryTailNotComplete = NULL; DNSLIST_ENTRY * pSlistEntry; CWorkItem * pWorkItem;
//
// Pop off the entire list of I/O, and check it for completions.
//
pSlistEntry = DNInterlockedFlushSList(&pWorkQueue->SlistOutstandingIO); while (pSlistEntry != NULL) { pWorkItem = CONTAINING_OBJECT(pSlistEntry, CWorkItem, m_SlistEntry); pSlistEntry = pSlistEntry->Next;
//
// If the I/O operation is complete, then queue it as a work item.
// Otherwise, put it back in the list.
//
if (DNHasOverlappedIoCompleted(&pWorkItem->m_Overlapped)) { 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).", pWorkItem, pWorkQueue, pWorkItem->m_Overlapped.Internal, pWorkItem->m_Overlapped.InternalHigh, pWorkItem->m_Overlapped.Offset, pWorkItem->m_Overlapped.OffsetHigh); ThreadpoolStatsQueue(pWorkItem);
//
// Add it to the caller's list.
//
if ((*ppHead) == NULL) { *ppTail = &pWorkItem->m_SlistEntry; } pWorkItem->m_SlistEntry.Next = *ppHead; *ppHead = &pWorkItem->m_SlistEntry; *pusCount = (*pusCount) + 1; } else { //DPFX(DPFPREP, 9, "Putting I/O work item 0x%p back into list.", pWorkItem);
//
// Add it to our local "not complete" list.
//
if (pSlistEntryHeadNotComplete == NULL) { pSlistEntryTailNotComplete = &pWorkItem->m_SlistEntry; } pWorkItem->m_SlistEntry.Next = pSlistEntryHeadNotComplete; pSlistEntryHeadNotComplete = &pWorkItem->m_SlistEntry; usCountNotComplete++; } }
//
// If we encountered any I/O that hadn't completed, put it all back on the
// list in one fell swoop.
//
if (pSlistEntryHeadNotComplete != NULL) { DNInterlockedPushListSList(&pWorkQueue->SlistOutstandingIO, pSlistEntryHeadNotComplete, pSlistEntryTailNotComplete, usCountNotComplete); } } // ProcessIo
#endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
#endif // ! WINCE
|