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
573 lines
17 KiB
/******************************************************************************
|
|
*
|
|
* 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
|