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

/******************************************************************************
*
* 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