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.
3358 lines
88 KiB
3358 lines
88 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 1998-2002 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: ThreadPool.cpp
|
|
* Content: main job thread pool
|
|
*
|
|
*
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 11/25/1998 jtk Created
|
|
***************************************************************************/
|
|
|
|
#include "dnwsocki.h"
|
|
|
|
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// Constant definitions
|
|
//**********************************************************************
|
|
|
|
//
|
|
// select polling period and duration for I/O (milliseconds)
|
|
//
|
|
static const DWORD g_dwSelectTimePeriod = 4; // wait for jobs for 4 ms, then wait for I/O
|
|
static const DWORD g_dwSelectTimeSlice = 0; // wait for I/O for 0 ms, then wait for jobs
|
|
|
|
|
|
//**********************************************************************
|
|
// Macro definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Structure definitions
|
|
//**********************************************************************
|
|
|
|
#ifndef DPNBUILD_NOSPUI
|
|
//
|
|
// structure passed to dialog threads
|
|
//
|
|
typedef struct _DIALOG_THREAD_PARAM
|
|
{
|
|
DIALOG_FUNCTION *pDialogFunction;
|
|
void *pContext;
|
|
CThreadPool *pThisThreadPool;
|
|
} DIALOG_THREAD_PARAM;
|
|
#endif // !DPNBUILD_NOSPUI
|
|
|
|
//**********************************************************************
|
|
// Variable definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function prototypes
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function definitions
|
|
//**********************************************************************
|
|
DWORD WINAPI DPNBlockingJobThreadProc(PVOID pvParameter);
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::PoolAllocFunction
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::PoolAllocFunction"
|
|
|
|
BOOL CThreadPool::PoolAllocFunction( void* pvItem, void* pvContext )
|
|
{
|
|
CThreadPool* pThreadPool = (CThreadPool*)pvItem;
|
|
|
|
pThreadPool->m_iRefCount = 0;
|
|
pThreadPool->m_fAllowThreadCountReduction = FALSE;
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
pThreadPool->m_iIntendedThreadCount = 0;
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
pThreadPool->m_uReservedSocketCount = 0;
|
|
memset( &pThreadPool->m_SocketSet, 0x00, sizeof( pThreadPool->m_SocketSet ) );
|
|
memset( &pThreadPool->m_pSocketPorts, 0x00, sizeof( pThreadPool->m_pSocketPorts ) );
|
|
pThreadPool->m_pvTimerDataWinsock1IO = NULL;
|
|
pThreadPool->m_uiTimerUniqueWinsock1IO = 0;
|
|
pThreadPool->m_fCancelWinsock1IO = FALSE;
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
|
|
#ifndef DPNBUILD_NONATHELP
|
|
pThreadPool->m_fNATHelpLoaded = FALSE;
|
|
pThreadPool->m_fNATHelpTimerJobSubmitted = FALSE;
|
|
pThreadPool->m_dwNATHelpUpdateThreadID = 0;
|
|
#endif // DPNBUILD_NONATHELP
|
|
|
|
#if ((defined(WINNT)) && (! defined(DPNBUILD_NOMULTICAST)))
|
|
pThreadPool->m_fMadcapLoaded = FALSE;
|
|
#endif // WINNT and not DPNBUILD_NOMULTICAST
|
|
|
|
|
|
pThreadPool->m_Sig[0] = 'T';
|
|
pThreadPool->m_Sig[1] = 'H';
|
|
pThreadPool->m_Sig[2] = 'P';
|
|
pThreadPool->m_Sig[3] = 'L';
|
|
|
|
pThreadPool->m_TimerJobList.Initialize();
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
pThreadPool->m_blBlockingJobQueue.Initialize();
|
|
pThreadPool->m_hBlockingJobThread = NULL;
|
|
pThreadPool->m_hBlockingJobEvent = NULL;
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
return TRUE;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::PoolDellocFunction
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::PoolDeallocFunction"
|
|
|
|
void CThreadPool::PoolDeallocFunction( void* pvItem )
|
|
{
|
|
#ifdef DBG
|
|
const CThreadPool* pThreadPool = (CThreadPool*)pvItem;
|
|
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
DNASSERT( pThreadPool->m_uReservedSocketCount == 0 );
|
|
DNASSERT( pThreadPool->m_pvTimerDataWinsock1IO == NULL );
|
|
DNASSERT( ! pThreadPool->m_fCancelWinsock1IO );
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
|
|
DNASSERT( pThreadPool->m_iRefCount == 0 );
|
|
#ifndef DPNBUILD_NONATHELP
|
|
DNASSERT( pThreadPool->m_fNATHelpLoaded == FALSE );
|
|
DNASSERT( pThreadPool->m_fNATHelpTimerJobSubmitted == FALSE );
|
|
DNASSERT( pThreadPool->m_dwNATHelpUpdateThreadID == 0 );
|
|
#endif // DPNBUILD_NONATHELP
|
|
#if ((defined(WINNT)) && (! defined(DPNBUILD_NOMULTICAST)))
|
|
DNASSERT( pThreadPool->m_fMadcapLoaded == FALSE );
|
|
#endif // WINNT and not DPNBUILD_NOMULTICAST
|
|
DNASSERT( pThreadPool->m_TimerJobList.IsEmpty() != FALSE );
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
DNASSERT( pThreadPool->m_blBlockingJobQueue.IsEmpty() );
|
|
DNASSERT( pThreadPool->m_hBlockingJobThread == NULL );
|
|
DNASSERT( pThreadPool->m_hBlockingJobEvent == NULL );
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
#endif // DBG
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::Initialize - initialize work threads
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error Code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::Initialize"
|
|
|
|
HRESULT CThreadPool::Initialize( void )
|
|
{
|
|
HRESULT hr;
|
|
BOOL fInittedLock = FALSE;
|
|
BOOL fInittedTimerDataLock = FALSE;
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
BOOL fInittedBlockingJobLock = FALSE;
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
DPFX(DPFPREP, 4, "(0x%p) Enter", this);
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
//
|
|
// initialize critical sections
|
|
//
|
|
if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_Lock, 0 );
|
|
DebugSetCriticalSectionGroup( &m_Lock, &g_blDPNWSockCritSecsHeld ); // separate dpnwsock CSes from the rest of DPlay's CSes
|
|
|
|
fInittedLock = TRUE;
|
|
|
|
|
|
//
|
|
// Create the thread pool work interface
|
|
//
|
|
#ifdef DPNBUILD_LIBINTERFACE
|
|
#if ((defined(DPNBUILD_ONLYONETHREAD)) && (! defined(DPNBUILD_MULTIPLETHREADPOOLS)))
|
|
DPTPCF_GetObject(reinterpret_cast<void**>(&m_pDPThreadPoolWork));
|
|
hr = S_OK;
|
|
#else // ! DPNBUILD_ONLYONETHREAD or DPNBUILD_MULTIPLETHREADPOOLS
|
|
hr = DPTPCF_CreateObject(reinterpret_cast<void**>(&m_pDPThreadPoolWork));
|
|
#endif // ! DPNBUILD_ONLYONETHREAD or DPNBUILD_MULTIPLETHREADPOOLS
|
|
#else // ! DPNBUILD_LIBINTERFACE
|
|
hr = COM_CoCreateInstance( CLSID_DirectPlay8ThreadPool,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IDirectPlay8ThreadPoolWork,
|
|
reinterpret_cast<void**>( &m_pDPThreadPoolWork ),
|
|
FALSE );
|
|
#endif // ! DPNBUILD_LIBINTERFACE
|
|
if ( hr != S_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, " Failed to create thread pool work interface (err = 0x%lx)!", hr);
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
if ( DNInitializeCriticalSection( &m_TimerDataLock ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_TimerDataLock, 1 );
|
|
DebugSetCriticalSectionGroup( &m_TimerDataLock, &g_blDPNWSockCritSecsHeld ); // separate dpnwsock CSes from the rest of DPlay's CSes
|
|
fInittedTimerDataLock = TRUE;
|
|
|
|
|
|
DNASSERT( m_fAllowThreadCountReduction == FALSE );
|
|
m_fAllowThreadCountReduction = TRUE;
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
if ( DNInitializeCriticalSection( &m_csBlockingJobLock ) == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_csBlockingJobLock, 0 );
|
|
DebugSetCriticalSectionGroup( &m_csBlockingJobLock, &g_blDPNWSockCritSecsHeld ); // separate dpnwsock CSes from the rest of DPlay's CSes
|
|
fInittedBlockingJobLock = TRUE;
|
|
|
|
DPFX(DPFPREP, 7, "SetIntendedThreadCount %i", g_iThreadCount);
|
|
SetIntendedThreadCount( g_iThreadCount );
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
Exit:
|
|
|
|
DPFX(DPFPREP, 4, "(0x%p) Return [0x%lx]", this, hr);
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
if (fInittedBlockingJobLock)
|
|
{
|
|
DNDeleteCriticalSection(&m_csBlockingJobLock);
|
|
fInittedBlockingJobLock = FALSE;
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
if (fInittedTimerDataLock)
|
|
{
|
|
DNDeleteCriticalSection(&m_TimerDataLock);
|
|
fInittedTimerDataLock = FALSE;
|
|
}
|
|
|
|
if (m_pDPThreadPoolWork != NULL)
|
|
{
|
|
IDirectPlay8ThreadPoolWork_Release(m_pDPThreadPoolWork);
|
|
m_pDPThreadPoolWork = NULL;
|
|
}
|
|
|
|
if (fInittedLock)
|
|
{
|
|
DNDeleteCriticalSection(&m_Lock);
|
|
fInittedLock = FALSE;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::Deinitialize - destroy work threads
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::Deinitialize"
|
|
|
|
void CThreadPool::Deinitialize( void )
|
|
{
|
|
DPFX(DPFPREP, 4, "(0x%p) Enter", this );
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
//
|
|
// Keep trying to cancel the Winsock1 timer. It may fail because it's in the
|
|
// process of actively executing, but eventually we will catch it while only
|
|
// the timer is active.
|
|
//
|
|
Lock();
|
|
if (m_pvTimerDataWinsock1IO != NULL)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwInterval;
|
|
|
|
|
|
DPFX(DPFPREP, 1, "Cancelling Winsock 1 I/O timer.");
|
|
dwInterval = 10;
|
|
do
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
|
|
m_pvTimerDataWinsock1IO,
|
|
m_uiTimerUniqueWinsock1IO,
|
|
0);
|
|
if (hr != DPN_OK)
|
|
{
|
|
Unlock();
|
|
IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork,
|
|
dwInterval,
|
|
0);
|
|
dwInterval += 5; // next time wait a bit longer
|
|
DNASSERT(dwInterval < 600);
|
|
Lock();
|
|
}
|
|
else
|
|
{
|
|
m_pvTimerDataWinsock1IO = NULL;
|
|
}
|
|
}
|
|
while (m_pvTimerDataWinsock1IO != NULL);
|
|
}
|
|
Unlock();
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
|
|
#ifndef DPNBUILD_NONATHELP
|
|
//
|
|
// Stop submitting new NAT help refresh jobs.
|
|
//
|
|
if ( IsNATHelpTimerJobSubmitted() )
|
|
{
|
|
//
|
|
// Try to cancel the job. It could fail if the timer is in the
|
|
// process of firing right now. If it is firing, keep looping
|
|
// until we see that the timer has noticed cancellation.
|
|
//
|
|
DPFX(DPFPREP, 5, "Cancelling NAT Help refresh timer job.");
|
|
if (! StopTimerJob( this, DPNERR_USERCANCEL ))
|
|
{
|
|
DWORD dwInterval;
|
|
|
|
|
|
DPFX(DPFPREP, 4, "Couldn't cancel NAT Help refresh timer job, waiting for completion.");
|
|
dwInterval = 10;
|
|
while (*((volatile BOOL *) (&m_fNATHelpTimerJobSubmitted)))
|
|
{
|
|
IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork,
|
|
dwInterval,
|
|
0);
|
|
dwInterval += 5; // next time wait a bit longer
|
|
if (dwInterval > 500)
|
|
{
|
|
dwInterval = 500;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(! m_fNATHelpTimerJobSubmitted);
|
|
}
|
|
}
|
|
#endif // DPNBUILD_NONATHELP
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
//
|
|
// Stop the blocking job thread, if it was running.
|
|
//
|
|
if (m_hBlockingJobThread != NULL)
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// Queue an item with a NULL callback to signal that the thread
|
|
// should quit.
|
|
//
|
|
hr = SubmitBlockingJob(NULL, NULL);
|
|
DNASSERT(hr == DPN_OK);
|
|
|
|
//
|
|
// Wait for the thread to complete.
|
|
//
|
|
hr = IDirectPlay8ThreadPoolWork_WaitWhileWorking(m_pDPThreadPoolWork,
|
|
HANDLE_FROM_DNHANDLE(m_hBlockingJobThread),
|
|
0);
|
|
DNASSERT(hr == DPN_OK);
|
|
DNASSERT(m_blBlockingJobQueue.IsEmpty());
|
|
|
|
DNCloseHandle(m_hBlockingJobThread);
|
|
m_hBlockingJobThread = NULL;
|
|
|
|
DNCloseHandle(m_hBlockingJobEvent);
|
|
m_hBlockingJobEvent = NULL;
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
#ifndef DPNBUILD_NONATHELP
|
|
//
|
|
// The refresh timer and blocking jobs should have finished earlier,
|
|
// it should be safe to unload NAT help now (if we even loaded it).
|
|
//
|
|
if ( IsNATHelpLoaded() )
|
|
{
|
|
UnloadNATHelp();
|
|
m_fNATHelpLoaded = FALSE;
|
|
}
|
|
#endif // DPNBUILD_NONATHELP
|
|
|
|
#if ((defined(WINNT)) && (! defined(DPNBUILD_NOMULTICAST)))
|
|
//
|
|
// Unload MADCAP, if we had loaded it.
|
|
//
|
|
if ( IsMadcapLoaded() )
|
|
{
|
|
UnloadMadcap();
|
|
m_fMadcapLoaded = FALSE;
|
|
}
|
|
#endif // WINNT and not DPNBUILD_NOMULTICAST
|
|
|
|
if ( m_pDPThreadPoolWork != NULL )
|
|
{
|
|
IDirectPlay8ThreadPoolWork_Release(m_pDPThreadPoolWork);
|
|
m_pDPThreadPoolWork = NULL;
|
|
}
|
|
|
|
m_fAllowThreadCountReduction = FALSE;
|
|
|
|
DNDeleteCriticalSection( &m_TimerDataLock );
|
|
DNDeleteCriticalSection( &m_Lock );
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
DNDeleteCriticalSection(&m_csBlockingJobLock);
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
DPFX(DPFPREP, 4, "(0x%p) Leave", this );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::GetNewReadIOData - get new read request from pool
|
|
//
|
|
// Entry: Pointer to context
|
|
//
|
|
// Exit: Pointer to new read request
|
|
// NULL = out of memory
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::GetNewReadIOData"
|
|
|
|
#if ((! defined(DPNBUILD_NOWINSOCK2)) && (! defined(DPNBUILD_ONLYWINSOCK2)))
|
|
CReadIOData *CThreadPool::GetNewReadIOData( READ_IO_DATA_POOL_CONTEXT *const pContext, const BOOL fNeedOverlapped )
|
|
#else // DPNBUILD_NOWINSOCK2 or DPNBUILD_ONLYWINSOCK2
|
|
CReadIOData *CThreadPool::GetNewReadIOData( READ_IO_DATA_POOL_CONTEXT *const pContext )
|
|
#endif // DPNBUILD_NOWINSOCK2 or DPNBUILD_ONLYWINSOCK2
|
|
{
|
|
CReadIOData * pTempReadData;
|
|
#ifndef DPNBUILD_NOWINSOCK2
|
|
OVERLAPPED * pOverlapped;
|
|
#endif // ! DPNBUILD_NOWINSOCK2
|
|
|
|
|
|
DNASSERT( pContext != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pTempReadData = NULL;
|
|
pContext->pThreadPool = this;
|
|
|
|
pTempReadData = (CReadIOData*)g_ReadIODataPool.Get( pContext );
|
|
if ( pTempReadData == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to get new ReadIOData from pool!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// we have data, immediately add a reference to it
|
|
//
|
|
pTempReadData->AddRef();
|
|
|
|
DNASSERT( pTempReadData->m_pSourceSocketAddress != NULL );
|
|
|
|
#ifndef DPNBUILD_NOWINSOCK2
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
if (! fNeedOverlapped)
|
|
{
|
|
DNASSERT( pTempReadData->GetOverlapped() == NULL );
|
|
}
|
|
else
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
#ifdef DPNBUILD_ONLYONEPROCESSOR
|
|
hr = IDirectPlay8ThreadPoolWork_CreateOverlapped( m_pDPThreadPoolWork,
|
|
0,
|
|
CSocketPort::Winsock2ReceiveComplete,
|
|
pTempReadData,
|
|
&pOverlapped,
|
|
0 );
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't create overlapped structure (err = 0x%lx)!", hr);
|
|
goto Failure;
|
|
}
|
|
#else // ! DPNBUILD_ONLYONEPROCESSOR
|
|
hr = IDirectPlay8ThreadPoolWork_CreateOverlapped( m_pDPThreadPoolWork,
|
|
pContext->dwCPU,
|
|
CSocketPort::Winsock2ReceiveComplete,
|
|
pTempReadData,
|
|
&pOverlapped,
|
|
0 );
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't create overlapped structure for CPU %i (err = 0x%lx)!",
|
|
pContext->dwCPU, hr);
|
|
goto Failure;
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONEPROCESSOR
|
|
|
|
pTempReadData->SetOverlapped( pOverlapped );
|
|
}
|
|
#endif // ! DPNBUILD_NOWINSOCK2
|
|
|
|
|
|
Exit:
|
|
|
|
return pTempReadData;
|
|
|
|
Failure:
|
|
if ( pTempReadData != NULL )
|
|
{
|
|
pTempReadData->DecRef();
|
|
pTempReadData = NULL;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::ReturnReadIOData - return read data item to pool
|
|
//
|
|
// Entry: Pointer to read data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::ReturnReadIOData"
|
|
|
|
void CThreadPool::ReturnReadIOData( CReadIOData *const pReadData )
|
|
{
|
|
DNASSERT( pReadData != NULL );
|
|
DNASSERT( pReadData->m_pSourceSocketAddress != NULL );
|
|
|
|
g_ReadIODataPool.Release( pReadData );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::SubmitTimerJob - add a timer job to the timer list
|
|
//
|
|
// Entry: Whether to perform immediately or not
|
|
// Retry count
|
|
// Boolean indicating that we retry forever
|
|
// Retry interval
|
|
// Boolean indicating that we wait forever
|
|
// Idle wait interval
|
|
// Pointer to callback when event fires
|
|
// Pointer to callback when event complete
|
|
// User context
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::SubmitTimerJob"
|
|
|
|
#ifdef DPNBUILD_ONLYONEPROCESSOR
|
|
HRESULT CThreadPool::SubmitTimerJob( const BOOL fPerformImmediately,
|
|
const UINT_PTR uRetryCount,
|
|
const BOOL fRetryForever,
|
|
const DWORD dwRetryInterval,
|
|
const BOOL fIdleWaitForever,
|
|
const DWORD dwIdleTimeout,
|
|
TIMER_EVENT_CALLBACK *const pTimerCallbackFunction,
|
|
TIMER_EVENT_COMPLETE *const pTimerCompleteFunction,
|
|
void *const pContext )
|
|
#else // ! DPNBUILD_ONLYONEPROCESSOR
|
|
HRESULT CThreadPool::SubmitTimerJob( const DWORD dwCPU,
|
|
const BOOL fPerformImmediately,
|
|
const UINT_PTR uRetryCount,
|
|
const BOOL fRetryForever,
|
|
const DWORD dwRetryInterval,
|
|
const BOOL fIdleWaitForever,
|
|
const DWORD dwIdleTimeout,
|
|
TIMER_EVENT_CALLBACK *const pTimerCallbackFunction,
|
|
TIMER_EVENT_COMPLETE *const pTimerCompleteFunction,
|
|
void *const pContext )
|
|
#endif // ! DPNBUILD_ONLYONEPROCESSOR
|
|
{
|
|
HRESULT hr = DPN_OK;
|
|
TIMER_OPERATION_ENTRY *pEntry = NULL;
|
|
BOOL fTimerDataLocked = FALSE;
|
|
#ifdef DPNBUILD_ONLYONEPROCESSOR
|
|
DWORD dwCPU = -1;
|
|
#endif // DPNBUILD_ONLYONEPROCESSOR
|
|
|
|
|
|
DNASSERT( uRetryCount != 0 );
|
|
DNASSERT( pTimerCallbackFunction != NULL );
|
|
DNASSERT( pTimerCompleteFunction != NULL );
|
|
DNASSERT( pContext != NULL ); // must be non-NULL because it's the lookup key to remove job
|
|
|
|
|
|
//
|
|
// allocate new enum entry
|
|
//
|
|
pEntry = static_cast<TIMER_OPERATION_ENTRY*>( g_TimerEntryPool.Get() );
|
|
if ( pEntry == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Cannot allocate memory to add to timer list!" );
|
|
goto Failure;
|
|
}
|
|
DNASSERT( pEntry->pContext == NULL );
|
|
|
|
|
|
DPFX(DPFPREP, 7, "Created timer entry 0x%p (CPU %i, context 0x%p, immed. %i, tries %u, forever %i, interval %u, timeout %u, forever = %i).",
|
|
pEntry, dwCPU, pContext, fPerformImmediately, uRetryCount, fRetryForever, dwRetryInterval, dwIdleTimeout, fIdleWaitForever);
|
|
|
|
//
|
|
// build timer entry block
|
|
//
|
|
pEntry->pContext = pContext;
|
|
pEntry->uRetryCount = uRetryCount;
|
|
pEntry->fRetryForever = fRetryForever;
|
|
pEntry->dwRetryInterval = dwRetryInterval;
|
|
pEntry->dwIdleTimeout = dwIdleTimeout;
|
|
pEntry->fIdleWaitForever = fIdleWaitForever;
|
|
pEntry->pTimerCallback = pTimerCallbackFunction;
|
|
pEntry->pTimerComplete = pTimerCompleteFunction;
|
|
pEntry->pThreadPool = this;
|
|
pEntry->fCancelling = FALSE;
|
|
#ifndef DPNBUILD_ONLYONEPROCESSOR
|
|
pEntry->dwCPU = dwCPU;
|
|
#endif // ! DPNBUILD_ONLYONEPROCESSOR
|
|
pEntry->dwNextRetryTime = GETTIMESTAMP();
|
|
if (fPerformImmediately)
|
|
{
|
|
pEntry->dwNextRetryTime -= 1; // longest possible time, so that the time has already passed
|
|
}
|
|
else
|
|
{
|
|
pEntry->dwNextRetryTime += dwRetryInterval;
|
|
}
|
|
|
|
LockTimerData();
|
|
fTimerDataLocked = TRUE;
|
|
|
|
pEntry->pvTimerData = NULL;
|
|
pEntry->uiTimerUnique = 0;
|
|
if (fPerformImmediately)
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_QueueWorkItem(m_pDPThreadPoolWork,
|
|
dwCPU, // CPU
|
|
CThreadPool::GenericTimerCallback, // callback
|
|
pEntry, // user context
|
|
0); // flags
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem queueing immediate work item!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
|
|
dwCPU, // CPU
|
|
dwRetryInterval, // delay
|
|
CThreadPool::GenericTimerCallback, // callback
|
|
pEntry, // user context
|
|
&pEntry->pvTimerData, // timer data (returned)
|
|
&pEntry->uiTimerUnique, // timer unique(returned)
|
|
0); // flags
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem scheduling timer!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// debug block to check for duplicate contexts
|
|
//
|
|
DEBUG_ONLY(
|
|
{
|
|
CBilink *pTempLink;
|
|
|
|
|
|
pTempLink = m_TimerJobList.GetNext();
|
|
while ( pTempLink != &m_TimerJobList )
|
|
{
|
|
TIMER_OPERATION_ENTRY *pTempTimerEntry;
|
|
|
|
|
|
pTempTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempLink );
|
|
DNASSERT( pTempTimerEntry->pContext != pContext );
|
|
pTempLink = pTempLink->GetNext();
|
|
}
|
|
}
|
|
);
|
|
|
|
//
|
|
// link to rest of list
|
|
//
|
|
pEntry->Linkage.InsertAfter( &m_TimerJobList );
|
|
|
|
UnlockTimerData();
|
|
fTimerDataLocked = FALSE;
|
|
|
|
|
|
Exit:
|
|
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem with SubmitTimerJob" );
|
|
DisplayDNError( 0, hr );
|
|
}
|
|
|
|
return hr;
|
|
|
|
|
|
Failure:
|
|
|
|
if ( pEntry != NULL )
|
|
{
|
|
g_TimerEntryPool.Release( pEntry );
|
|
DEBUG_ONLY( pEntry = NULL );
|
|
}
|
|
|
|
if ( fTimerDataLocked != FALSE )
|
|
{
|
|
UnlockTimerData();
|
|
fTimerDataLocked = FALSE;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::StopTimerJob - remove timer job from list
|
|
//
|
|
// Entry: Pointer to job context (these MUST be unique for jobs)
|
|
// Command result
|
|
//
|
|
// Exit: Boolean indicating whether a job was stopped or not
|
|
//
|
|
// Note: This function is for the forced removal of a job from the timed job
|
|
// list. It is assumed that the caller of this function will clean
|
|
// up any messes.
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::StopTimerJob"
|
|
|
|
BOOL CThreadPool::StopTimerJob( void *const pContext, const HRESULT hCommandResult )
|
|
{
|
|
BOOL fComplete = FALSE;
|
|
CBilink * pTempEntry;
|
|
TIMER_OPERATION_ENTRY * pTimerEntry = NULL;
|
|
|
|
|
|
DNASSERT( pContext != NULL );
|
|
|
|
DPFX(DPFPREP, 8, "Parameters (0x%p, 0x%lx)", pContext, hCommandResult);
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
LockTimerData();
|
|
|
|
pTempEntry = m_TimerJobList.GetNext();
|
|
while ( pTempEntry != &m_TimerJobList )
|
|
{
|
|
pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempEntry );
|
|
if ( pTimerEntry->pContext == pContext )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// Mark the entry as cancelling.
|
|
//
|
|
pTimerEntry->fCancelling = TRUE;
|
|
|
|
|
|
//
|
|
// Make sure an actual timer has been submitted.
|
|
//
|
|
if (pTimerEntry->pvTimerData != NULL)
|
|
{
|
|
//
|
|
// Attempt to cancel the timer. If it succeeds, we're cool.
|
|
//
|
|
hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
|
|
pTimerEntry->pvTimerData,
|
|
pTimerEntry->uiTimerUnique,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
if ((! pTimerEntry->fIdleWaitForever) ||
|
|
(pTimerEntry->uRetryCount > 0))
|
|
{
|
|
//
|
|
// Timer hasn't been submitted yet, the completion function should
|
|
// notice that it is now cancelling.
|
|
//
|
|
DPFX(DPFPREP, 1, "Timer for entry 0x%p not submitted yet, reporting that timer will still fire.",
|
|
pTimerEntry);
|
|
hr = DPNERR_CANNOTCANCEL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No other timer will ever be submitted because the job is now
|
|
// waiting forever.
|
|
//
|
|
DPFX(DPFPREP, 1, "Entry 0x%p was idling forever, cancelling.",
|
|
pTimerEntry);
|
|
hr = DPN_OK;
|
|
}
|
|
}
|
|
|
|
if (hr != DPN_OK)
|
|
{
|
|
//
|
|
// The processing function is still going to fire. It should notice
|
|
// that it needs to complete.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// remove this link from the list
|
|
//
|
|
pTimerEntry->Linkage.RemoveFromList();
|
|
|
|
fComplete = TRUE;
|
|
}
|
|
|
|
//
|
|
// terminate loop
|
|
//
|
|
break;
|
|
}
|
|
|
|
pTempEntry = pTempEntry->GetNext();
|
|
}
|
|
|
|
UnlockTimerData();
|
|
|
|
//
|
|
// tell owner that the job is complete and return the job to the pool
|
|
// outside of the lock
|
|
//
|
|
if (fComplete)
|
|
{
|
|
DPFX(DPFPREP, 8, "Found cancellable timer entry 0x%p (context 0x%p), completing with result 0x%lx.",
|
|
pTimerEntry, pTimerEntry->pContext, hCommandResult);
|
|
|
|
pTimerEntry->pTimerComplete( hCommandResult, pTimerEntry->pContext );
|
|
|
|
//
|
|
// Relock the timer list so we can safely put items back in the pool,
|
|
//
|
|
LockTimerData();
|
|
|
|
g_TimerEntryPool.Release( pTimerEntry );
|
|
|
|
UnlockTimerData();
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 8, "Returning [%i]", fComplete);
|
|
|
|
return fComplete;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::ModifyTimerJobNextRetryTime - update a timer job's next retry time
|
|
//
|
|
// Entry: Pointer to job context (these MUST be unique for jobs)
|
|
// New time for next retry
|
|
//
|
|
// Exit: Boolean indicating whether a job was found & updated or not
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::ModifyTimerJobNextRetryTime"
|
|
|
|
BOOL CThreadPool::ModifyTimerJobNextRetryTime( void *const pContext,
|
|
DWORD const dwNextRetryTime)
|
|
{
|
|
BOOL fFound;
|
|
CBilink * pTempEntry;
|
|
TIMER_OPERATION_ENTRY * pTimerEntry;
|
|
INT_PTR iResult;
|
|
|
|
|
|
|
|
DNASSERT( pContext != NULL );
|
|
|
|
DPFX(DPFPREP, 7, "Parameters (0x%p, %u)", pContext, dwNextRetryTime);
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fFound = FALSE;
|
|
|
|
|
|
LockTimerData();
|
|
|
|
|
|
pTempEntry = m_TimerJobList.GetNext();
|
|
while ( pTempEntry != &m_TimerJobList )
|
|
{
|
|
pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempEntry );
|
|
if ( pTimerEntry->pContext == pContext )
|
|
{
|
|
iResult = (int) (pTimerEntry->dwNextRetryTime - dwNextRetryTime);
|
|
if (iResult != 0)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwNextRetryTimeDifference;
|
|
DWORD dwNewRetryInterval;
|
|
|
|
|
|
if (iResult < 0)
|
|
{
|
|
//
|
|
// Next time to fire is now later.
|
|
//
|
|
|
|
dwNextRetryTimeDifference = dwNextRetryTime - pTimerEntry->dwNextRetryTime;
|
|
dwNewRetryInterval = pTimerEntry->dwRetryInterval + dwNextRetryTimeDifference;
|
|
|
|
DPFX(DPFPREP, 7, "Timer 0x%p next retry time delayed by %u ms from offset %u to offset %u, modifying interval from %u to %u.",
|
|
pTimerEntry,
|
|
dwNextRetryTimeDifference,
|
|
pTimerEntry->dwNextRetryTime,
|
|
dwNextRetryTime,
|
|
pTimerEntry->dwRetryInterval,
|
|
dwNewRetryInterval);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Next time to fire is now earlier.
|
|
//
|
|
|
|
dwNextRetryTimeDifference = pTimerEntry->dwNextRetryTime - dwNextRetryTime;
|
|
dwNewRetryInterval = pTimerEntry->dwRetryInterval - dwNextRetryTimeDifference;
|
|
|
|
DPFX(DPFPREP, 7, "Timer 0x%p next retry time moved up by %u ms from offset %u to offset %u, modifying interval from %u to %u.",
|
|
pTimerEntry,
|
|
dwNextRetryTimeDifference,
|
|
pTimerEntry->dwNextRetryTime,
|
|
dwNextRetryTime,
|
|
pTimerEntry->dwRetryInterval,
|
|
dwNewRetryInterval);
|
|
}
|
|
|
|
//
|
|
// Force the timer to expire right away if the calculations returned a really
|
|
// long delay.
|
|
//
|
|
if (dwNewRetryInterval > 0x80000000)
|
|
{
|
|
DPFX(DPFPREP, 1, "Timer 0x%p delay 0x%x/%u (next retry time %u) being set to 0.",
|
|
pTimerEntry, dwNewRetryInterval, dwNewRetryInterval, dwNextRetryTime);
|
|
pTimerEntry->dwRetryInterval = 0;
|
|
pTimerEntry->dwNextRetryTime = GETTIMESTAMP();
|
|
}
|
|
else
|
|
{
|
|
pTimerEntry->dwRetryInterval = dwNewRetryInterval;
|
|
pTimerEntry->dwNextRetryTime = dwNextRetryTime;
|
|
}
|
|
|
|
|
|
//
|
|
// Attempt to cancel the existing timer.
|
|
//
|
|
hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
|
|
pTimerEntry->pvTimerData,
|
|
pTimerEntry->uiTimerUnique,
|
|
0);
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 1, "Couldn't cancel existing timer for entry 0x%p (err = 0x%lx), modifying retry timer only.",
|
|
pTimerEntry, hr);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Restart the timer.
|
|
//
|
|
#ifdef DPNBUILD_ONLYONEPROCESSOR
|
|
hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
|
|
-1, // pick any CPU
|
|
dwNewRetryInterval, // delay
|
|
CThreadPool::GenericTimerCallback, // callback
|
|
pTimerEntry, // user context
|
|
&pTimerEntry->pvTimerData, // timer data (returned)
|
|
&pTimerEntry->uiTimerUnique, // timer unique (returned)
|
|
0); // flags
|
|
#else // ! DPNBUILD_ONLYONEPROCESSOR
|
|
hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
|
|
pTimerEntry->dwCPU, // use same CPU as before
|
|
dwNewRetryInterval, // delay
|
|
CThreadPool::GenericTimerCallback, // callback
|
|
pTimerEntry, // user context
|
|
&pTimerEntry->pvTimerData, // timer data (returned)
|
|
&pTimerEntry->uiTimerUnique, // timer unique (returned)
|
|
0); // flags
|
|
#endif // ! DPNBUILD_ONLYONEPROCESSOR
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't reschedule timer for entry 0x%p (err = 0x%lx)!",
|
|
pTimerEntry, hr);
|
|
|
|
pTimerEntry->pvTimerData = NULL;
|
|
|
|
//
|
|
// Drop lock while we complete timer.
|
|
//
|
|
UnlockTimerData();
|
|
|
|
pTimerEntry->pTimerComplete(hr, pTimerEntry->pContext);
|
|
g_TimerEntryPool.Release(pTimerEntry);
|
|
|
|
LockTimerData();
|
|
|
|
//
|
|
// Drop through...
|
|
//
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The intervals are the same, no change necessary.
|
|
//
|
|
|
|
DPFX(DPFPREP, 7, "Timer 0x%p next retry time was unchanged (offset %u), not changing interval from %u.",
|
|
pTimerEntry,
|
|
pTimerEntry->dwNextRetryTime,
|
|
pTimerEntry->dwRetryInterval);
|
|
}
|
|
|
|
|
|
fFound = TRUE;
|
|
|
|
|
|
//
|
|
// Terminate loop
|
|
//
|
|
break;
|
|
}
|
|
|
|
pTempEntry = pTempEntry->GetNext();
|
|
}
|
|
|
|
|
|
UnlockTimerData();
|
|
|
|
|
|
DPFX(DPFPREP, 7, "Returning [%i]", fFound);
|
|
|
|
return fFound;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::SubmitBlockingJob - submits a blocking job to the be processed by the blockable thread
|
|
// duplicate commands (matching callback and context) are disallowed
|
|
//
|
|
// Entry: Pointer to callback that executes blocking job
|
|
// User context
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::SubmitBlockingJob"
|
|
|
|
HRESULT CThreadPool::SubmitBlockingJob( BLOCKING_JOB_CALLBACK *const pfnBlockingJobCallback,
|
|
void *const pvContext )
|
|
{
|
|
HRESULT hr;
|
|
BLOCKING_JOB * pJob = NULL;
|
|
DWORD dwTemp;
|
|
BOOL fQueueLocked = FALSE;
|
|
CBilink * pBilink;
|
|
BLOCKING_JOB * pExistingJob;
|
|
|
|
|
|
//
|
|
// allocate new enum entry
|
|
//
|
|
pJob = (BLOCKING_JOB*) g_BlockingJobPool.Get();
|
|
if (pJob == NULL)
|
|
{
|
|
DPFX(DPFPREP, 0, "Cannot allocate memory for blocking job!" );
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 6, "Created blocking job 0x%p (callback 0x%p, context 0x%p).",
|
|
pJob, pfnBlockingJobCallback, pvContext);
|
|
|
|
pJob->Linkage.Initialize();
|
|
pJob->pfnBlockingJobCallback = pfnBlockingJobCallback;
|
|
pJob->pvContext = pvContext;
|
|
|
|
|
|
DNEnterCriticalSection(&m_csBlockingJobLock);
|
|
fQueueLocked = TRUE;
|
|
|
|
//
|
|
// Start the blocking job thread, if we haven't already.
|
|
//
|
|
if (m_hBlockingJobThread == NULL)
|
|
{
|
|
m_hBlockingJobEvent = DNCreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (m_hBlockingJobEvent == NULL)
|
|
{
|
|
#ifdef DBG
|
|
dwTemp = GetLastError();
|
|
DPFX(DPFPREP, 0, "Couldn't create blocking job event (err = %u)!", dwTemp);
|
|
#endif // DBG
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
m_hBlockingJobThread = DNCreateThread(NULL,
|
|
0,
|
|
DPNBlockingJobThreadProc,
|
|
this,
|
|
0,
|
|
&dwTemp);
|
|
if (m_hBlockingJobThread == NULL)
|
|
{
|
|
#ifdef DBG
|
|
dwTemp = GetLastError();
|
|
DPFX(DPFPREP, 0, "Couldn't create blocking job thread (err = %u)!", dwTemp);
|
|
#endif // DBG
|
|
DNCloseHandle(m_hBlockingJobEvent);
|
|
m_hBlockingJobEvent = NULL;
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
DNASSERT(m_blBlockingJobQueue.IsEmpty());
|
|
}
|
|
else
|
|
{
|
|
pBilink = m_blBlockingJobQueue.GetNext();
|
|
while (pBilink != &m_blBlockingJobQueue)
|
|
{
|
|
pExistingJob = CONTAINING_OBJECT(pBilink, BLOCKING_JOB, Linkage);
|
|
if ((pExistingJob->pfnBlockingJobCallback == pfnBlockingJobCallback) &&
|
|
(pExistingJob->pvContext == pvContext))
|
|
{
|
|
DPFX(DPFPREP, 1, "Existing blocking job 0x%p matches new job 0x%p, not submitting.",
|
|
pExistingJob, pJob);
|
|
hr = DPNERR_DUPLICATECOMMAND;
|
|
goto Failure;
|
|
}
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Add job to queue.
|
|
//
|
|
pJob->Linkage.InsertBefore(&m_blBlockingJobQueue);
|
|
|
|
DNLeaveCriticalSection(&m_csBlockingJobLock);
|
|
fQueueLocked = FALSE;
|
|
|
|
//
|
|
// Alert the thread.
|
|
//
|
|
DNSetEvent( m_hBlockingJobEvent );
|
|
|
|
hr = DPN_OK;
|
|
|
|
Exit:
|
|
|
|
return hr;
|
|
|
|
|
|
Failure:
|
|
|
|
if (fQueueLocked)
|
|
{
|
|
DNLeaveCriticalSection(&m_csBlockingJobLock);
|
|
fQueueLocked = FALSE;
|
|
}
|
|
|
|
if (pJob != NULL)
|
|
{
|
|
g_BlockingJobPool.Release(pJob);
|
|
pJob = NULL;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::DoBlockingJobs - processes all blocking jobs.
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::DoBlockingJobs"
|
|
|
|
void CThreadPool::DoBlockingJobs( void )
|
|
{
|
|
BOOL fRunning = TRUE;
|
|
CBilink * pBilink;
|
|
BLOCKING_JOB * pJob;
|
|
|
|
|
|
//
|
|
// Keep looping until it's time to shutdown.
|
|
//
|
|
while (fRunning)
|
|
{
|
|
//
|
|
// Wait for more work.
|
|
//
|
|
DNWaitForSingleObject( m_hBlockingJobEvent, INFINITE );
|
|
|
|
//
|
|
// Keep looping while we have work.
|
|
//
|
|
do
|
|
{
|
|
DNEnterCriticalSection(&m_csBlockingJobLock);
|
|
pBilink = m_blBlockingJobQueue.GetNext();
|
|
if (pBilink == &m_blBlockingJobQueue)
|
|
{
|
|
//
|
|
// Bail out of the inner loop.
|
|
//
|
|
DNLeaveCriticalSection(&m_csBlockingJobLock);
|
|
break;
|
|
}
|
|
|
|
pJob = CONTAINING_OBJECT(pBilink, BLOCKING_JOB, Linkage);
|
|
pJob->Linkage.RemoveFromList();
|
|
|
|
DNLeaveCriticalSection(&m_csBlockingJobLock);
|
|
|
|
//
|
|
// Bail out of both loops if it's the quit job.
|
|
//
|
|
if (pJob->pfnBlockingJobCallback == NULL)
|
|
{
|
|
DPFX(DPFPREP, 5, "Recognized quit job 0x%p.", pJob);
|
|
g_BlockingJobPool.Release(pJob);
|
|
fRunning = FALSE;
|
|
break;
|
|
}
|
|
|
|
DPFX(DPFPREP, 6, "Processing blocking job 0x%p (callback 0x%p, context 0x%p).",
|
|
pJob, pJob->pfnBlockingJobCallback, pJob->pvContext);
|
|
|
|
pJob->pfnBlockingJobCallback(pJob->pvContext);
|
|
|
|
DPFX(DPFPREP, 7, "Returning blocking job 0x%p to pool.", pJob);
|
|
g_BlockingJobPool.Release(pJob);
|
|
}
|
|
while (TRUE);
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_NOSPUI
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::SpawnDialogThread - start a secondary thread to display service
|
|
// provider UI.
|
|
//
|
|
// Entry: Pointer to dialog function
|
|
// Dialog context
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::SpawnDialogThread"
|
|
|
|
HRESULT CThreadPool::SpawnDialogThread( DIALOG_FUNCTION *const pDialogFunction, void *const pDialogContext )
|
|
{
|
|
HRESULT hr;
|
|
DNHANDLE hDialogThread;
|
|
DIALOG_THREAD_PARAM *pThreadParam;
|
|
DWORD dwThreadID;
|
|
|
|
|
|
DNASSERT( pDialogFunction != NULL );
|
|
DNASSERT( pDialogContext != NULL ); // why would anyone not want a dialog context??
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
pThreadParam = NULL;
|
|
|
|
//
|
|
// create and initialize thread param
|
|
//
|
|
pThreadParam = static_cast<DIALOG_THREAD_PARAM*>( DNMalloc( sizeof( *pThreadParam ) ) );
|
|
if ( pThreadParam == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Failed to allocate memory for dialog thread!" );
|
|
goto Failure;
|
|
}
|
|
|
|
pThreadParam->pDialogFunction = pDialogFunction;
|
|
pThreadParam->pContext = pDialogContext;
|
|
pThreadParam->pThisThreadPool = this;
|
|
|
|
//
|
|
// create thread
|
|
//
|
|
hDialogThread = DNCreateThread( NULL, // pointer to security (none)
|
|
0, // stack size (default)
|
|
DialogThreadProc, // thread procedure
|
|
pThreadParam, // thread param
|
|
0, // creation flags (none)
|
|
&dwThreadID ); // pointer to thread ID
|
|
if ( hDialogThread == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to start dialog thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
goto Failure;
|
|
}
|
|
|
|
if ( DNCloseHandle( hDialogThread ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing handle from create dialog thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
Exit:
|
|
return hr;
|
|
|
|
Failure:
|
|
if ( pThreadParam != NULL )
|
|
{
|
|
DNFree( pThreadParam );
|
|
pThreadParam = NULL;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
#endif // !DPNBUILD_NOSPUI
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::GetIOThreadCount - get I/O thread count
|
|
//
|
|
// Entry: Pointer to variable to fill
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::GetIOThreadCount"
|
|
|
|
HRESULT CThreadPool::GetIOThreadCount( LONG *const piThreadCount )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwThreadPoolCount;
|
|
|
|
|
|
DNASSERT( piThreadCount != NULL );
|
|
|
|
Lock();
|
|
|
|
hr = IDirectPlay8ThreadPoolWork_GetThreadCount(m_pDPThreadPoolWork,
|
|
-1,
|
|
&dwThreadPoolCount,
|
|
0);
|
|
switch (hr)
|
|
{
|
|
case DPN_OK:
|
|
{
|
|
*piThreadCount = dwThreadPoolCount;
|
|
DPFX(DPFPREP, 6, "User has explicitly started %u threads.", (*piThreadCount));
|
|
break;
|
|
}
|
|
|
|
case DPNSUCCESS_PENDING:
|
|
{
|
|
if ((IsThreadCountReductionAllowed()) &&
|
|
(((DWORD) GetIntendedThreadCount()) > dwThreadPoolCount))
|
|
{
|
|
*piThreadCount = GetIntendedThreadCount();
|
|
DPFX(DPFPREP, 6, "Thread pool not locked down and only %u threads currently started, using intended count of %u.",
|
|
dwThreadPoolCount, (*piThreadCount));
|
|
}
|
|
else
|
|
{
|
|
*piThreadCount = dwThreadPoolCount;
|
|
DPFX(DPFPREP, 6, "Thread pool locked down (%i) or more than %u threads already started, using actual count of %u.",
|
|
(! IsThreadCountReductionAllowed()), GetIntendedThreadCount(),
|
|
(*piThreadCount));
|
|
}
|
|
hr = DPN_OK;
|
|
break;
|
|
}
|
|
|
|
case DPNERR_NOTREADY:
|
|
{
|
|
DNASSERT(IsThreadCountReductionAllowed());
|
|
|
|
*piThreadCount = GetIntendedThreadCount();
|
|
DPFX(DPFPREP, 6, "Thread pool does not have a thread count set, using intended count of %u.",
|
|
(*piThreadCount));
|
|
|
|
hr = DPN_OK;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed getting thread count!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::SetIOThreadCount - set I/O thread count
|
|
//
|
|
// Entry: New thread count
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::SetIOThreadCount"
|
|
|
|
HRESULT CThreadPool::SetIOThreadCount( const LONG iThreadCount )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwThreadCount;
|
|
|
|
|
|
DNASSERT( iThreadCount > 0 );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
Lock();
|
|
|
|
if ( IsThreadCountReductionAllowed() )
|
|
{
|
|
DPFX(DPFPREP, 4, "Thread pool not locked down, setting intended thread count to %i.", iThreadCount );
|
|
SetIntendedThreadCount( iThreadCount );
|
|
}
|
|
else
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_GetThreadCount(m_pDPThreadPoolWork,
|
|
-1,
|
|
&dwThreadCount,
|
|
0);
|
|
switch (hr)
|
|
{
|
|
case DPN_OK:
|
|
case DPNSUCCESS_PENDING:
|
|
case DPNERR_NOTREADY:
|
|
{
|
|
if ( (DWORD) iThreadCount != dwThreadCount )
|
|
{
|
|
if (hr == DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 1, "Thread count already set to %u by user, not changing to %i total.",
|
|
dwThreadCount, iThreadCount);
|
|
hr = DPNERR_NOTALLOWED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Artificially prevent thread pool reduction after operations have
|
|
// started, to simulate pre-unified-threadpool behavior. Only
|
|
// request a thread count change if the new count is greater
|
|
// than the old count.
|
|
//
|
|
if ( (DWORD) iThreadCount > dwThreadCount )
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_RequestTotalThreadCount(m_pDPThreadPoolWork,
|
|
(DWORD) iThreadCount,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 4, "Thread pool locked down and already has %u threads, not adjusting to %i.",
|
|
dwThreadCount, iThreadCount );
|
|
hr = DPN_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 4, "Thread pool thread count matches (%u).",
|
|
dwThreadCount);
|
|
hr = DPN_OK;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Getting current thread count failed!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::PreventThreadPoolReduction - prevents the thread pool size from being reduced
|
|
//
|
|
// Entry: None
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::PreventThreadPoolReduction"
|
|
|
|
HRESULT CThreadPool::PreventThreadPoolReduction( void )
|
|
{
|
|
HRESULT hr;
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
LONG iDesiredThreads;
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
#ifdef _XBOX
|
|
DWORD dwStatus;
|
|
DWORD dwInterval;
|
|
XNADDR xnaddr;
|
|
#endif // _XBOX
|
|
#ifdef DBG
|
|
DWORD dwStartTime;
|
|
#endif // DBG
|
|
|
|
|
|
Lock();
|
|
|
|
//
|
|
// If we haven't already clamped down, do so, and spin up the threads.
|
|
//
|
|
if ( IsThreadCountReductionAllowed() )
|
|
{
|
|
m_fAllowThreadCountReduction = FALSE;
|
|
|
|
#ifdef DPNBUILD_ONLYONETHREAD
|
|
DPFX(DPFPREP, 3, "Locking down thread pool." );
|
|
#else // ! DPNBUILD_ONLYONETHREAD
|
|
iDesiredThreads = GetIntendedThreadCount();
|
|
DNASSERT( iDesiredThreads > 0 );
|
|
SetIntendedThreadCount( 0 );
|
|
|
|
DPFX(DPFPREP, 3, "Locking down thread count at %i.", iDesiredThreads );
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
#ifdef DBG
|
|
dwStartTime = GETTIMESTAMP();
|
|
#endif // DBG
|
|
|
|
//
|
|
// Have the thread pool object try to start the requested number of threads.
|
|
//
|
|
// We'll ignore failure, because we could still operate in DoWork mode even
|
|
// when starting the threads fails. It most likely failed because the user
|
|
// is in that mode already anyway (DPNERR_ALREADYINITIALIZED).
|
|
//
|
|
hr = IDirectPlay8ThreadPoolWork_RequestTotalThreadCount(m_pDPThreadPoolWork,
|
|
iDesiredThreads,
|
|
0);
|
|
if (hr != DPN_OK)
|
|
{
|
|
if (hr != DPNERR_ALREADYINITIALIZED)
|
|
{
|
|
DPFX(DPFPREP, 0, "Requesting thread count failed (err = 0x%lx)!", hr);
|
|
}
|
|
|
|
//
|
|
// Continue...
|
|
//
|
|
}
|
|
|
|
#ifdef DBG
|
|
DPFX(DPFPREP, 8, "Spent %u ms trying to start %i threads.",
|
|
(GETTIMESTAMP() - dwStartTime), iDesiredThreads);
|
|
#endif // DBG
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
#ifdef _XBOX
|
|
#ifdef DBG
|
|
dwStartTime = GETTIMESTAMP();
|
|
#endif // DBG
|
|
|
|
#pragma TODO(vanceo, "Use #defines")
|
|
//
|
|
// Wait until the Ethernet link is active.
|
|
//
|
|
DPFX(DPFPREP, 1, "Ensuring Ethernet link status is active...");
|
|
dwStatus = XNetGetEthernetLinkStatus();
|
|
dwInterval = 5;
|
|
while (! (dwStatus & XNET_ETHERNET_LINK_ACTIVE))
|
|
{
|
|
if (dwInterval > 100)
|
|
{
|
|
DPFX(DPFPREP, 0, "Ethernet link never became ready (status = 0x%x)!",
|
|
dwStatus);
|
|
hr = DPNERR_NOCONNECTION;
|
|
goto Failure;
|
|
}
|
|
|
|
DPFX(DPFPREP, 0, "Ethernet link is not ready (0x%x).", dwStatus);
|
|
IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork, dwInterval, 0);
|
|
dwInterval += 5;
|
|
|
|
dwStatus = XNetGetEthernetLinkStatus();
|
|
}
|
|
|
|
//
|
|
// Wait until a DHCP address has been acquired or we give up trying.
|
|
// If we're not using DHCP, this should return something other than
|
|
// XNET_GET_XNADDR_PENDING right away.
|
|
//
|
|
DPFX(DPFPREP, 1, "Waiting for a valid address...");
|
|
dwStatus = XNetGetTitleXnAddr(&xnaddr);
|
|
dwInterval = 5;
|
|
while (dwStatus == XNET_GET_XNADDR_PENDING)
|
|
{
|
|
if (dwInterval > 225)
|
|
{
|
|
DPFX(DPFPREP, 0, "Never acquired an address (status = 0x%x)!",
|
|
dwStatus);
|
|
hr = DPNERR_NOTREADY;
|
|
goto Failure;
|
|
}
|
|
|
|
IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork, dwInterval, 0);
|
|
dwInterval += 5;
|
|
|
|
dwStatus = XNetGetTitleXnAddr(&xnaddr);
|
|
}
|
|
|
|
if (dwStatus == XNET_GET_XNADDR_NONE)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't get an address!");
|
|
hr = DPNERR_NOCONNECTION;
|
|
goto Failure;
|
|
}
|
|
|
|
DPFX(DPFPREP, 1, "Network ready.");
|
|
|
|
#pragma TODO(vanceo, "Ethernet link status timer?")
|
|
|
|
#ifdef DBG
|
|
DPFX(DPFPREP, 8, "Spent %u ms waiting for network.",
|
|
(GETTIMESTAMP() - dwStartTime));
|
|
#endif // DBG
|
|
#endif // _XBOX
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 3, "Thread count already locked down." );
|
|
}
|
|
|
|
//
|
|
// If we're here, everything is successful.
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
#ifdef _XBOX
|
|
Exit:
|
|
#endif // _XBOX
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
|
|
#ifdef _XBOX
|
|
Failure:
|
|
|
|
//
|
|
// The only time we can fail in this function is if we disabled thread
|
|
// count reduction. In order to return to the previous state, re-enable
|
|
// reduction.
|
|
//
|
|
m_fAllowThreadCountReduction = TRUE;
|
|
|
|
goto Exit;
|
|
#endif // _XBOX
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
#if ((! defined(DPNBUILD_NOMULTICAST)) && (defined(WINNT)))
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::EnsureMadcapLoaded - Load the MADCAP API if it hasn't been
|
|
// already, and it can be loaded.
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: TRUE if MADCAP is loaded, FALSE otherwise
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::EnsureMadcapLoaded"
|
|
|
|
BOOL CThreadPool::EnsureMadcapLoaded( void )
|
|
{
|
|
BOOL fReturn;
|
|
|
|
|
|
DPFX(DPFPREP, 7, "Enter");
|
|
|
|
#ifndef DPNBUILD_NOREGISTRY
|
|
if (! g_fDisableMadcapSupport)
|
|
#endif // ! DPNBUILD_NOREGISTRY
|
|
{
|
|
Lock();
|
|
|
|
if (! IsMadcapLoaded())
|
|
{
|
|
if ( LoadMadcap() == FALSE )
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to load MADCAP API, continuing." );
|
|
fReturn = FALSE;
|
|
}
|
|
else
|
|
{
|
|
m_fMadcapLoaded = TRUE;
|
|
fReturn = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 4, "MADCAP already loaded." );
|
|
fReturn = TRUE;
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
#ifndef DPNBUILD_NOREGISTRY
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Not loading MADCAP API." );
|
|
fReturn = FALSE;
|
|
}
|
|
#endif // ! DPNBUILD_NOREGISTRY
|
|
|
|
DPFX(DPFPREP, 7, "Return [%i]", fReturn);
|
|
|
|
return fReturn;
|
|
}
|
|
#endif // ! DPNBUILD_NOMULTICAST and WINNT
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_NONATHELP
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::EnsureNATHelpLoaded - Load NAT Help if it hasn't been already.
|
|
// This has no return values, so if NAT
|
|
// traversal is explicitly disabled, or some
|
|
// error occurs, NAT Help will not actually
|
|
// get loaded.
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::EnsureNATHelpLoaded"
|
|
|
|
void CThreadPool::EnsureNATHelpLoaded( void )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwTemp;
|
|
DPNHCAPS dpnhcaps;
|
|
DWORD dwNATHelpRetryTime;
|
|
#ifdef DBG
|
|
DWORD dwStartTime;
|
|
#endif // DBG
|
|
|
|
|
|
DPFX(DPFPREP, 7, "Enter");
|
|
|
|
|
|
#ifndef DPNBUILD_NOREGISTRY
|
|
if ((! g_fDisableDPNHGatewaySupport) || (! g_fDisableDPNHFirewallSupport))
|
|
#endif // ! DPNBUILD_NOREGISTRY
|
|
{
|
|
Lock();
|
|
|
|
if ( ! IsNATHelpLoaded() )
|
|
{
|
|
//
|
|
// Attempt to load the NAT helper(s).
|
|
//
|
|
if ( LoadNATHelp() )
|
|
{
|
|
m_fNATHelpLoaded = TRUE;
|
|
|
|
#ifdef DBG
|
|
dwStartTime = GETTIMESTAMP();
|
|
#endif // DBG
|
|
|
|
//
|
|
// Initialize the timer values.
|
|
//
|
|
dwNATHelpRetryTime = -1;
|
|
|
|
|
|
//
|
|
// Loop through each NAT help object.
|
|
//
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
if (g_papNATHelpObjects[dwTemp] != NULL)
|
|
{
|
|
//
|
|
// Determine how often to refresh the NAT help caps in the future.
|
|
//
|
|
// We're going to force server detection now. This will increase the time
|
|
// it takes to startup up this Enum/Connect/Listen operation, but is
|
|
// necessary since the IDirectPlayNATHelp::GetRegisteredAddresses call in
|
|
// CSocketPort::BindToInternetGateway and possibly the
|
|
// IDirectPlayNATHelp::QueryAddress call in CSocketPort::MungePublicAddress
|
|
// could occur before the first NATHelp GetCaps timer fires.
|
|
// In the vast majority of NAT cases, the gateway is already available.
|
|
// If we hadn't detected that yet (because we hadn't called
|
|
// IDirectPlayNATHelp::GetCaps with DPNHGETCAPS_UPDATESERVERSTATUS)
|
|
// then we would get an incorrect result from GetRegisteredAddresses or
|
|
// QueryAddress.
|
|
//
|
|
ZeroMemory(&dpnhcaps, sizeof(dpnhcaps));
|
|
dpnhcaps.dwSize = sizeof(dpnhcaps);
|
|
|
|
hr = IDirectPlayNATHelp_GetCaps(g_papNATHelpObjects[dwTemp],
|
|
&dpnhcaps,
|
|
DPNHGETCAPS_UPDATESERVERSTATUS);
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed getting NAT Help capabilities (error = 0x%lx), continuing.",
|
|
hr);
|
|
|
|
//
|
|
// NAT Help will probably not work correctly, but that won't prevent
|
|
// local connections from working. Consider it non-fatal.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// See if this is the shortest interval.
|
|
//
|
|
if (dpnhcaps.dwRecommendedGetCapsInterval < dwNATHelpRetryTime)
|
|
{
|
|
dwNATHelpRetryTime = dpnhcaps.dwRecommendedGetCapsInterval;
|
|
}
|
|
|
|
#ifndef DPNBUILD_NOLOCALNAT
|
|
//
|
|
// Remember if there's a local NAT.
|
|
//
|
|
if ((dpnhcaps.dwFlags & DPNHCAPSFLAG_GATEWAYPRESENT) &&
|
|
(dpnhcaps.dwFlags & DPNHCAPSFLAG_GATEWAYISLOCAL))
|
|
{
|
|
g_fLocalNATDetectedAtStartup = TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_fLocalNATDetectedAtStartup = FALSE;
|
|
}
|
|
#endif // ! DPNBUILD_NOLOCALNAT
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No object loaded in this slot.
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If there's a retry interval, submit a timer job.
|
|
//
|
|
if (dwNATHelpRetryTime != -1)
|
|
{
|
|
//
|
|
// Attempt to add timer job that will refresh the lease and server
|
|
// status.
|
|
// Although we're submitting it as a periodic timer, it's actually
|
|
// not going to be called at regular intervals.
|
|
// There is a race condition where the alert event/IOCP may have
|
|
// been fired already, and another thread tried to cancel this timer
|
|
// which hasn't been submitted yet. The logic to handle this race
|
|
// is placed there (HandleNATHelpUpdate); here we can assume we
|
|
// are the first person to submit the refresh timer.
|
|
//
|
|
|
|
|
|
DPFX(DPFPREP, 7, "Submitting NAT Help refresh timer (for every %u ms) for thread pool 0x%p.",
|
|
dwNATHelpRetryTime, this);
|
|
|
|
DNASSERT(! m_fNATHelpTimerJobSubmitted );
|
|
m_fNATHelpTimerJobSubmitted = TRUE;
|
|
|
|
#ifdef DPNBUILD_ONLYONEPROCESSOR
|
|
hr = SubmitTimerJob(FALSE, // don't perform immediately
|
|
1, // retry count
|
|
TRUE, // retry forever
|
|
dwNATHelpRetryTime, // retry timeout
|
|
TRUE, // wait forever
|
|
0, // idle timeout
|
|
CThreadPool::NATHelpTimerFunction, // periodic callback function
|
|
CThreadPool::NATHelpTimerComplete, // completion function
|
|
this); // context
|
|
#else // ! DPNBUILD_ONLYONEPROCESSOR
|
|
hr = SubmitTimerJob(-1, // pick any CPU
|
|
FALSE, // don't perform immediately
|
|
1, // retry count
|
|
TRUE, // retry forever
|
|
dwNATHelpRetryTime, // retry timeout
|
|
TRUE, // wait forever
|
|
0, // idle timeout
|
|
CThreadPool::NATHelpTimerFunction, // periodic callback function
|
|
CThreadPool::NATHelpTimerComplete, // completion function
|
|
this); // context
|
|
#endif // ! DPNBUILD_ONLYONEPROCESSOR
|
|
if (hr != DPN_OK)
|
|
{
|
|
m_fNATHelpTimerJobSubmitted = FALSE;
|
|
DPFX(DPFPREP, 0, "Failed to submit timer job to watch over NAT Help (err = 0x%lx)!", hr );
|
|
|
|
//
|
|
// NAT Help will probably not work correctly, but that won't
|
|
// prevent local connections from working. Consider it
|
|
// non-fatal.
|
|
//
|
|
}
|
|
}
|
|
|
|
#ifdef DBG
|
|
DPFX(DPFPREP, 8, "Spent %u ms preparing NAT Help.",
|
|
(GETTIMESTAMP() - dwStartTime));
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to load NAT Help, continuing." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 4, "NAT Help already loaded." );
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
#ifndef DPNBUILD_NOREGISTRY
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Not loading NAT Help." );
|
|
}
|
|
#endif // ! DPNBUILD_NOREGISTRY
|
|
|
|
DPFX(DPFPREP, 7, "Leave");
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::PerformSubsequentNATHelpGetCaps - blocking function to get NAT Help caps again
|
|
//
|
|
// Entry: Pointer to job information
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CEndpoint::EnumQueryJobCallback"
|
|
|
|
void CThreadPool::PerformSubsequentNATHelpGetCaps( void * const pvContext )
|
|
{
|
|
CThreadPool * pThisThreadPool;
|
|
|
|
|
|
DNASSERT( pvContext != NULL );
|
|
pThisThreadPool = (CThreadPool*) pvContext;
|
|
|
|
pThisThreadPool->HandleNATHelpUpdate(NULL);
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::NATHelpTimerComplete - NAT Help timer job has completed
|
|
//
|
|
// Entry: Timer result code
|
|
// Context
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::NATHelpTimerComplete"
|
|
|
|
void CThreadPool::NATHelpTimerComplete( const HRESULT hResult, void * const pContext )
|
|
{
|
|
CThreadPool * pThisThreadPool;
|
|
|
|
DNASSERT( pContext != NULL );
|
|
pThisThreadPool = (CThreadPool*) pContext;
|
|
|
|
DPFX(DPFPREP, 5, "Threadpool 0x%p NAT Help Timer complete.", pThisThreadPool);
|
|
|
|
pThisThreadPool->m_fNATHelpTimerJobSubmitted = FALSE;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::NATHelpTimerFunction - NAT Help timer job needs service
|
|
//
|
|
// Entry: Pointer to context
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::NATHelpTimerFunction"
|
|
|
|
void CThreadPool::NATHelpTimerFunction( void * const pContext )
|
|
{
|
|
CThreadPool * pThisThreadPool;
|
|
|
|
|
|
DNASSERT( pContext != NULL );
|
|
pThisThreadPool = (CThreadPool*) pContext;
|
|
|
|
//
|
|
// Attempt to submit a blocking job to update the NAT capabilites. If it
|
|
// fails, we'll just try again later. It might also fail because a previous
|
|
// blocking job took so long that there's still a job scheduled in the queue
|
|
// already (we disallow duplicates).
|
|
//
|
|
pThisThreadPool->SubmitBlockingJob(CThreadPool::PerformSubsequentNATHelpGetCaps, pThisThreadPool);
|
|
}
|
|
//**********************************************************************
|
|
#endif // DPNBUILD_NONATHELP
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::AddSocketPort - add a socket to the Win9x watch list
|
|
//
|
|
// Entry: Pointer to SocketPort
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::AddSocketPort"
|
|
|
|
HRESULT CThreadPool::AddSocketPort( CSocketPort *const pSocketPort )
|
|
{
|
|
HRESULT hr;
|
|
BOOL fSocketAdded;
|
|
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Parameters: (0x%p)", this, pSocketPort);
|
|
DNASSERT( pSocketPort != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
fSocketAdded = FALSE;
|
|
|
|
Lock();
|
|
|
|
//
|
|
// We're capped by the number of sockets we can use for Winsock1. Make
|
|
// sure we don't allocate too many sockets.
|
|
//
|
|
if ( m_uReservedSocketCount == FD_SETSIZE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "There are too many sockets allocated on Winsock1!" );
|
|
goto Failure;
|
|
}
|
|
|
|
m_uReservedSocketCount++;
|
|
|
|
DNASSERT( m_SocketSet.fd_count < FD_SETSIZE );
|
|
m_pSocketPorts[ m_SocketSet.fd_count ] = pSocketPort;
|
|
m_SocketSet.fd_array[ m_SocketSet.fd_count ] = pSocketPort->GetSocket();
|
|
m_SocketSet.fd_count++;
|
|
fSocketAdded = TRUE;
|
|
|
|
//
|
|
// add a reference to note that this socket port is being used by the thread
|
|
// pool
|
|
//
|
|
pSocketPort->AddRef();
|
|
|
|
if (m_pvTimerDataWinsock1IO == NULL)
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
|
|
0, // use CPU 0, we shouldn't have multiple CPUs anyway
|
|
g_dwSelectTimePeriod, // delay
|
|
CThreadPool::CheckWinsock1IOCallback, // callback
|
|
this, // user context
|
|
&m_pvTimerDataWinsock1IO, // timer data (returned)
|
|
&m_uiTimerUniqueWinsock1IO, // timer unique (returned)
|
|
0); // flags
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't schedule Winsock 1 I/O poll timer!");
|
|
goto Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's possible there's still an outstanding lazy cancellation
|
|
// attempt. If so, don't try to cancel I/O anymore.
|
|
//
|
|
if (m_fCancelWinsock1IO)
|
|
{
|
|
DPFX(DPFPREP, 1, "Retracting lazy cancellation attempt.");
|
|
m_fCancelWinsock1IO = FALSE;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
Unlock();
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Return: [0x%08x]", this, hr);
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
if ( fSocketAdded != FALSE )
|
|
{
|
|
AssertCriticalSectionIsTakenByThisThread( &m_Lock, TRUE );
|
|
m_SocketSet.fd_count--;
|
|
m_pSocketPorts[ m_SocketSet.fd_count ] = NULL;
|
|
m_SocketSet.fd_array[ m_SocketSet.fd_count ] = NULL;
|
|
fSocketAdded = FALSE;
|
|
}
|
|
|
|
m_uReservedSocketCount--;
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::RemoveSocketPort - remove a socket from the Win9x watch list
|
|
//
|
|
// Entry: Pointer to socket port to remove
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::RemoveSocketPort"
|
|
|
|
void CThreadPool::RemoveSocketPort( CSocketPort *const pSocketPort )
|
|
{
|
|
UINT_PTR uIndex;
|
|
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Parameters: (0x%p)", this, pSocketPort);
|
|
DNASSERT( pSocketPort != NULL );
|
|
|
|
Lock();
|
|
|
|
uIndex = m_SocketSet.fd_count;
|
|
DNASSERT( uIndex != 0 );
|
|
|
|
//
|
|
// If this is the last socket, cancel the I/O timer.
|
|
//
|
|
if ( uIndex == 1 )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// Keep trying to cancel the Winsock1 timer. It may fail because it's in the
|
|
// process of actively executing, but we will set the cancel flag so that the
|
|
// timer will eventually notice.
|
|
//
|
|
DPFX(DPFPREP, 5, "Cancelling Winsock 1 I/O timer.");
|
|
DNASSERT(m_pvTimerDataWinsock1IO != NULL);
|
|
hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
|
|
m_pvTimerDataWinsock1IO,
|
|
m_uiTimerUniqueWinsock1IO,
|
|
0);
|
|
if (hr != DPN_OK)
|
|
{
|
|
DPFX(DPFPREP, 2, "Couldn't stop Winsock1 I/O timer, marking for lazy cancellation.");
|
|
m_fCancelWinsock1IO = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_pvTimerDataWinsock1IO = NULL;
|
|
}
|
|
}
|
|
|
|
while ( uIndex != 0 )
|
|
{
|
|
uIndex--;
|
|
|
|
if ( m_pSocketPorts[ uIndex ] == pSocketPort )
|
|
{
|
|
m_uReservedSocketCount--;
|
|
m_SocketSet.fd_count--;
|
|
|
|
memmove( &m_pSocketPorts[ uIndex ],
|
|
&m_pSocketPorts[ uIndex + 1 ],
|
|
( sizeof( m_pSocketPorts[ uIndex ] ) * ( m_SocketSet.fd_count - uIndex ) ) );
|
|
|
|
memmove( &m_SocketSet.fd_array[ uIndex ],
|
|
&m_SocketSet.fd_array[ uIndex + 1 ],
|
|
( sizeof( m_SocketSet.fd_array[ uIndex ] ) * ( m_SocketSet.fd_count - uIndex ) ) );
|
|
|
|
//
|
|
// clear last entry which is now unused
|
|
//
|
|
memset( &m_pSocketPorts[ m_SocketSet.fd_count ], 0x00, sizeof( m_pSocketPorts[ m_SocketSet.fd_count ] ) );
|
|
memset( &m_SocketSet.fd_array[ m_SocketSet.fd_count ], 0x00, sizeof( m_SocketSet.fd_array[ m_SocketSet.fd_count ] ) );
|
|
|
|
//
|
|
// end the loop
|
|
//
|
|
uIndex = 0;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
pSocketPort->DecRef();
|
|
|
|
DPFX(DPFPREP, 6, "(0x%p) Leave", this);
|
|
}
|
|
//**********************************************************************
|
|
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
|
|
|
|
|
|
#ifdef WIN95
|
|
#ifndef DPNBUILD_NOWINSOCK2
|
|
#endif // ! DPNBUILD_NOWINSOCK2
|
|
#endif // WIN95
|
|
|
|
|
|
#ifndef DPNBUILD_NONATHELP
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::HandleNATHelpUpdate - handle a NAT Help update event
|
|
//
|
|
// Entry: Timer interval if update is occurring periodically, or
|
|
// NULL if a triggered event.
|
|
// This function may take a while, because updating NAT Help
|
|
// can block.
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::HandleNATHelpUpdate"
|
|
|
|
void CThreadPool::HandleNATHelpUpdate( DWORD * const pdwTimerInterval )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwTemp;
|
|
DPNHCAPS dpnhcaps;
|
|
DWORD dwNATHelpRetryTime;
|
|
BOOL fModifiedRetryInterval;
|
|
DWORD dwFirstUpdateTime;
|
|
DWORD dwCurrentTime;
|
|
DWORD dwNumGetCaps = 0;
|
|
|
|
|
|
DNASSERT(IsNATHelpLoaded());
|
|
|
|
|
|
Lock();
|
|
|
|
//
|
|
// Prevent multiple threads from trying to update NAT Help status at the same
|
|
// time. If we're a duplicate, just bail.
|
|
//
|
|
|
|
if (m_dwNATHelpUpdateThreadID != 0)
|
|
{
|
|
DPFX(DPFPREP, 1, "Thread %u/0x%x already handling NAT Help update, not processing again (thread pool = 0x%p, timer = 0x%p).",
|
|
m_dwNATHelpUpdateThreadID, m_dwNATHelpUpdateThreadID, this, pdwTimerInterval);
|
|
|
|
Unlock();
|
|
|
|
return;
|
|
}
|
|
|
|
m_dwNATHelpUpdateThreadID = GetCurrentThreadId();
|
|
|
|
if (! m_fNATHelpTimerJobSubmitted)
|
|
{
|
|
DPFX(DPFPREP, 1, "Handling NAT Help update without a NAT refresh timer job submitted (thread pool = 0x%p).",
|
|
this);
|
|
DNASSERT(pdwTimerInterval == NULL);
|
|
}
|
|
|
|
Unlock();
|
|
|
|
|
|
DPFX(DPFPREP, 6, "Beginning thread pool 0x%p NAT Help update.", this);
|
|
|
|
|
|
//
|
|
// Initialize the timer values.
|
|
//
|
|
dwNATHelpRetryTime = -1;
|
|
dwFirstUpdateTime = GETTIMESTAMP() - 1; // longest possible time
|
|
|
|
|
|
for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
|
|
{
|
|
if (g_papNATHelpObjects[dwTemp] != NULL)
|
|
{
|
|
ZeroMemory(&dpnhcaps, sizeof(dpnhcaps));
|
|
dpnhcaps.dwSize = sizeof(dpnhcaps);
|
|
|
|
hr = IDirectPlayNATHelp_GetCaps(g_papNATHelpObjects[dwTemp],
|
|
&dpnhcaps,
|
|
DPNHGETCAPS_UPDATESERVERSTATUS);
|
|
switch (hr)
|
|
{
|
|
case DPNH_OK:
|
|
{
|
|
//
|
|
// See if this is the shortest interval.
|
|
//
|
|
if (dpnhcaps.dwRecommendedGetCapsInterval < dwNATHelpRetryTime)
|
|
{
|
|
dwNATHelpRetryTime = dpnhcaps.dwRecommendedGetCapsInterval;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DPNHSUCCESS_ADDRESSESCHANGED:
|
|
{
|
|
DPFX(DPFPREP, 1, "NAT Help index %u indicated public addresses changed.",
|
|
dwTemp);
|
|
|
|
//
|
|
// We don't actually store any public address information,
|
|
// we query it each time. Therefore we don't need to
|
|
// actually do anything with the change notification.
|
|
//
|
|
|
|
|
|
//
|
|
// See if this is the shortest interval.
|
|
//
|
|
if (dpnhcaps.dwRecommendedGetCapsInterval < dwNATHelpRetryTime)
|
|
{
|
|
dwNATHelpRetryTime = dpnhcaps.dwRecommendedGetCapsInterval;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DPNHERR_OUTOFMEMORY:
|
|
{
|
|
//
|
|
// This should generally only happen in stress. We'll
|
|
// continue on to other NAT help objects, and hope we
|
|
// aren't totally hosed.
|
|
//
|
|
|
|
DPFX(DPFPREP, 0, "NAT Help index %u returned out-of-memory error! Continuing.",
|
|
dwTemp);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// Some other unknown error occurred. Ignore it.
|
|
//
|
|
|
|
DPFX(DPFPREP, 0, "NAT Help index %u returned unknown error 0x%lx! Continuing.",
|
|
dwTemp, hr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Save the current time, if this is the first GetCaps.
|
|
//
|
|
if (dwNumGetCaps == 0)
|
|
{
|
|
dwFirstUpdateTime = GETTIMESTAMP();
|
|
}
|
|
|
|
dwNumGetCaps++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No DPNATHelp object in that slot.
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Assert that at least one NAT Help object is loaded.
|
|
//
|
|
DNASSERT(dwNumGetCaps > 0);
|
|
|
|
|
|
|
|
dwCurrentTime = GETTIMESTAMP();
|
|
|
|
//
|
|
// We may need to make some adjustments to the timer. Either subtract out time
|
|
// we've spent fiddling around with other NAT Help interfaces, or make sure the
|
|
// time isn't really large because that can screw up time calculations. It's
|
|
// not a big deal to wake up after 24 days even though we wouldn't need to
|
|
// according to the logic above.
|
|
//
|
|
if (dwNATHelpRetryTime & 0x80000000)
|
|
{
|
|
DPFX(DPFPREP, 3, "NAT Help refresh timer for thread pool 0x%p is set to longest possible without going negative.",
|
|
this);
|
|
|
|
dwNATHelpRetryTime = 0x7FFFFFFF;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwTimeElapsed;
|
|
|
|
|
|
//
|
|
// Find out how much time has elapsed since the first GetCaps completed.
|
|
//
|
|
dwTimeElapsed = dwCurrentTime - dwFirstUpdateTime;
|
|
|
|
//
|
|
// Remove it from the retry interval, unless it's already overdue.
|
|
//
|
|
if (dwTimeElapsed < dwNATHelpRetryTime)
|
|
{
|
|
dwNATHelpRetryTime -= dwTimeElapsed;
|
|
}
|
|
else
|
|
{
|
|
dwNATHelpRetryTime = 0; // shortest time possible
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Modify the next time when we should refresh the NAT Help information based
|
|
// on the reported recommendation.
|
|
//
|
|
if (pdwTimerInterval != NULL)
|
|
{
|
|
DPFX(DPFPREP, 6, "Modifying NAT Help refresh timer for thread pool 0x%p in place (was %u ms, changing to %u).",
|
|
this, (*pdwTimerInterval), dwNATHelpRetryTime);
|
|
|
|
(*pdwTimerInterval) = dwNATHelpRetryTime;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add the interval to the current time to find the new retry time.
|
|
//
|
|
dwCurrentTime += dwNATHelpRetryTime;
|
|
|
|
|
|
DPFX(DPFPREP, 6, "Modifying NAT Help refresh timer for thread pool 0x%p to run at offset %u (in %u ms).",
|
|
this, dwCurrentTime, dwNATHelpRetryTime);
|
|
|
|
|
|
//
|
|
// Try to modify the existing timer job. There are two race conditions:
|
|
// 1) the recurring timer that triggered this refresh (which occurs on a
|
|
// separate non-blockable thread) has not rescheduled itself yet. It
|
|
// should pick up the new timer setting we're about to apply when it
|
|
// eventually does reschedule. ModifyTimerJobNextRetryTime should
|
|
// succeed.
|
|
// 2) the NAT help timer is being killed. ModifyTimerJobNextRetryTime
|
|
// will fail, but there's nothing we can or should do.
|
|
//
|
|
fModifiedRetryInterval = ModifyTimerJobNextRetryTime(this, dwCurrentTime);
|
|
if (! fModifiedRetryInterval)
|
|
{
|
|
DPFX(DPFPREP, 1, "Unable to modify NAT Help refresh timer (thread pool 0x%p), timer should be cancelled.",
|
|
this);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Now that we're done handling the update, let other threads do what they
|
|
// want.
|
|
//
|
|
Lock();
|
|
DNASSERT(m_dwNATHelpUpdateThreadID == GetCurrentThreadId());
|
|
m_dwNATHelpUpdateThreadID = 0;
|
|
Unlock();
|
|
|
|
}
|
|
//**********************************************************************
|
|
#endif // DPNBUILD_NONATHELP
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_NOSPUI
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::DialogThreadProc - thread proc for spawning dialogs
|
|
//
|
|
// Entry: Pointer to startup parameter
|
|
//
|
|
// Exit: Error Code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::DialogThreadProc"
|
|
|
|
DWORD WINAPI CThreadPool::DialogThreadProc( void *pParam )
|
|
{
|
|
const DIALOG_THREAD_PARAM *pThreadParam;
|
|
BOOL fComInitialized;
|
|
|
|
|
|
//
|
|
// Initialize COM. If this fails, we'll have problems later.
|
|
//
|
|
fComInitialized = FALSE;
|
|
switch ( COM_CoInitialize( NULL ) )
|
|
{
|
|
case S_OK:
|
|
{
|
|
fComInitialized = TRUE;
|
|
break;
|
|
}
|
|
|
|
case S_FALSE:
|
|
{
|
|
DNASSERT( FALSE );
|
|
fComInitialized = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM init failed!
|
|
//
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to initialize COM!" );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
DNASSERT( pParam != NULL );
|
|
pThreadParam = static_cast<DIALOG_THREAD_PARAM*>( pParam );
|
|
|
|
pThreadParam->pDialogFunction( pThreadParam->pContext );
|
|
|
|
DNFree( pParam );
|
|
|
|
if ( fComInitialized != FALSE )
|
|
{
|
|
COM_CoUninitialize();
|
|
fComInitialized = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//**********************************************************************
|
|
#endif // ! DPNBUILD_NOSPUI
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::CheckWinsock1IO - check the IO status for Winsock1 sockets
|
|
//
|
|
// Entry: Pointer to sockets to watch
|
|
//
|
|
// Exit: Boolean indicating whether I/O was serviced
|
|
// TRUE = I/O serviced
|
|
// FALSE = I/O not serviced
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::CheckWinsock1IO"
|
|
|
|
BOOL CThreadPool::CheckWinsock1IO( FD_SET *const pWinsock1Sockets )
|
|
{
|
|
#ifdef DPNBUILD_NONEWTHREADPOOL
|
|
static const TIMEVAL SelectTime = { 0, 0 }; // zero, do an instant check
|
|
#else // ! DPNBUILD_NONEWTHREADPOOL
|
|
TIMEVAL SelectTime = { 0, (g_dwSelectTimeSlice * 1000)}; // convert ms into microseconds
|
|
#endif // ! DPNBUILD_NONEWTHREADPOOL
|
|
BOOL fIOServiced;
|
|
INT iSelectReturn;
|
|
FD_SET ReadSocketSet;
|
|
FD_SET ErrorSocketSet;
|
|
|
|
|
|
//
|
|
// Make a local copy of all of the sockets. This isn't totally
|
|
// efficient, but it works. Multiplying by active socket count will
|
|
// spend half the time in the integer multiply.
|
|
//
|
|
fIOServiced = FALSE;
|
|
Lock();
|
|
|
|
if (m_fCancelWinsock1IO)
|
|
{
|
|
DPFX(DPFPREP, 1, "Detected Winsock 1 I/O cancellation, aborting.");
|
|
Unlock();
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy( &ReadSocketSet, pWinsock1Sockets, sizeof( ReadSocketSet ) );
|
|
memcpy( &ErrorSocketSet, pWinsock1Sockets, sizeof( ErrorSocketSet ) );
|
|
Unlock();
|
|
|
|
//
|
|
// Don't check write sockets here because it's very likely that they're ready
|
|
// for service but have no outgoing data and will thrash
|
|
//
|
|
iSelectReturn = select( 0, // compatibility parameter (ignored)
|
|
&ReadSocketSet, // sockets to check for read
|
|
NULL, // sockets to check for write (none)
|
|
&ErrorSocketSet, // sockets to check for error
|
|
&SelectTime // wait timeout
|
|
);
|
|
switch ( iSelectReturn )
|
|
{
|
|
//
|
|
// timeout
|
|
//
|
|
case 0:
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// select got pissed
|
|
//
|
|
case SOCKET_ERROR:
|
|
{
|
|
DWORD dwWSAError;
|
|
|
|
|
|
dwWSAError = WSAGetLastError();
|
|
switch ( dwWSAError )
|
|
{
|
|
//
|
|
// WSAENOTSOCK = This socket was probably closed
|
|
//
|
|
case WSAENOTSOCK:
|
|
{
|
|
DPFX(DPFPREP, 1, "Winsock1 reporting 'Not a socket' when selecting read or error sockets!" );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// WSAEINTR = this operation was interrupted
|
|
//
|
|
case WSAEINTR:
|
|
{
|
|
DPFX(DPFPREP, 1, "Winsock1 reporting interrupted operation when selecting read or error sockets!" );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other
|
|
//
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem selecting read or error sockets for service!" );
|
|
DisplayWinsockError( 0, dwWSAError );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for sockets needing read service and error service.
|
|
//
|
|
default:
|
|
{
|
|
fIOServiced |= ServiceWinsock1Sockets( &ReadSocketSet, CSocketPort::Winsock1ReadService );
|
|
fIOServiced |= ServiceWinsock1Sockets( &ErrorSocketSet, CSocketPort::Winsock1ErrorService );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fIOServiced;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::ServiceWinsock1Sockets - service requests on Winsock1 sockets ports
|
|
//
|
|
// Entry: Pointer to set of sockets
|
|
// Pointer to service function
|
|
//
|
|
// Exit: Boolean indicating whether I/O was serviced
|
|
// TRUE = I/O serviced
|
|
// FALSE = I/O not serviced
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::ServiceWinsock1Sockets"
|
|
|
|
BOOL CThreadPool::ServiceWinsock1Sockets( FD_SET *pSocketSet, PSOCKET_SERVICE_FUNCTION pServiceFunction )
|
|
{
|
|
BOOL fReturn;
|
|
UINT_PTR uWaitingSocketCount;
|
|
UINT_PTR uSocketPortCount;
|
|
CSocketPort *pSocketPorts[ FD_SETSIZE ];
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fReturn = FALSE;
|
|
uSocketPortCount = 0;
|
|
uWaitingSocketCount = pSocketSet->fd_count;
|
|
|
|
Lock();
|
|
while ( uWaitingSocketCount > 0 )
|
|
{
|
|
UINT_PTR uIdx;
|
|
|
|
|
|
uWaitingSocketCount--;
|
|
uIdx = m_SocketSet.fd_count;
|
|
while ( uIdx != 0 )
|
|
{
|
|
uIdx--;
|
|
if ( __WSAFDIsSet( m_SocketSet.fd_array[ uIdx ], pSocketSet ) != FALSE )
|
|
{
|
|
//
|
|
// this socket is still available, add a reference to the socket
|
|
// port and keep it around to be processed outside of the lock
|
|
//
|
|
pSocketPorts[ uSocketPortCount ] = m_pSocketPorts[ uIdx ];
|
|
pSocketPorts[ uSocketPortCount ]->AddRef();
|
|
uSocketPortCount++;
|
|
uIdx = 0;
|
|
}
|
|
}
|
|
}
|
|
Unlock();
|
|
|
|
while ( uSocketPortCount != 0 )
|
|
{
|
|
uSocketPortCount--;
|
|
|
|
//
|
|
// call the service function and remove the reference
|
|
//
|
|
fReturn |= (pSocketPorts[ uSocketPortCount ]->*pServiceFunction)();
|
|
pSocketPorts[ uSocketPortCount ]->DecRef();
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
//**********************************************************************
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::SubmitDelayedCommand - submit request to perform work in another thread
|
|
//
|
|
// Entry: CPU index (non DPNBUILD_ONLYONEPROCESSOR builds only)
|
|
// Pointer to callback function
|
|
// Pointer to callback context
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::SubmitDelayedCommand"
|
|
|
|
#ifdef DPNBUILD_ONLYONEPROCESSOR
|
|
HRESULT CThreadPool::SubmitDelayedCommand( const PFNDPTNWORKCALLBACK pFunction,
|
|
void *const pContext )
|
|
{
|
|
return IDirectPlay8ThreadPoolWork_QueueWorkItem(m_pDPThreadPoolWork,
|
|
-1,
|
|
pFunction,
|
|
pContext,
|
|
0);
|
|
}
|
|
#else // ! DPNBUILD_ONLYONEPROCESSOR
|
|
HRESULT CThreadPool::SubmitDelayedCommand( const DWORD dwCPU,
|
|
const PFNDPTNWORKCALLBACK pFunction,
|
|
void *const pContext )
|
|
{
|
|
return IDirectPlay8ThreadPoolWork_QueueWorkItem(m_pDPThreadPoolWork,
|
|
dwCPU,
|
|
pFunction,
|
|
pContext,
|
|
0);
|
|
}
|
|
#endif // ! DPNBUILD_ONLYONEPROCESSOR
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::GenericTimerCallback - generic timer callback
|
|
//
|
|
// Entry: Pointer to callback context
|
|
// Pointer to timer data
|
|
// Pointer to timer uniqueness value
|
|
//
|
|
// Exit: None
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::GenericTimerCallback"
|
|
|
|
void WINAPI CThreadPool::GenericTimerCallback( void * const pvContext,
|
|
void * const pvTimerData,
|
|
const UINT uiTimerUnique )
|
|
{
|
|
TIMER_OPERATION_ENTRY * pTimerEntry;
|
|
CThreadPool * pThisThreadPool;
|
|
DWORD dwCurrentTime;
|
|
HRESULT hr;
|
|
DWORD dwNewTimerDelay;
|
|
#ifdef DBG
|
|
DWORD dwNextRetryTime;
|
|
#endif // DBG
|
|
|
|
|
|
pTimerEntry = (TIMER_OPERATION_ENTRY*) pvContext;
|
|
DNASSERT((pvTimerData == pTimerEntry->pvTimerData) || (pTimerEntry->pvTimerData == NULL));
|
|
DNASSERT((uiTimerUnique == pTimerEntry->uiTimerUnique) || (pTimerEntry->uiTimerUnique == 0));
|
|
|
|
pThisThreadPool = pTimerEntry->pThreadPool;
|
|
|
|
|
|
//
|
|
// Process the timer, unless we just went through the idle timeout.
|
|
//
|
|
if (pTimerEntry->uRetryCount != 0)
|
|
{
|
|
dwCurrentTime = GETTIMESTAMP();
|
|
|
|
#ifdef DBG
|
|
dwNextRetryTime = pTimerEntry->dwNextRetryTime; // copy since lock is not held
|
|
if ((int) (dwNextRetryTime - dwCurrentTime) <= 0)
|
|
{
|
|
//
|
|
// Timer expired and has not been rescheduled yet.
|
|
//
|
|
DPFX(DPFPREP, 7, "Threadpool 0x%p performing timed job 0x%p approximately %u ms after intended time of %u.",
|
|
pThisThreadPool, pTimerEntry, (dwCurrentTime - dwNextRetryTime), pTimerEntry->dwNextRetryTime);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Another thread may have modified the timer already.
|
|
//
|
|
DPFX(DPFPREP, 7, "Threadpool 0x%p performing timed job 0x%p (next time already modified to be %u).",
|
|
pThisThreadPool, pTimerEntry, dwNextRetryTime);
|
|
}
|
|
#endif // DBG
|
|
|
|
//
|
|
// Execute this timed item.
|
|
//
|
|
pTimerEntry->pTimerCallback(pTimerEntry->pContext);
|
|
|
|
//
|
|
// Reschedule the job, unless it was *just* cancelled, or it's idling
|
|
// forever.
|
|
//
|
|
pThisThreadPool->LockTimerData();
|
|
if (pTimerEntry->fCancelling)
|
|
{
|
|
DPFX(DPFPREP, 5, "Timer 0x%p was just cancelled, completing.", pTimerEntry);
|
|
|
|
pTimerEntry->Linkage.RemoveFromList();
|
|
pThisThreadPool->UnlockTimerData();
|
|
|
|
pTimerEntry->pTimerComplete(DPNERR_USERCANCEL, pTimerEntry->pContext);
|
|
g_TimerEntryPool.Release(pTimerEntry);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If this job isn't running forever, decrement the retry count.
|
|
// If there are no more retries, set up the idle timer.
|
|
//
|
|
if ( pTimerEntry->fRetryForever == FALSE )
|
|
{
|
|
pTimerEntry->uRetryCount--;
|
|
if ( pTimerEntry->uRetryCount == 0 )
|
|
{
|
|
if ( pTimerEntry->fIdleWaitForever == FALSE )
|
|
{
|
|
//
|
|
// Compute stopping time for this job's 'Timeout' phase.
|
|
//
|
|
dwNewTimerDelay = pTimerEntry->dwIdleTimeout;
|
|
pTimerEntry->dwIdleTimeout += dwCurrentTime;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're waiting forever for enum returns. ASSERT that we
|
|
// have the maximum timeout.
|
|
//
|
|
DNASSERT( pTimerEntry->dwIdleTimeout == -1 );
|
|
|
|
//
|
|
// Set this value to avoid a false PREfast warning.
|
|
//
|
|
dwNewTimerDelay = 0;
|
|
}
|
|
|
|
goto SkipNextRetryTimeComputation;
|
|
}
|
|
} // end if (don't retry forever)
|
|
|
|
dwNewTimerDelay = pTimerEntry->dwRetryInterval;
|
|
pTimerEntry->dwNextRetryTime = dwCurrentTime + pTimerEntry->dwRetryInterval;
|
|
|
|
SkipNextRetryTimeComputation:
|
|
|
|
if ((! pTimerEntry->fIdleWaitForever) ||
|
|
(pTimerEntry->uRetryCount > 0))
|
|
{
|
|
//
|
|
// Make sure we aren't trying to schedule something too far in
|
|
// the future or backward in time. If we are, we'll just force
|
|
// the timer to expire earlier.
|
|
//
|
|
if ((int) dwNewTimerDelay < 0)
|
|
{
|
|
DNASSERT(! "Job time is unexpectedly long or backward in time!");
|
|
dwNewTimerDelay = 0x7FFFFFFF;
|
|
}
|
|
|
|
hr = IDirectPlay8ThreadPoolWork_ResetCompletingTimer(pThisThreadPool->m_pDPThreadPoolWork,
|
|
pvTimerData, // timer data
|
|
dwNewTimerDelay, // delay
|
|
CThreadPool::GenericTimerCallback, // callback
|
|
pTimerEntry, // user context
|
|
&pTimerEntry->uiTimerUnique, // new timer uniqueness value
|
|
0); // flags
|
|
DNASSERT(hr == DPN_OK);
|
|
pTimerEntry->pvTimerData = pvTimerData; // ensure that we remember the timer handle
|
|
pThisThreadPool->UnlockTimerData();
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 5, "Timer 0x%p now idling forever.", pTimerEntry);
|
|
|
|
pTimerEntry->pvTimerData = NULL;
|
|
pThisThreadPool->UnlockTimerData();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT((int) (pTimerEntry->dwIdleTimeout - GETTIMESTAMP()) <= 0);
|
|
|
|
//
|
|
// Remove this link from the list, tell owner that the job is
|
|
// complete and return the job to the pool.
|
|
//
|
|
pThisThreadPool->LockTimerData();
|
|
pTimerEntry->Linkage.RemoveFromList();
|
|
pThisThreadPool->UnlockTimerData();
|
|
pTimerEntry->pTimerComplete(DPN_OK, pTimerEntry->pContext);
|
|
g_TimerEntryPool.Release(pTimerEntry);
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYWINSOCK2
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CThreadPool::CheckWinsock1IOCallback - Winsock1 I/O servicing callback
|
|
//
|
|
// Pointer to timer data
|
|
// Pointer to timer uniqueness value
|
|
// Entry: Pointer to callback context
|
|
//
|
|
// Exit: None
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CThreadPool::CheckWinsock1IOCallback"
|
|
|
|
void WINAPI CThreadPool::CheckWinsock1IOCallback( void * const pvContext,
|
|
void * const pvTimerData,
|
|
const UINT uiTimerUnique )
|
|
{
|
|
CThreadPool * pThisThreadPool = (CThreadPool*) pvContext;
|
|
BOOL fResult;
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// Service all Winsock1 I/O possible.
|
|
//
|
|
do
|
|
{
|
|
fResult = pThisThreadPool->CheckWinsock1IO(&pThisThreadPool->m_SocketSet);
|
|
}
|
|
while (fResult);
|
|
|
|
|
|
//
|
|
// Schedule the timer again, unless we're cancelling it.
|
|
//
|
|
pThisThreadPool->Lock();
|
|
|
|
DNASSERT(pvTimerData == pThisThreadPool->m_pvTimerDataWinsock1IO);
|
|
DNASSERT(uiTimerUnique == pThisThreadPool->m_uiTimerUniqueWinsock1IO);
|
|
|
|
if (! pThisThreadPool->m_fCancelWinsock1IO)
|
|
{
|
|
hr = IDirectPlay8ThreadPoolWork_ResetCompletingTimer(pThisThreadPool->m_pDPThreadPoolWork,
|
|
pvTimerData, // timer data
|
|
g_dwSelectTimePeriod, // delay
|
|
CThreadPool::CheckWinsock1IOCallback, // callback
|
|
pThisThreadPool, // user context
|
|
&pThisThreadPool->m_uiTimerUniqueWinsock1IO, // updated timer uniqueness value
|
|
0); // flags
|
|
DNASSERT(hr == DPN_OK);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 1, "Not resubmitting Winsock1 I/O timer due to cancellation.");
|
|
|
|
pThisThreadPool->m_fCancelWinsock1IO = FALSE;
|
|
pThisThreadPool->m_pvTimerDataWinsock1IO = NULL;
|
|
}
|
|
|
|
pThisThreadPool->Unlock();
|
|
}
|
|
//**********************************************************************
|
|
|
|
#endif // ! DPNBUILD_ONLYWINSOCK2
|
|
|
|
|
|
|
|
#ifndef DPNBUILD_ONLYONETHREAD
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// DPNBlockingJobThreadProc - thread procedure for executing blocking jobs
|
|
//
|
|
// Entry: Parameter
|
|
//
|
|
// Exit: Result code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DPNBlockingJobThreadProc"
|
|
|
|
DWORD WINAPI DPNBlockingJobThreadProc(PVOID pvParameter)
|
|
{
|
|
HRESULT hr;
|
|
CThreadPool * pThisThreadPool;
|
|
BOOL fUninitializeCOM = TRUE;
|
|
|
|
|
|
DPFX(DPFPREP, 5, "Parameters: (0x%p)", pvParameter);
|
|
|
|
pThisThreadPool = (CThreadPool*) pvParameter;
|
|
|
|
//
|
|
// Init COM.
|
|
//
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to initialize COM (err = 0x%lx)! Continuing.", hr);
|
|
fUninitializeCOM = FALSE;
|
|
|
|
//
|
|
// Continue...
|
|
//
|
|
}
|
|
|
|
//
|
|
// Process all jobs until we're told to quit.
|
|
//
|
|
pThisThreadPool->DoBlockingJobs();
|
|
|
|
if (fUninitializeCOM)
|
|
{
|
|
CoUninitialize();
|
|
fUninitializeCOM = FALSE;
|
|
}
|
|
|
|
DPFX(DPFPREP, 5, "Leave");
|
|
|
|
return 0;
|
|
}
|
|
//**********************************************************************
|
|
|
|
#endif // ! DPNBUILD_ONLYONETHREAD
|
|
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// TimerEntry_Alloc - allocate a new timer job entry
|
|
//
|
|
// Entry: Pointer to new entry
|
|
//
|
|
// Exit: Boolean indicating success
|
|
// TRUE = initialization successful
|
|
// FALSE = initialization failed
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "TimerEntry_Alloc"
|
|
|
|
BOOL TimerEntry_Alloc( void *pvItem, void* pvContext )
|
|
{
|
|
TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
|
|
DEBUG_ONLY( memset( pTimerEntry, 0x00, sizeof( *pTimerEntry ) ) );
|
|
pTimerEntry->Linkage.Initialize();
|
|
|
|
return TRUE;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// TimerEntry_Get - get new timer job entry from pool
|
|
//
|
|
// Entry: Pointer to new entry
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "TimerEntry_Get"
|
|
|
|
void TimerEntry_Get( void *pvItem, void* pvContext )
|
|
{
|
|
#ifdef DBG
|
|
const TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
DNASSERT( pTimerEntry->pContext == NULL );
|
|
DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
|
|
#endif // DBG
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// TimerEntry_Release - return timer job entry to pool
|
|
//
|
|
// Entry: Pointer to entry
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "TimerEntry_Release"
|
|
void TimerEntry_Release( void *pvItem )
|
|
{
|
|
TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
|
|
|
|
pTimerEntry->pContext= NULL;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// TimerEntry_Dealloc - deallocate a timer job entry
|
|
//
|
|
// Entry: Pointer to entry
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "TimerEntry_Dealloc"
|
|
void TimerEntry_Dealloc( void *pvItem )
|
|
{
|
|
#ifdef DBG
|
|
const TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
|
|
DNASSERT( pTimerEntry->pContext == NULL );
|
|
#endif // DBG
|
|
}
|
|
//**********************************************************************
|
|
|