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.
4186 lines
106 KiB
4186 lines
106 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 1998-2000 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: ThreadPool.cpp
|
|
* Content: main job thread pool
|
|
*
|
|
*
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 11/25/98 jtk Created
|
|
***************************************************************************/
|
|
|
|
#include "dnmdmi.h"
|
|
|
|
|
|
#undef DPF_SUBCOMP
|
|
#define DPF_SUBCOMP DN_SUBCOMP_MODEM
|
|
|
|
//**********************************************************************
|
|
// Constant definitions
|
|
//**********************************************************************
|
|
|
|
//
|
|
// events for threads
|
|
//
|
|
enum
|
|
{
|
|
EVENT_INDEX_STOP_ALL_THREADS = 0,
|
|
EVENT_INDEX_PENDING_JOB = 1,
|
|
EVENT_INDEX_WAKE_NT_TIMER_THREAD = 1,
|
|
EVENT_INDEX_SEND_COMPLETE = 2,
|
|
EVENT_INDEX_RECEIVE_COMPLETE = 3,
|
|
EVENT_INDEX_TAPI_MESSAGE = 4,
|
|
|
|
EVENT_INDEX_MAX
|
|
};
|
|
|
|
//
|
|
// times to wait in milliseconds when polling for work thread shutdown
|
|
//
|
|
#define WORK_THREAD_CLOSE_WAIT_TIME 3000
|
|
#define WORK_THREAD_CLOSE_SLEEP_TIME 100
|
|
|
|
//**********************************************************************
|
|
// Macro definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Structure definitions
|
|
//**********************************************************************
|
|
|
|
//
|
|
// structure for common data in Win9x thread
|
|
//
|
|
typedef struct _WIN9X_CORE_DATA
|
|
{
|
|
DWORD dwNextTimerJobTime; // time when the next timer job needs service
|
|
DNHANDLE hWaitHandles[ EVENT_INDEX_MAX ]; // handles for waiting on
|
|
DWORD dwWaitHandleCount; // count of handles to wait on
|
|
DWORD dwTimeToNextJob; // time to next job
|
|
BOOL fTimerJobsActive; // Boolean indicating that there are active jobs
|
|
BOOL fLooping; // Boolean indicating that this thread is still active
|
|
|
|
} WIN9X_CORE_DATA;
|
|
|
|
//
|
|
// information passed to the Win9x workhorse thread
|
|
//
|
|
typedef struct _WIN9X_THREAD_DATA
|
|
{
|
|
CModemThreadPool *pThisThreadPool; // pointer to this object
|
|
} WIN9X_THREAD_DATA;
|
|
|
|
//
|
|
// information passed to the IOCompletion thread
|
|
//
|
|
typedef struct _IOCOMPLETION_THREAD_DATA
|
|
{
|
|
CModemThreadPool *pThisThreadPool; // pointer to this object
|
|
} IOCOMPLETION_THREAD_DATA;
|
|
|
|
//
|
|
// structure passed to dialog threads
|
|
//
|
|
typedef struct _DIALOG_THREAD_PARAM
|
|
{
|
|
DIALOG_FUNCTION *pDialogFunction;
|
|
void *pContext;
|
|
CModemThreadPool *pThisThreadPool;
|
|
} DIALOG_THREAD_PARAM;
|
|
|
|
//**********************************************************************
|
|
// Variable definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function prototypes
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function definitions
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::PoolAllocFunction
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::PoolAllocFunction"
|
|
|
|
BOOL CModemThreadPool::PoolAllocFunction( void* pvItem, void* pvContext )
|
|
{
|
|
CModemThreadPool* pThreadPool = (CModemThreadPool*)pvItem;
|
|
|
|
pThreadPool->m_iTotalThreadCount = 0;
|
|
#ifdef WINNT
|
|
pThreadPool->m_iNTCompletionThreadCount = 0;
|
|
pThreadPool->m_fNTTimerThreadRunning = FALSE;
|
|
pThreadPool->m_hIOCompletionPort = NULL;
|
|
#endif // WINNT
|
|
pThreadPool->m_fAllowThreadCountReduction = FALSE;
|
|
pThreadPool->m_iIntendedThreadCount = 0;
|
|
pThreadPool->m_hStopAllThreads = NULL;
|
|
#ifdef WIN95
|
|
pThreadPool->m_hSendComplete = NULL;
|
|
pThreadPool->m_hReceiveComplete = NULL;
|
|
pThreadPool->m_hTAPIEvent = NULL;
|
|
pThreadPool->m_hFakeTAPIEvent = NULL;
|
|
#endif // WIN95
|
|
pThreadPool->m_fTAPIAvailable = FALSE;
|
|
pThreadPool->m_iRefCount = 0;
|
|
|
|
pThreadPool->m_Sig[0] = 'T';
|
|
pThreadPool->m_Sig[1] = 'H';
|
|
pThreadPool->m_Sig[2] = 'P';
|
|
pThreadPool->m_Sig[3] = 'L';
|
|
|
|
pThreadPool->m_OutstandingReadList.Initialize();
|
|
pThreadPool->m_OutstandingWriteList.Initialize();
|
|
memset( &pThreadPool->m_InitFlags, 0x00, sizeof( pThreadPool->m_InitFlags ) );
|
|
memset( &pThreadPool->m_TAPIInfo, 0x00, sizeof( pThreadPool->m_TAPIInfo ) );
|
|
pThreadPool->m_TimerJobList.Initialize();
|
|
|
|
return TRUE;
|
|
}
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::PoolInitFunction
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::PoolInitFunction"
|
|
|
|
void CModemThreadPool::PoolInitFunction( void* pvItem, void* pvContext )
|
|
{
|
|
CModemThreadPool* pThreadPool = (CModemThreadPool*)pvItem;
|
|
|
|
DNASSERT(pThreadPool->m_iRefCount == 0);
|
|
pThreadPool->m_iRefCount = 1;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::PoolDeallocFunction
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::PoolDeallocFunction"
|
|
|
|
void CModemThreadPool::PoolDeallocFunction( void* pvItem )
|
|
{
|
|
const CModemThreadPool* pThreadPool = (CModemThreadPool*)pvItem;
|
|
|
|
DNASSERT( pThreadPool->m_iTotalThreadCount == 0 );
|
|
#ifdef WINNT
|
|
DNASSERT( pThreadPool->m_iNTCompletionThreadCount == 0 );
|
|
DNASSERT( pThreadPool->m_fNTTimerThreadRunning == FALSE );
|
|
DNASSERT( pThreadPool->m_hIOCompletionPort == NULL );
|
|
#endif // WINNT
|
|
DNASSERT( pThreadPool->m_fAllowThreadCountReduction == FALSE );
|
|
DNASSERT( pThreadPool->m_iIntendedThreadCount == 0 );
|
|
DNASSERT( pThreadPool->m_hStopAllThreads == NULL );
|
|
#ifdef WIN95
|
|
DNASSERT( pThreadPool->m_hSendComplete == NULL );
|
|
DNASSERT( pThreadPool->m_hReceiveComplete == NULL );
|
|
DNASSERT( pThreadPool->m_hTAPIEvent == NULL );
|
|
DNASSERT( pThreadPool->m_hFakeTAPIEvent == NULL );
|
|
#endif // WIN95
|
|
DNASSERT( pThreadPool->m_fTAPIAvailable == FALSE );
|
|
|
|
DNASSERT( pThreadPool->m_OutstandingReadList.IsEmpty() != FALSE );
|
|
DNASSERT( pThreadPool->m_OutstandingReadList.IsEmpty() != FALSE );
|
|
DNASSERT( pThreadPool->m_TimerJobList.IsEmpty() != FALSE );
|
|
|
|
DNASSERT( pThreadPool->m_InitFlags.fTAPILoaded == FALSE );
|
|
DNASSERT( pThreadPool->m_InitFlags.fLockInitialized == FALSE );
|
|
DNASSERT( pThreadPool->m_InitFlags.fIODataLockInitialized == FALSE );
|
|
DNASSERT( pThreadPool->m_InitFlags.fJobDataLockInitialized == FALSE );
|
|
DNASSERT( pThreadPool->m_InitFlags.fTimerDataLockInitialized == FALSE );
|
|
DNASSERT( pThreadPool->m_InitFlags.fJobQueueInitialized == FALSE );
|
|
DNASSERT( pThreadPool->m_iRefCount == 0 );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::Initialize - initialize work threads
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Boolean indicating success
|
|
// TRUE = success
|
|
// FALSE = failure
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::Initialize"
|
|
|
|
BOOL CModemThreadPool::Initialize( void )
|
|
{
|
|
HRESULT hTempResult;
|
|
BOOL fReturn;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fReturn = TRUE;
|
|
DNASSERT( m_InitFlags.fTAPILoaded == FALSE );
|
|
DNASSERT( m_InitFlags.fLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fIODataLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fJobDataLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fTimerDataLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fDataPortHandleTableInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fJobQueueInitialized == FALSE );
|
|
|
|
//
|
|
// try to load TAPI before anything else
|
|
//
|
|
hTempResult = LoadTAPILibrary();
|
|
if ( hTempResult == DPN_OK )
|
|
{
|
|
m_InitFlags.fTAPILoaded = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to load TAPI!" );
|
|
DisplayDNError( 0, hTempResult );
|
|
}
|
|
|
|
//
|
|
// initialize critical sections
|
|
//
|
|
if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
|
|
{
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_Lock, 0 );
|
|
DebugSetCriticalSectionGroup( &m_Lock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
|
|
m_InitFlags.fLockInitialized = TRUE;
|
|
|
|
//
|
|
// Win9x has poor APC support and as part of the workaround, the read and
|
|
// write data locks need to be taken twice. Adjust the recursion counts
|
|
// accordingly.
|
|
//
|
|
if ( DNInitializeCriticalSection( &m_IODataLock ) == FALSE )
|
|
{
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_IODataLock, 1 );
|
|
DebugSetCriticalSectionGroup( &m_IODataLock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
|
|
m_InitFlags.fIODataLockInitialized = TRUE;
|
|
|
|
if ( DNInitializeCriticalSection( &m_JobDataLock ) == FALSE )
|
|
{
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_JobDataLock, 0 );
|
|
DebugSetCriticalSectionGroup( &m_JobDataLock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
|
|
m_InitFlags.fJobDataLockInitialized = TRUE;
|
|
|
|
if ( DNInitializeCriticalSection( &m_TimerDataLock ) == FALSE )
|
|
{
|
|
goto Failure;
|
|
}
|
|
DebugSetCriticalSectionRecursionCount( &m_TimerDataLock, 0 );
|
|
DebugSetCriticalSectionGroup( &m_TimerDataLock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
|
|
m_InitFlags.fTimerDataLockInitialized = TRUE;
|
|
|
|
//
|
|
// handle table
|
|
//
|
|
if ( m_DataPortHandleTable.Initialize() != DPN_OK )
|
|
{
|
|
goto Failure;
|
|
}
|
|
m_InitFlags.fDataPortHandleTableInitialized = TRUE;
|
|
|
|
//
|
|
// initialize job queue
|
|
//
|
|
if ( m_JobQueue.Initialize() == FALSE )
|
|
{
|
|
goto Failure;
|
|
}
|
|
m_InitFlags.fJobQueueInitialized = TRUE;
|
|
|
|
//
|
|
// Create event to stop all threads. Win9x needs this to stop processing
|
|
// and the NT enum thread uses this to stop processing
|
|
//
|
|
DNASSERT( m_hStopAllThreads == NULL );
|
|
m_hStopAllThreads = DNCreateEvent( NULL, // pointer to security (none)
|
|
TRUE, // manual reset
|
|
FALSE, // start unsignalled
|
|
NULL ); // pointer to name (none)
|
|
if ( m_hStopAllThreads == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to create event to stop all threads!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
goto Failure;
|
|
}
|
|
|
|
DNASSERT( m_fAllowThreadCountReduction == FALSE );
|
|
m_fAllowThreadCountReduction = TRUE;
|
|
|
|
//
|
|
// OS-specific initialization
|
|
//
|
|
#ifdef WINNT
|
|
//
|
|
// WinNT
|
|
//
|
|
if (FAILED(WinNTInit()))
|
|
{
|
|
goto Failure;
|
|
}
|
|
#else // WIN95
|
|
//
|
|
// Windows 9x
|
|
//
|
|
if (FAILED(Win9xInit()))
|
|
{
|
|
goto Failure;
|
|
}
|
|
#endif // WINNT
|
|
|
|
//
|
|
// Verify all internal flags. It's possible that TAPI didn't load
|
|
// so don't check it (it's not a fatal condition).
|
|
//
|
|
DNASSERT( m_InitFlags.fLockInitialized != FALSE );
|
|
DNASSERT( m_InitFlags.fIODataLockInitialized != FALSE );
|
|
DNASSERT( m_InitFlags.fJobDataLockInitialized != FALSE );
|
|
DNASSERT( m_InitFlags.fTimerDataLockInitialized != FALSE );
|
|
DNASSERT( m_InitFlags.fJobQueueInitialized != FALSE );
|
|
|
|
Exit:
|
|
return fReturn;
|
|
|
|
Failure:
|
|
fReturn = FALSE;
|
|
StopAllThreads();
|
|
Deinitialize();
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::Deinitialize - destroy work threads
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::Deinitialize"
|
|
|
|
void CModemThreadPool::Deinitialize( void )
|
|
{
|
|
// DNASSERT( m_JobQueue.IsEmpty() != FALSE );
|
|
|
|
//
|
|
// request that all threads stop and then cycle our timeslice to
|
|
// allow the threads a chance for cleanup
|
|
//
|
|
m_fAllowThreadCountReduction = FALSE;
|
|
DPFX(DPFPREP, 9, "SetIntendedThreadCount 0");
|
|
SetIntendedThreadCount( 0 );
|
|
StopAllThreads();
|
|
SleepEx( 0, TRUE );
|
|
|
|
#ifdef WINNT
|
|
if ( m_hIOCompletionPort != NULL )
|
|
{
|
|
if ( CloseHandle( m_hIOCompletionPort ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing handle to I/O completion port!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
m_hIOCompletionPort = NULL;
|
|
}
|
|
#endif // WINNT
|
|
|
|
//
|
|
// close StopAllThreads handle
|
|
//
|
|
if ( m_hStopAllThreads != NULL )
|
|
{
|
|
if ( DNCloseHandle( m_hStopAllThreads ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to close StopAllThreads handle!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
m_hStopAllThreads = NULL;
|
|
}
|
|
|
|
#ifdef WIN95
|
|
//
|
|
// close handles for I/O events
|
|
//
|
|
if ( m_hSendComplete != NULL )
|
|
{
|
|
if ( DNCloseHandle( m_hSendComplete ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing SendComplete handle!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
m_hSendComplete = NULL;
|
|
}
|
|
|
|
if ( m_hReceiveComplete != NULL )
|
|
{
|
|
if ( DNCloseHandle( m_hReceiveComplete ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing ReceiveComplete handle!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
m_hReceiveComplete = NULL;
|
|
}
|
|
#endif // WIN95
|
|
//
|
|
// Now that all of the threads are stopped, clean up any outstanding I/O.
|
|
// this can be done without taking any locks
|
|
//
|
|
if (m_InitFlags.fJobQueueInitialized)
|
|
{
|
|
CancelOutstandingIO();
|
|
}
|
|
|
|
//
|
|
// double-check empty IO lists
|
|
//
|
|
DNASSERT( m_OutstandingWriteList.IsEmpty() != FALSE );
|
|
DNASSERT( m_OutstandingReadList.IsEmpty() != FALSE );
|
|
|
|
//
|
|
// deinitialize handle table
|
|
//
|
|
if ( m_InitFlags.fDataPortHandleTableInitialized != FALSE )
|
|
{
|
|
m_DataPortHandleTable.Deinitialize();
|
|
m_InitFlags.fDataPortHandleTableInitialized = FALSE;
|
|
}
|
|
|
|
//
|
|
// deinitialize job queue
|
|
//
|
|
if ( m_InitFlags.fJobQueueInitialized != FALSE )
|
|
{
|
|
m_JobQueue.Deinitialize();
|
|
m_InitFlags.fJobQueueInitialized = FALSE;
|
|
}
|
|
|
|
if ( m_InitFlags.fTimerDataLockInitialized != FALSE )
|
|
{
|
|
DNDeleteCriticalSection( &m_TimerDataLock );
|
|
m_InitFlags.fTimerDataLockInitialized = FALSE;
|
|
}
|
|
|
|
if ( m_InitFlags.fJobDataLockInitialized != FALSE )
|
|
{
|
|
DNDeleteCriticalSection( &m_JobDataLock );
|
|
m_InitFlags.fJobDataLockInitialized = FALSE;
|
|
}
|
|
|
|
if ( m_InitFlags.fIODataLockInitialized != FALSE )
|
|
{
|
|
DNDeleteCriticalSection( &m_IODataLock );
|
|
m_InitFlags.fIODataLockInitialized = FALSE;
|
|
}
|
|
|
|
if ( m_InitFlags.fLockInitialized != FALSE )
|
|
{
|
|
DNDeleteCriticalSection( &m_Lock );
|
|
m_InitFlags.fLockInitialized = FALSE;
|
|
}
|
|
|
|
//
|
|
// unload TAPI
|
|
//
|
|
if ( m_TAPIInfo.hApplicationInstance != NULL )
|
|
{
|
|
DNASSERT( p_lineShutdown != NULL );
|
|
p_lineShutdown( m_TAPIInfo.hApplicationInstance );
|
|
m_TAPIInfo.hApplicationInstance = NULL;
|
|
}
|
|
m_fTAPIAvailable = FALSE;
|
|
memset( &m_TAPIInfo, 0x00, sizeof( m_TAPIInfo ) );
|
|
|
|
#ifdef WIN95
|
|
if (m_hTAPIEvent != NULL && m_hFakeTAPIEvent == NULL)
|
|
{
|
|
// In the case that we got the event from lineInitializeEx, lineShutdown will have closed the event handle,
|
|
// so we can tell the handle tracking code that the handle is already cleaned up.
|
|
REMOVE_DNHANDLE(m_hTAPIEvent);
|
|
}
|
|
m_hTAPIEvent = NULL;
|
|
|
|
if ( m_hFakeTAPIEvent != NULL )
|
|
{
|
|
if ( DNCloseHandle( m_hFakeTAPIEvent ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing fake TAPI event!" );
|
|
DisplayErrorCode( 0, GetLastError() );
|
|
}
|
|
|
|
m_hFakeTAPIEvent = NULL;
|
|
}
|
|
#endif // WIN95
|
|
//
|
|
// close TAPI
|
|
//
|
|
if ( m_InitFlags.fTAPILoaded != FALSE )
|
|
{
|
|
UnloadTAPILibrary();
|
|
m_InitFlags.fTAPILoaded = FALSE;
|
|
}
|
|
|
|
DNASSERT( m_InitFlags.fTAPILoaded == FALSE );
|
|
DNASSERT( m_InitFlags.fLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fIODataLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fJobDataLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fTimerDataLockInitialized == FALSE );
|
|
DNASSERT( m_InitFlags.fJobQueueInitialized == FALSE );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CreateReadIOData - create read IO data
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Pointer to Read IO Data
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::CreateReadIOData"
|
|
|
|
CModemReadIOData *CModemThreadPool::CreateReadIOData( void )
|
|
{
|
|
CModemReadIOData *pReadData;
|
|
|
|
|
|
LockReadData();
|
|
#ifdef WIN95
|
|
pReadData = (CModemReadIOData*)g_ModemReadIODataPool.Get( GetReceiveCompleteEvent() );
|
|
#else
|
|
pReadData = (CModemReadIOData*)g_ModemReadIODataPool.Get( NULL );
|
|
#endif // WIN95
|
|
if ( pReadData != NULL )
|
|
{
|
|
pReadData->SetThreadPool( this );
|
|
pReadData->m_OutstandingReadListLinkage.InsertBefore( &m_OutstandingReadList );
|
|
}
|
|
|
|
UnlockReadData();
|
|
return pReadData;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ReturnReadIOData - return read data to pool
|
|
//
|
|
// Entry: Pointer to read data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ReturnReadIOData"
|
|
|
|
void CModemThreadPool::ReturnReadIOData( CModemReadIOData *const pReadIOData )
|
|
{
|
|
DNASSERT( pReadIOData != NULL );
|
|
|
|
LockReadData();
|
|
|
|
pReadIOData->m_OutstandingReadListLinkage.RemoveFromList();
|
|
DNASSERT( pReadIOData->m_OutstandingReadListLinkage.IsEmpty() != FALSE );
|
|
|
|
g_ModemReadIODataPool.Release( pReadIOData );
|
|
|
|
UnlockReadData();
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CreateWriteIOData - create Write IO data
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Pointer to Write IO Data
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::CreateWriteIOData"
|
|
|
|
CModemWriteIOData *CModemThreadPool::CreateWriteIOData( void )
|
|
{
|
|
CModemWriteIOData *pWriteData;
|
|
|
|
|
|
LockWriteData();
|
|
#ifdef WIN95
|
|
pWriteData = (CModemWriteIOData*)g_ModemWriteIODataPool.Get( GetSendCompleteEvent() );
|
|
#else // WINNT
|
|
pWriteData = (CModemWriteIOData*)g_ModemWriteIODataPool.Get( NULL );
|
|
#endif // WIN95
|
|
if ( pWriteData != NULL )
|
|
{
|
|
pWriteData->m_OutstandingWriteListLinkage.InsertBefore( &m_OutstandingWriteList );
|
|
}
|
|
|
|
UnlockWriteData();
|
|
return pWriteData;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ReturnWriteIOData - return write data to pool
|
|
//
|
|
// Entry: Pointer to Write data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ReturnWriteIOData"
|
|
|
|
void CModemThreadPool::ReturnWriteIOData( CModemWriteIOData *const pWriteIOData )
|
|
{
|
|
DNASSERT( pWriteIOData != NULL );
|
|
|
|
LockWriteData();
|
|
|
|
pWriteIOData->m_OutstandingWriteListLinkage.RemoveFromList();
|
|
|
|
g_ModemWriteIODataPool.Release( pWriteIOData );
|
|
|
|
UnlockWriteData();
|
|
}
|
|
//**********************************************************************
|
|
|
|
#ifdef WINNT
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::WinNTInit - initialize WinNT components
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::WinNTInit"
|
|
|
|
HRESULT CModemThreadPool::WinNTInit( void )
|
|
{
|
|
HRESULT hr;
|
|
LINEINITIALIZEEXPARAMS LineInitializeExParams;
|
|
LONG lReturn;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
DNASSERT( m_hIOCompletionPort == NULL );
|
|
m_hIOCompletionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, // don't associate a file handle yet
|
|
NULL, // handle of existing completion port (none)
|
|
NULL, // completion key for callback (none)
|
|
0 // number of concurent threads (0 = use number of processors)
|
|
);
|
|
if ( m_hIOCompletionPort == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Could not create NT IOCompletionPort!" );
|
|
DisplayErrorCode( 0, GetLastError() );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Initialize TAPI. If TAPI doesn't start, it's not a problem, but note
|
|
// the failure.
|
|
//
|
|
DNASSERT( m_fTAPIAvailable == FALSE );
|
|
memset( &m_TAPIInfo, 0x00, sizeof( m_TAPIInfo ) );
|
|
memset( &LineInitializeExParams, 0x00, sizeof( LineInitializeExParams ) );
|
|
m_TAPIInfo.dwVersion = TAPI_CURRENT_VERSION;
|
|
LineInitializeExParams.dwTotalSize = sizeof( LineInitializeExParams );
|
|
LineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USECOMPLETIONPORT;
|
|
LineInitializeExParams.dwCompletionKey = IO_COMPLETION_KEY_TAPI_MESSAGE;
|
|
DNASSERT( GetIOCompletionPort() != NULL );
|
|
LineInitializeExParams.Handles.hCompletionPort = GetIOCompletionPort();
|
|
|
|
lReturn = LINEERR_UNINITIALIZED;
|
|
|
|
if ( p_lineInitializeEx != NULL )
|
|
{
|
|
lReturn = p_lineInitializeEx( &m_TAPIInfo.hApplicationInstance, // pointer to application TAPI instance handle
|
|
DNGetApplicationInstance(), // instance handle of .DLL
|
|
NULL, // callback function (not used)
|
|
NULL, // friendly application name (none)
|
|
&m_TAPIInfo.dwLinesAvailable, // pointer to number of devices available to TAPI
|
|
&m_TAPIInfo.dwVersion, // pointer to input/output TAPI version
|
|
&LineInitializeExParams ); // pointer to extra params
|
|
}
|
|
|
|
if ( lReturn == LINEERR_NONE )
|
|
{
|
|
m_fTAPIAvailable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to initialize TAPI for NT!" );
|
|
}
|
|
|
|
|
|
//
|
|
// Prepare to spin up IOCompletionPort threads
|
|
//
|
|
DNASSERT( ThreadCount() == 0 );
|
|
DNASSERT( NTCompletionThreadCount() == 0 );
|
|
|
|
DPFX(DPFPREP, 9, "SetIntendedThreadCount %i", g_iThreadCount);
|
|
SetIntendedThreadCount( g_iThreadCount );
|
|
|
|
Exit:
|
|
return hr;
|
|
|
|
Failure:
|
|
DPFX(DPFPREP, 0, "Failed WinNT initialization!" );
|
|
DisplayDNError( 0, hr );
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WINNT
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::Win9xInit - initialize Win9x components
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::Win9xInit"
|
|
|
|
HRESULT CModemThreadPool::Win9xInit( void )
|
|
{
|
|
HRESULT hr;
|
|
DNHANDLE hPrimaryThread;
|
|
DWORD dwPrimaryThreadID;
|
|
WIN9X_THREAD_DATA *pPrimaryThreadInput;
|
|
LINEINITIALIZEEXPARAMS LineInitializeExParams;
|
|
LONG lReturn;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
hPrimaryThread = NULL;
|
|
pPrimaryThreadInput = NULL;
|
|
|
|
//
|
|
// Initialize TAPI. If this succeeds, it will give us an event to use for
|
|
// TAPI messages. If not, create a fake event that will take the place of
|
|
// the TAPI event for the Win9x threads.
|
|
//
|
|
DNASSERT( m_fTAPIAvailable == FALSE );
|
|
memset( &m_TAPIInfo, 0x00, sizeof( m_TAPIInfo ) );
|
|
memset( &LineInitializeExParams, 0x00, sizeof( LineInitializeExParams ) );
|
|
m_TAPIInfo.dwVersion = TAPI_CURRENT_VERSION;
|
|
LineInitializeExParams.dwTotalSize = sizeof( LineInitializeExParams );
|
|
LineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
|
|
|
|
lReturn = LINEERR_UNINITIALIZED;
|
|
if ( p_lineInitializeEx != NULL )
|
|
{
|
|
lReturn = p_lineInitializeEx( &m_TAPIInfo.hApplicationInstance, // pointer to application TAPI instance handle
|
|
DNGetApplicationInstance(), // instance handle of .DLL
|
|
NULL, // callback function (not used)
|
|
NULL, // friendly application name (none)
|
|
&m_TAPIInfo.dwLinesAvailable, // pointer to number of devices available to TAPI
|
|
&m_TAPIInfo.dwVersion, // pointer to input/output TAPI version
|
|
&LineInitializeExParams ); // pointer to extra params
|
|
}
|
|
|
|
if ( lReturn == LINEERR_NONE )
|
|
{
|
|
m_hTAPIEvent = MAKE_DNHANDLE(LineInitializeExParams.Handles.hEvent);
|
|
m_fTAPIAvailable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to initialize TAPI for Win9x!" );
|
|
DNASSERT( m_hTAPIEvent == NULL );
|
|
DNASSERT( m_hFakeTAPIEvent == NULL );
|
|
m_hFakeTAPIEvent = DNCreateEvent( NULL, // pointer to security (none)
|
|
TRUE, // manual reset
|
|
FALSE, // start unsignalled
|
|
NULL ); // pointer to name (none)
|
|
if ( m_hFakeTAPIEvent == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to create fake TAPI event!" );
|
|
DisplayErrorCode( 0, hr );
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
m_hTAPIEvent = m_hFakeTAPIEvent;
|
|
DNASSERT( m_fTAPIAvailable == FALSE );
|
|
}
|
|
|
|
//
|
|
// create send complete event
|
|
//
|
|
DNASSERT( m_hSendComplete == NULL );
|
|
m_hSendComplete = DNCreateEvent( NULL, // pointer to security (none)
|
|
TRUE, // manual reset
|
|
FALSE, // start unsignalled
|
|
NULL // pointer to name (none)
|
|
);
|
|
if ( m_hSendComplete == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to create event for Send!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// create receive complete event
|
|
//
|
|
DNASSERT( m_hReceiveComplete == NULL );
|
|
m_hReceiveComplete = DNCreateEvent( NULL, // pointer to security (none)
|
|
TRUE, // manual reset
|
|
FALSE, // start unsignalled
|
|
NULL // pointer to name (none)
|
|
);
|
|
if ( m_hReceiveComplete == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to create event for Receive!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
//
|
|
// create parameters to worker threads
|
|
//
|
|
pPrimaryThreadInput = static_cast<WIN9X_THREAD_DATA*>( DNMalloc( sizeof( *pPrimaryThreadInput ) ) );
|
|
if ( pPrimaryThreadInput == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem allocating memory for primary Win9x thread!" );
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
memset( pPrimaryThreadInput, 0x00, sizeof( *pPrimaryThreadInput ) );
|
|
pPrimaryThreadInput->pThisThreadPool = this;
|
|
|
|
//
|
|
// Create one worker thread and boost its priority. If the primary thread
|
|
// can be created and boosted, create a secondary thread. Do not create a
|
|
// secondary thread if the primary could not be boosted because the system
|
|
// is probably low on resources.
|
|
//
|
|
IncrementActiveThreadCount();
|
|
hPrimaryThread = DNCreateThread( NULL, // pointer to security attributes (none)
|
|
0, // stack size (default)
|
|
PrimaryWin9xThread, // pointer to thread function
|
|
pPrimaryThreadInput, // pointer to input parameter
|
|
0, // let it run
|
|
&dwPrimaryThreadID // pointer to destination of thread ID
|
|
);
|
|
if ( hPrimaryThread == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
//
|
|
// Failed to create thread, decrement active thread count and report
|
|
// error.
|
|
//
|
|
DecrementActiveThreadCount();
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem creating Win9x thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
|
|
goto Failure;
|
|
}
|
|
pPrimaryThreadInput = NULL;
|
|
|
|
|
|
DPFX(DPFPREP, 8, "Created primary Win9x thread: 0x%x\tTotal Thread Count: %d", dwPrimaryThreadID, ThreadCount() );
|
|
DNASSERT( hPrimaryThread != NULL );
|
|
|
|
#if ADJUST_THREAD_PRIORITY
|
|
if ( SetThreadPriority( hPrimaryThread, THREAD_PRIORITY_ABOVE_NORMAL ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to boost priority of primary Win9x read thread! Not starting secondary thread" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
#endif // ADJUST_THREAD_PRIORITY
|
|
|
|
//
|
|
// Disallow thread reduction right off the bat.
|
|
// We give them these two threads and that's what they're stuck with.
|
|
//
|
|
m_fAllowThreadCountReduction = FALSE;
|
|
|
|
|
|
Exit:
|
|
if ( pPrimaryThreadInput != NULL )
|
|
{
|
|
DNFree( pPrimaryThreadInput );
|
|
pPrimaryThreadInput = NULL;
|
|
}
|
|
|
|
if ( hPrimaryThread != NULL )
|
|
{
|
|
if ( DNCloseHandle( hPrimaryThread ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing Win9x thread hanle!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
hPrimaryThread = NULL;
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
DPFX(DPFPREP, 0, "Failed Win9x Initialization!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
#ifdef WINNT
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::StartNTCompletionThread - start a WinNT completion thread
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::StartNTCompletionThread"
|
|
|
|
void CModemThreadPool::StartNTCompletionThread( void )
|
|
{
|
|
DNHANDLE hThread;
|
|
DWORD dwThreadID;
|
|
IOCOMPLETION_THREAD_DATA *pIOCompletionThreadData;
|
|
|
|
|
|
pIOCompletionThreadData = static_cast<IOCOMPLETION_THREAD_DATA*>( DNMalloc( sizeof( *pIOCompletionThreadData ) ) );
|
|
if ( pIOCompletionThreadData != NULL )
|
|
{
|
|
pIOCompletionThreadData->pThisThreadPool = this;
|
|
hThread = NULL;
|
|
hThread = DNCreateThread( NULL, // pointer to security attributes (none)
|
|
0, // stack size (default)
|
|
WinNTIOCompletionThread, // thread function
|
|
pIOCompletionThreadData, // thread parameter
|
|
0, // start thread immediately
|
|
&dwThreadID // pointer to thread ID destination
|
|
);
|
|
if ( hThread != NULL )
|
|
{
|
|
//
|
|
// note that a thread was created, and close the handle
|
|
// to the thread because it's no longer needed.
|
|
//
|
|
IncrementActiveNTCompletionThreadCount();
|
|
|
|
DPFX(DPFPREP, 8, "Creating I/O completion thread: 0x%x\tTotal Thread Count: %d\tNT Completion Thread Count: %d", dwThreadID, ThreadCount(), NTCompletionThreadCount() );
|
|
if ( DNCloseHandle( hThread ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem creating thread for I/O completion port" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to create I/O completion thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
|
|
DNFree( pIOCompletionThreadData );
|
|
}
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
#endif // WINNT
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::StopAllThreads - stop all work threads
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::StopAllThreads"
|
|
|
|
void CModemThreadPool::StopAllThreads( void )
|
|
{
|
|
//
|
|
// stop all non-I/O completion threads
|
|
//
|
|
if ( m_hStopAllThreads != NULL )
|
|
{
|
|
if ( DNSetEvent( m_hStopAllThreads ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to set event to stop all threads!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we're running on NT submit enough jobs to stop all threads.
|
|
//
|
|
#ifdef WINNT
|
|
UINT_PTR uIndex;
|
|
|
|
|
|
uIndex = NTCompletionThreadCount();
|
|
while ( uIndex > 0 )
|
|
{
|
|
uIndex--;
|
|
if ( PostQueuedCompletionStatus( m_hIOCompletionPort, // handle of completion port
|
|
0, // number of bytes transferred
|
|
IO_COMPLETION_KEY_SP_CLOSE, // completion key
|
|
NULL // pointer to overlapped structure (none)
|
|
) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem submitting Stop job to IO completion port!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
}
|
|
#endif // WINNT
|
|
//
|
|
// check for outstanding threads (no need to lock thread pool count)
|
|
//
|
|
DPFX(DPFPREP, 8, "Number of outstanding threads: %d", ThreadCount() );
|
|
while ( ThreadCount() != 0 )
|
|
{
|
|
DPFX(DPFPREP, 8, "Waiting for %d threads to quit.", ThreadCount() );
|
|
SleepEx( WORK_THREAD_CLOSE_SLEEP_TIME, TRUE );
|
|
}
|
|
|
|
DNASSERT( ThreadCount() == 0 );
|
|
m_iTotalThreadCount = 0;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CancelOutstandingIO - cancel outstanding IO
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::CancelOutstandingIO"
|
|
|
|
void CModemThreadPool::CancelOutstandingIO( void )
|
|
{
|
|
CBilink *pTemp;
|
|
|
|
|
|
DNASSERT( ThreadCount() == 0 );
|
|
|
|
//
|
|
// stop any receives with the notification that they were cancelled
|
|
//
|
|
pTemp = m_OutstandingReadList.GetNext();
|
|
while ( pTemp != &m_OutstandingReadList )
|
|
{
|
|
CModemReadIOData *pReadData;
|
|
|
|
|
|
pReadData = CModemReadIOData::ReadDataFromBilink( pTemp );
|
|
pTemp = pTemp->GetNext();
|
|
pReadData->m_OutstandingReadListLinkage.RemoveFromList();
|
|
|
|
DPFX(DPFPREP, 1, "Forcing read data 0x%p to be cancelled.", pReadData);
|
|
|
|
#ifdef WIN95
|
|
DNASSERT( pReadData->Win9xOperationPending() != FALSE );
|
|
pReadData->SetWin9xOperationPending( FALSE );
|
|
#endif // WIN95
|
|
pReadData->DataPort()->ProcessReceivedData( 0, ERROR_OPERATION_ABORTED );
|
|
}
|
|
|
|
//
|
|
// stop any pending writes with the notification that the user cancelled it.
|
|
//
|
|
pTemp = m_OutstandingWriteList.GetNext();
|
|
while ( pTemp != &m_OutstandingWriteList )
|
|
{
|
|
CModemWriteIOData *pWriteData;
|
|
|
|
|
|
pWriteData = CModemWriteIOData::WriteDataFromBilink( pTemp );
|
|
pTemp = pTemp->GetNext();
|
|
pWriteData->m_OutstandingWriteListLinkage.RemoveFromList();
|
|
|
|
DPFX(DPFPREP, 1, "Forcing write data 0x%p to be cancelled.", pWriteData);
|
|
|
|
#ifdef WIN95
|
|
DNASSERT( pWriteData->Win9xOperationPending() != FALSE );
|
|
pWriteData->SetWin9xOperationPending( FALSE );
|
|
#endif // WIN95
|
|
pWriteData->DataPort()->SendComplete( pWriteData, DPNERR_USERCANCEL );
|
|
}
|
|
|
|
while ( m_JobQueue.IsEmpty() == FALSE )
|
|
{
|
|
THREAD_POOL_JOB *pJob;
|
|
|
|
|
|
pJob = m_JobQueue.DequeueJob();
|
|
DNASSERT( pJob != NULL );
|
|
if (pJob->pCancelFunction != NULL)
|
|
{
|
|
pJob->pCancelFunction( pJob );
|
|
}
|
|
pJob->JobType = JOB_UNINITIALIZED;
|
|
|
|
g_ModemThreadPoolJobPool.Release( pJob );
|
|
};
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ReturnSelfToPool - return this object to the pool
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ReturnSelfToPool"
|
|
|
|
void CModemThreadPool::ReturnSelfToPool( void )
|
|
{
|
|
g_ModemThreadPoolPool.Release( this );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ProcessTimerJobs - process timed jobs
|
|
//
|
|
// Entry: Pointer to job list
|
|
// Pointer to destination for time of next job
|
|
//
|
|
// Exit: Boolean indicating active jobs exist
|
|
// TRUE = there are active jobs
|
|
// FALSE = there are no active jobs
|
|
//
|
|
// Notes: The input job queue is expected to be locked for the duration
|
|
// of this function call!
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ProcessTimerJobs"
|
|
|
|
BOOL CModemThreadPool::ProcessTimerJobs( const CBilink *const pJobList, DWORD *const pdwNextJobTime )
|
|
{
|
|
BOOL fReturn;
|
|
CBilink *pWorkingEntry;
|
|
INT_PTR iActiveTimerJobCount;
|
|
DWORD dwCurrentTime;
|
|
|
|
|
|
DNASSERT( pJobList != NULL );
|
|
DNASSERT( pdwNextJobTime != NULL );
|
|
|
|
//
|
|
// Initialize. Set the next job time to be infinitely far in the future
|
|
// so this thread will wake up for any jobs that need to completed before
|
|
// then.
|
|
//
|
|
fReturn = FALSE;
|
|
DBG_CASSERT( OFFSETOF( TIMER_OPERATION_ENTRY, Linkage ) == 0 );
|
|
pWorkingEntry = pJobList->GetNext();
|
|
iActiveTimerJobCount = 0;
|
|
dwCurrentTime = GETTIMESTAMP();
|
|
(*pdwNextJobTime) = dwCurrentTime - 1;
|
|
|
|
//
|
|
// loop through all timer items
|
|
//
|
|
while ( pWorkingEntry != pJobList )
|
|
{
|
|
TIMER_OPERATION_ENTRY *pTimerEntry;
|
|
BOOL fJobActive;
|
|
|
|
|
|
pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pWorkingEntry );
|
|
pWorkingEntry = pWorkingEntry->GetNext();
|
|
|
|
fJobActive = ProcessTimedOperation( pTimerEntry, dwCurrentTime, pdwNextJobTime );
|
|
DNASSERT( ( fJobActive == FALSE ) || ( fJobActive == TRUE ) );
|
|
|
|
fReturn |= fJobActive;
|
|
|
|
if ( fJobActive == FALSE )
|
|
{
|
|
RemoveTimerOperationEntry( pTimerEntry, DPN_OK );
|
|
}
|
|
}
|
|
|
|
DNASSERT( ( fReturn == FALSE ) || ( fReturn == TRUE ) );
|
|
|
|
return fReturn;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ProcessTimedOperation - process a timed operation
|
|
//
|
|
// Entry: Pointer to job information
|
|
// Current time
|
|
// Pointer to time to be updated
|
|
//
|
|
// Exit: Boolean indicating that the job is still active
|
|
// TRUE = operation active
|
|
// FALSE = operation not active
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ProcessTimedOperation"
|
|
|
|
BOOL CModemThreadPool::ProcessTimedOperation( TIMER_OPERATION_ENTRY *const pTimedJob,
|
|
const DWORD dwCurrentTime,
|
|
DWORD *const pdwNextJobTime )
|
|
{
|
|
BOOL fEnumActive;
|
|
|
|
|
|
DNASSERT( pTimedJob != NULL );
|
|
DNASSERT( pdwNextJobTime != NULL );
|
|
|
|
|
|
DPFX(DPFPREP, 9, "(0x%p) Parameters: (0x%p, %u, 0x%p)",
|
|
this, pTimedJob, dwCurrentTime, pdwNextJobTime);
|
|
|
|
//
|
|
// Assume that this enum will remain active. If we retire this enum, this
|
|
// value will be reset.
|
|
//
|
|
fEnumActive = TRUE;
|
|
|
|
//
|
|
// If this enum has completed sending enums and is waiting only
|
|
// for responses, decrement the wait time (assuming it's not infinite)
|
|
// and remove the enum if the we've exceeded its wait time.
|
|
//
|
|
if ( pTimedJob->uRetryCount == 0 )
|
|
{
|
|
if ( (int) ( pTimedJob->dwIdleTimeout - dwCurrentTime ) <= 0 )
|
|
{
|
|
fEnumActive = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This enum isn't complete, check to see if it's the next enum
|
|
// to need service.
|
|
//
|
|
if ( (int) ( pTimedJob->dwIdleTimeout - (*pdwNextJobTime) ) < 0 )
|
|
{
|
|
(*pdwNextJobTime) = pTimedJob->dwIdleTimeout;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This enum is still sending. Determine if it's time to send a new enum
|
|
// and adjust the wakeup time if appropriate.
|
|
//
|
|
if ( (int) ( pTimedJob->dwNextRetryTime - dwCurrentTime ) <= 0 )
|
|
{
|
|
#ifdef DBG
|
|
DWORD dwDelay;
|
|
|
|
|
|
dwDelay = dwCurrentTime - pTimedJob->dwNextRetryTime;
|
|
|
|
DPFX(DPFPREP, 7, "Threadpool 0x%p performing timed job 0x%p approximately %u ms after intended time of %u.",
|
|
this, pTimedJob, dwDelay, pTimedJob->dwNextRetryTime);
|
|
#endif // DBG
|
|
|
|
//
|
|
// Timeout, execute this timed item
|
|
//
|
|
pTimedJob->pTimerCallback( pTimedJob->pContext );
|
|
|
|
//
|
|
// If this job isn't running forever, decrement the retry count.
|
|
// If there are no more retries, set up wait time. If the job
|
|
// is waiting forever, set max wait timeout.
|
|
//
|
|
if ( pTimedJob->fRetryForever == FALSE )
|
|
{
|
|
pTimedJob->uRetryCount--;
|
|
if ( pTimedJob->uRetryCount == 0 )
|
|
{
|
|
if ( pTimedJob->fIdleWaitForever == FALSE )
|
|
{
|
|
//
|
|
// Compute stopping time for this job's 'Timeout' phase and
|
|
// see if this will be the next job to need service.
|
|
//
|
|
pTimedJob->dwIdleTimeout += dwCurrentTime;
|
|
if ( (int) (pTimedJob->dwIdleTimeout - (*pdwNextJobTime) ) < 0 )
|
|
{
|
|
(*pdwNextJobTime) = pTimedJob->dwIdleTimeout;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're waiting forever for enum returns. ASSERT that we
|
|
// have the maximum timeout and don't bother checking to see
|
|
// if this will be the next enum to need service (it'll never
|
|
// need service).
|
|
//
|
|
DNASSERT( pTimedJob->dwIdleTimeout == -1 );
|
|
}
|
|
|
|
goto SkipNextRetryTimeComputation;
|
|
}
|
|
}
|
|
|
|
pTimedJob->dwNextRetryTime = dwCurrentTime + pTimedJob->dwRetryInterval;
|
|
}
|
|
|
|
//
|
|
// is this the next enum to fire?
|
|
//
|
|
if ( (int) ( (*pdwNextJobTime) - pTimedJob->dwNextRetryTime ) < 0 )
|
|
{
|
|
(*pdwNextJobTime) = pTimedJob->dwNextRetryTime;
|
|
|
|
|
|
DPFX(DPFPREP, 8, "Job 0x%p is the next job to fire (at time %u).",
|
|
pTimedJob, pTimedJob->dwNextRetryTime);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not next job to fire.
|
|
//
|
|
}
|
|
|
|
SkipNextRetryTimeComputation:
|
|
//
|
|
// the following blank line is there to shut up the compiler
|
|
//
|
|
;
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 9, "(0x%p) Returning [%i]",
|
|
this, fEnumActive);
|
|
|
|
return fEnumActive;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::SubmitWorkItem - submit a work item for processing and inform
|
|
// work thread that another job is available
|
|
//
|
|
// Entry: Pointer to job information
|
|
//
|
|
// Exit: Error code
|
|
//
|
|
// Note: This function assumes that the job data is locked.
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::SubmitWorkItem"
|
|
|
|
HRESULT CModemThreadPool::SubmitWorkItem( THREAD_POOL_JOB *const pJobInfo )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
DNASSERT( pJobInfo != NULL );
|
|
AssertCriticalSectionIsTakenByThisThread( &m_JobDataLock, TRUE );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
//
|
|
// add job to queue and tell someone that there's a job available
|
|
//
|
|
m_JobQueue.Lock();
|
|
m_JobQueue.EnqueueJob( pJobInfo );
|
|
m_JobQueue.Unlock();
|
|
|
|
#ifdef WINNT
|
|
//
|
|
// WinNT, submit new I/O completion item
|
|
//
|
|
DNASSERT( m_hIOCompletionPort != NULL );
|
|
if ( PostQueuedCompletionStatus( m_hIOCompletionPort, // completion port
|
|
0, // number of bytes written (unused)
|
|
IO_COMPLETION_KEY_NEW_JOB, // completion key
|
|
NULL // pointer to overlapped structure (unused)
|
|
) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem posting completion item for new job!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
goto Failure;
|
|
}
|
|
#else // WIN95
|
|
//
|
|
// Win9x, set event that the work thread will listen for
|
|
//
|
|
DNASSERT( m_JobQueue.GetPendingJobHandle() != NULL );
|
|
if ( m_JobQueue.SignalPendingJob() == FALSE )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Failed to signal pending job!" );
|
|
goto Failure;
|
|
}
|
|
#endif // WINNT
|
|
|
|
Exit:
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem with SubmitWorkItem!" );
|
|
DisplayDNError( 0, hr );
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::GetWorkItem - get a work item from the job queue
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Pointer to job information (may be NULL)
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::GetWorkItem"
|
|
|
|
THREAD_POOL_JOB *CModemThreadPool::GetWorkItem( void )
|
|
{
|
|
THREAD_POOL_JOB *pReturn;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pReturn = NULL;
|
|
|
|
m_JobQueue.Lock();
|
|
pReturn = m_JobQueue.DequeueJob();
|
|
|
|
//
|
|
// if we're under Win9x (we have a 'pending job' handle),
|
|
// see if the handle needs to be reset
|
|
//
|
|
if ( m_JobQueue.IsEmpty() != FALSE )
|
|
{
|
|
DNASSERT( m_JobQueue.GetPendingJobHandle() != NULL );
|
|
if ( DNResetEvent( m_JobQueue.GetPendingJobHandle() ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem resetting event for pending Win9x jobs!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
}
|
|
|
|
m_JobQueue.Unlock();
|
|
|
|
return pReturn;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::SubmitTimerJob - add a timer job to the timer list
|
|
//
|
|
// Entry: 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 "CModemThreadPool::SubmitTimerJob"
|
|
|
|
HRESULT CModemThreadPool::SubmitTimerJob( 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 )
|
|
{
|
|
HRESULT hr;
|
|
TIMER_OPERATION_ENTRY *pEntry;
|
|
THREAD_POOL_JOB *pJob;
|
|
BOOL fTimerJobListLocked;
|
|
|
|
|
|
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
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
pEntry = NULL;
|
|
pJob = NULL;
|
|
fTimerJobListLocked = FALSE;
|
|
|
|
LockJobData();
|
|
|
|
//
|
|
// If we're on NT, attempt to start the enum thread here so we can return
|
|
// an error if it fails to start. If it does start, it'll sit until it's
|
|
// informed that an enum job has been added.
|
|
//
|
|
#ifdef WINNT
|
|
hr = StartNTTimerThread();
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Cannot spin up NT timer thread!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
#endif // WINNT
|
|
//
|
|
// allocate new enum entry
|
|
//
|
|
pEntry = static_cast<TIMER_OPERATION_ENTRY*>( g_ModemTimerEntryPool.Get( ) );
|
|
if ( pEntry == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Cannot allocate memory to add to timer list!" );
|
|
goto Failure;
|
|
}
|
|
DNASSERT( pEntry->pContext == NULL );
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// set this enum to fire as soon as it gets a chance
|
|
//
|
|
pEntry->dwNextRetryTime = 0;
|
|
|
|
pJob = static_cast<THREAD_POOL_JOB*>( g_ModemThreadPoolJobPool.Get( ) );
|
|
if ( pJob == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Cannot allocate memory for enum job!" );
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Create job for work thread.
|
|
//
|
|
pJob->pCancelFunction = NULL;
|
|
pJob->JobType = JOB_REFRESH_TIMER_JOBS;
|
|
|
|
// set our dummy paramter to simulate passing data
|
|
DEBUG_ONLY( pJob->JobData.JobRefreshTimedJobs.uDummy = 0 );
|
|
|
|
LockTimerData();
|
|
fTimerJobListLocked = TRUE;
|
|
|
|
//
|
|
// we can submit the 'ENUM_REFRESH' job before inserting the enum entry
|
|
// into the active enum list because nobody will be able to pull the
|
|
// 'ENUM_REFRESH' job from the queue since we have the queue locked
|
|
//
|
|
hr = SubmitWorkItem( pJob );
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem submitting enum work item" );
|
|
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 );
|
|
|
|
Exit:
|
|
if ( fTimerJobListLocked != FALSE )
|
|
{
|
|
UnlockTimerData();
|
|
fTimerJobListLocked = FALSE;
|
|
}
|
|
|
|
UnlockJobData();
|
|
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem with SubmitEnumJob" );
|
|
DisplayDNError( 0, hr );
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
if ( pEntry != NULL )
|
|
{
|
|
g_ModemTimerEntryPool.Release( pEntry );
|
|
DEBUG_ONLY( pEntry = NULL );
|
|
}
|
|
|
|
if ( pJob != NULL )
|
|
{
|
|
g_ModemThreadPoolJobPool.Release( pJob );
|
|
DEBUG_ONLY( pJob = NULL );
|
|
}
|
|
|
|
//
|
|
// It's possible that the enum thread has been started for this enum.
|
|
// Since there's no way to stop it without completing the enums or
|
|
// closing the SP, leave it running.
|
|
//
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::StopTimerJob - remove timer job from list
|
|
//
|
|
// Entry: Pointer to job context (these MUST be uniquie 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 "CModemThreadPool::StopTimerJob"
|
|
|
|
BOOL CModemThreadPool::StopTimerJob( void *const pContext, const HRESULT hCommandResult )
|
|
{
|
|
BOOL fComplete = FALSE;
|
|
CBilink * pTempEntry;
|
|
TIMER_OPERATION_ENTRY * pTimerEntry;
|
|
|
|
|
|
DNASSERT( pContext != NULL );
|
|
|
|
DPFX(DPFPREP, 9, "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 )
|
|
{
|
|
//
|
|
// 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)
|
|
{
|
|
pTimerEntry->pTimerComplete( hCommandResult, pTimerEntry->pContext );
|
|
g_ModemTimerEntryPool.Release( pTimerEntry );
|
|
}
|
|
|
|
|
|
DPFX(DPFPREP, 9, "Returning [%i]", fComplete);
|
|
|
|
return fComplete;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::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 "CModemThreadPool::SpawnDialogThread"
|
|
|
|
HRESULT CModemThreadPool::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;
|
|
|
|
//
|
|
// assume that a thread will be created
|
|
//
|
|
IncrementActiveThreadCount();
|
|
|
|
//
|
|
// 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;
|
|
|
|
|
|
//
|
|
// decrement active thread count and report error
|
|
//
|
|
DecrementActiveThreadCount();
|
|
|
|
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;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::GetIOThreadCount - get I/O thread count
|
|
//
|
|
// Entry: Pointer to variable to fill
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::GetIOThreadCount"
|
|
|
|
HRESULT CModemThreadPool::GetIOThreadCount( LONG *const piThreadCount )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
DNASSERT( piThreadCount != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
|
|
Lock();
|
|
|
|
if ( IsThreadCountReductionAllowed() )
|
|
{
|
|
*piThreadCount = GetIntendedThreadCount();
|
|
}
|
|
else
|
|
{
|
|
#ifdef WIN95
|
|
*piThreadCount = ThreadCount();
|
|
#else // WINNT
|
|
DNASSERT( NTCompletionThreadCount() != 0 );
|
|
*piThreadCount = NTCompletionThreadCount();
|
|
#endif // WIN95
|
|
}
|
|
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::SetIOThreadCount - set I/O thread count
|
|
//
|
|
// Entry: New thread count
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::SetIOThreadCount"
|
|
|
|
HRESULT CModemThreadPool::SetIOThreadCount( const LONG iThreadCount )
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
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
|
|
{
|
|
#ifdef WIN95
|
|
//
|
|
// Win9x can not adjust thread count.
|
|
//
|
|
DPFX(DPFPREP, 4, "Thread pool locked down and already has %i 9x threads, not adjusting to %i.",
|
|
ThreadCount(), iThreadCount );
|
|
#else // WINNT
|
|
//
|
|
// WinNT can have many threads. If the user wants more threads, attempt
|
|
// to boost the thread pool to the requested amount (if we fail to
|
|
// start a new thread, too bad). If the user wants fewer threads, check
|
|
// to see if the thread pool has been locked out of changes. If not,
|
|
// start killing off the threads.
|
|
//
|
|
if ( iThreadCount > NTCompletionThreadCount() )
|
|
{
|
|
INT_PTR iDeltaThreads;
|
|
|
|
|
|
iDeltaThreads = iThreadCount - NTCompletionThreadCount();
|
|
|
|
DPFX(DPFPREP, 4, "Thread pool locked down, spawning %i new NT threads (for a total of %i).",
|
|
iDeltaThreads, iThreadCount );
|
|
|
|
while ( iDeltaThreads > 0 )
|
|
{
|
|
iDeltaThreads--;
|
|
StartNTCompletionThread();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 4, "Thread pool locked down and already has %i NT threads, not adjusting to %i.",
|
|
NTCompletionThreadCount(), iThreadCount );
|
|
}
|
|
#endif // WIN95
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::PreventThreadPoolReduction - prevents the thread pool size from being reduced
|
|
//
|
|
// Entry: None
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::PreventThreadPoolReduction"
|
|
|
|
HRESULT CModemThreadPool::PreventThreadPoolReduction( void )
|
|
{
|
|
HRESULT hr = DPN_OK;
|
|
LONG iDesiredThreads;
|
|
#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;
|
|
|
|
DNASSERT( GetIntendedThreadCount() > 0 );
|
|
DNASSERT( ThreadCount() == 0 );
|
|
|
|
iDesiredThreads = GetIntendedThreadCount();
|
|
SetIntendedThreadCount( 0 );
|
|
|
|
|
|
DPFX(DPFPREP, 3, "Locking down thread count at %i.", iDesiredThreads );
|
|
|
|
#ifdef DBG
|
|
dwStartTime = GETTIMESTAMP();
|
|
#endif // DBG
|
|
|
|
|
|
//
|
|
// OS-specific thread starting.
|
|
//
|
|
#ifdef WINNT
|
|
//
|
|
// WinNT
|
|
//
|
|
DNASSERT( NTCompletionThreadCount() == 0 );
|
|
|
|
while ( iDesiredThreads > 0 )
|
|
{
|
|
iDesiredThreads--;
|
|
StartNTCompletionThread();
|
|
}
|
|
|
|
//
|
|
// If at least one thread was created, the SP will perform in a
|
|
// non-optimal fashion, but we will still function. If no threads
|
|
// were created, fail.
|
|
//
|
|
if ( ThreadCount() == 0 )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Unable to create any threads to service NT I/O completion port!" );
|
|
goto Failure;
|
|
}
|
|
#else // WIN95
|
|
//
|
|
// Windows 9x
|
|
//
|
|
//
|
|
// We should never get here because there will always only
|
|
// be 1 thread.
|
|
//
|
|
DNASSERT( FALSE );
|
|
#endif // WINNT
|
|
|
|
#ifdef DBG
|
|
DPFX(DPFPREP, 8, "Spent %u ms starting %i threads.", (GETTIMESTAMP() - dwStartTime), ThreadCount());
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 3, "Thread count already locked down (at %i).", ThreadCount() );
|
|
}
|
|
|
|
#ifdef WINNT
|
|
Exit:
|
|
#endif // WINNT
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
|
|
#ifdef WINNT
|
|
Failure:
|
|
|
|
goto Exit;
|
|
#endif // WINNT
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CreateDataPortHandle - create a new handle and assign a CDataPort
|
|
// to it
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
HRESULT CModemThreadPool::CreateDataPortHandle( CDataPort *const pDataPort )
|
|
{
|
|
HRESULT hr;
|
|
DPNHANDLE hDataPort;
|
|
|
|
|
|
DNASSERT( pDataPort != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
hDataPort = 0;
|
|
|
|
hr = m_DataPortHandleTable.Create( pDataPort, &hDataPort );
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DNASSERT( hDataPort == 0 );
|
|
DPFX(DPFPREP, 0, "Failed to create handle!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
|
|
pDataPort->SetHandle( hDataPort );
|
|
pDataPort->AddRef();
|
|
|
|
Exit:
|
|
return hr;
|
|
|
|
Failure:
|
|
DNASSERT( hDataPort == 0 );
|
|
DNASSERT( pDataPort->GetHandle() == 0 );
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CloseDataPortHandle - invalidate a handle for a CDataPort
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
void CModemThreadPool::CloseDataPortHandle( CDataPort *const pDataPort )
|
|
{
|
|
DPNHANDLE hDataPort;
|
|
|
|
|
|
DNASSERT( pDataPort != NULL );
|
|
|
|
hDataPort = pDataPort->GetHandle();
|
|
|
|
if (SUCCEEDED(m_DataPortHandleTable.Destroy( hDataPort, NULL )))
|
|
{
|
|
pDataPort->SetHandle( 0 );
|
|
pDataPort->DecRef();
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::DataPortFromHandle - convert a handle to a CDataPort
|
|
//
|
|
// Entry: Handle
|
|
//
|
|
// Exit: Pointer to CDataPort (may be NULL for invalid handle)
|
|
// ------------------------------
|
|
CDataPort *CModemThreadPool::DataPortFromHandle( const DPNHANDLE hDataPort )
|
|
{
|
|
CDataPort *pDataPort;
|
|
|
|
|
|
DNASSERT( hDataPort != 0 );
|
|
|
|
pDataPort = NULL;
|
|
|
|
m_DataPortHandleTable.Lock();
|
|
if (SUCCEEDED(m_DataPortHandleTable.Find( hDataPort, (PVOID*)&pDataPort )))
|
|
{
|
|
pDataPort->AddRef();
|
|
}
|
|
m_DataPortHandleTable.Unlock();
|
|
|
|
return pDataPort;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::SubmitDelayedCommand - submit request to enum query to remote session
|
|
//
|
|
// Entry: Pointer to callback function
|
|
// Pointer to cancel function
|
|
// Pointer to callback context
|
|
//
|
|
// Exit: Error code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::SubmitDelayedCommand"
|
|
|
|
HRESULT CModemThreadPool::SubmitDelayedCommand( JOB_FUNCTION *const pFunction, JOB_FUNCTION *const pCancelFunction, void *const pContext )
|
|
{
|
|
HRESULT hr;
|
|
THREAD_POOL_JOB *pJob;
|
|
BOOL fJobDataLocked;
|
|
|
|
|
|
DNASSERT( pFunction != NULL );
|
|
DNASSERT( pCancelFunction != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
pJob = NULL;
|
|
fJobDataLocked = FALSE;
|
|
|
|
pJob = static_cast<THREAD_POOL_JOB*>( g_ModemThreadPoolJobPool.Get( ) );
|
|
if ( pJob == NULL )
|
|
{
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
DPFX(DPFPREP, 0, "Cannot allocate job for DelayedCommand!" );
|
|
goto Failure;
|
|
}
|
|
|
|
pJob->JobType = JOB_DELAYED_COMMAND;
|
|
pJob->pCancelFunction = pCancelFunction;
|
|
pJob->JobData.JobDelayedCommand.pCommandFunction = pFunction;
|
|
pJob->JobData.JobDelayedCommand.pContext = pContext;
|
|
|
|
LockJobData();
|
|
fJobDataLocked = TRUE;
|
|
|
|
hr = SubmitWorkItem( pJob );
|
|
if ( hr != DPN_OK )
|
|
{
|
|
DPFX(DPFPREP, 0, "Problem submitting DelayedCommand job!" );
|
|
DisplayDNError( 0, hr );
|
|
goto Failure;
|
|
}
|
|
|
|
Exit:
|
|
if ( fJobDataLocked != FALSE )
|
|
{
|
|
UnlockJobData();
|
|
fJobDataLocked = FALSE;
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failure:
|
|
if ( pJob != NULL )
|
|
{
|
|
g_ModemThreadPoolJobPool.Release( pJob );
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
#ifdef WINNT
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::StartNTTimerThread - start the timer thread for NT
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Error code
|
|
//
|
|
// Note: This function assumes that the enum data is locked.
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::StartNTTimerThread"
|
|
|
|
HRESULT CModemThreadPool::StartNTTimerThread( void )
|
|
{
|
|
HRESULT hr;
|
|
DNHANDLE hThread;
|
|
DWORD dwThreadID;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
hr = DPN_OK;
|
|
DNASSERT( m_JobQueue.GetPendingJobHandle() != NULL );
|
|
|
|
if ( m_fNTTimerThreadRunning != FALSE )
|
|
{
|
|
//
|
|
// the enum thread is already running, poke it to note new enums
|
|
//
|
|
if ( DNSetEvent( m_JobQueue.GetPendingJobHandle() ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem setting event to wake NTTimerThread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
goto Failure;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// DNASSERT( m_hWakeNTEnumThread != NULL );
|
|
// m_NTEnumThreadData.hEventList[ EVENT_INDEX_ENUM_WAKEUP ] = m_hWakeNTEnumThread;
|
|
|
|
IncrementActiveThreadCount();
|
|
hThread = DNCreateThread( NULL, // pointer to security attributes (none)
|
|
0, // stack size (default)
|
|
WinNTTimerThread, // thread function
|
|
this, // thread parameter
|
|
0, // creation flags (none, start running now)
|
|
&dwThreadID // pointer to thread ID
|
|
);
|
|
if ( hThread == NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
hr = DPNERR_OUTOFMEMORY;
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to create NT timer thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
DecrementActiveThreadCount();
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// note that the thread is running and close the handle to the thread
|
|
//
|
|
m_fNTTimerThreadRunning = TRUE;
|
|
DPFX(DPFPREP, 8, "Creating NT-Timer thread: 0x%x\tTotal Thread Count: %d\tNT Completion Thread Count: %d", dwThreadID, ThreadCount(), NTCompletionThreadCount() );
|
|
|
|
if ( DNCloseHandle( hThread ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem closing handle after starting NTTimerThread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
Exit:
|
|
return hr;
|
|
|
|
Failure:
|
|
goto Exit;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::WakeNTTimerThread - wake the timer thread because a timed event
|
|
// has been added
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::WakeNTTimerThread"
|
|
|
|
void CModemThreadPool::WakeNTTimerThread( void )
|
|
{
|
|
LockJobData();
|
|
DNASSERT( m_JobQueue.GetPendingJobHandle() != FALSE );
|
|
if ( DNSetEvent( m_JobQueue.GetPendingJobHandle() ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem setting event to wake up NT timer thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
UnlockJobData();
|
|
}
|
|
//**********************************************************************
|
|
#endif // WINNT
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::RemoveTimerOperationEntry - remove timer operation job from list
|
|
//
|
|
// Entry: Pointer to timer operation
|
|
// Result code to return
|
|
//
|
|
// Exit: Nothing
|
|
//
|
|
// Note: This function assumes that the list is appropriately locked
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::RemoveTimerOperationEntry"
|
|
|
|
void CModemThreadPool::RemoveTimerOperationEntry( TIMER_OPERATION_ENTRY *const pTimerEntry, const HRESULT hJobResult )
|
|
{
|
|
DNASSERT( pTimerEntry != NULL );
|
|
AssertCriticalSectionIsTakenByThisThread( &m_TimerDataLock, TRUE );
|
|
|
|
//
|
|
// remove this link from the list, tell owner that the job is complete and
|
|
// return the job to the pool
|
|
//
|
|
pTimerEntry->Linkage.RemoveFromList();
|
|
pTimerEntry->pTimerComplete( hJobResult, pTimerEntry->pContext );
|
|
g_ModemTimerEntryPool.Release( pTimerEntry );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CompleteOutstandingSends - check for completed sends and
|
|
// indicate send completion for them.
|
|
//
|
|
// Entry: Send complete event
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::CompleteOutstandingSends"
|
|
|
|
void CModemThreadPool::CompleteOutstandingSends( const DNHANDLE hSendCompleteEvent )
|
|
{
|
|
CBilink *pCurrentOutstandingWrite;
|
|
CBilink WritesToBeProcessed;
|
|
|
|
|
|
DNASSERT( hSendCompleteEvent != NULL );
|
|
WritesToBeProcessed.Initialize();
|
|
|
|
//
|
|
// Loop through the list out outstanding sends. Any completed sends are
|
|
// removed from the list and processed after we release the write data lock.
|
|
//
|
|
LockWriteData();
|
|
pCurrentOutstandingWrite = m_OutstandingWriteList.GetNext();
|
|
while ( pCurrentOutstandingWrite != &m_OutstandingWriteList )
|
|
{
|
|
CDataPort *pDataPort;
|
|
CModemWriteIOData *pWriteIOData;
|
|
DWORD dwFlags;
|
|
DWORD dwBytesSent;
|
|
|
|
|
|
//
|
|
// note this send and advance pointer to the next pending send
|
|
//
|
|
pWriteIOData = CModemWriteIOData::WriteDataFromBilink( pCurrentOutstandingWrite );
|
|
pCurrentOutstandingWrite = pCurrentOutstandingWrite->GetNext();
|
|
|
|
if ( pWriteIOData->Win9xOperationPending() != FALSE )
|
|
{
|
|
if ( GetOverlappedResult( HANDLE_FROM_DNHANDLE(pWriteIOData->DataPort()->GetFileHandle()), // file handle
|
|
pWriteIOData->Overlap(), // pointer to overlap structure
|
|
&dwBytesSent, // pointer to bytes sent
|
|
FALSE // wait for completion (don't wait)
|
|
) != FALSE )
|
|
{
|
|
pWriteIOData->jkm_hSendResult = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
switch( dwError )
|
|
{
|
|
//
|
|
// ERROR_IO_PENDING = operation pending
|
|
//
|
|
case ERROR_IO_PENDING:
|
|
{
|
|
goto SkipSendCompletion;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ERROR_IO_INCOMPLETE = overlapped I/O is not in a signalled state
|
|
// this is expected since the event is always
|
|
// cleared before checking I/O. Assume complete.
|
|
//
|
|
case ERROR_IO_INCOMPLETE:
|
|
{
|
|
pWriteIOData->jkm_hSendResult = DPN_OK;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ERROR_OPERATION_ABORTED = operation complete because of cancel or
|
|
// a thread quit. (Com port was closed)
|
|
//
|
|
case ERROR_OPERATION_ABORTED:
|
|
{
|
|
pWriteIOData->jkm_hSendResult = DPNERR_USERCANCEL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ERROR_INVALID_HANDLE = the serial port is gone. This is
|
|
// possible when a modem hangs up.
|
|
//
|
|
case ERROR_INVALID_HANDLE:
|
|
{
|
|
pWriteIOData->jkm_hSendResult = DPNERR_NOCONNECTION;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other error, stop and take a look
|
|
//
|
|
default:
|
|
{
|
|
pWriteIOData->jkm_hSendResult = DPNERR_GENERIC;
|
|
DisplayErrorCode( 0, dwError );
|
|
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DNASSERT( pWriteIOData->Win9xOperationPending() != FALSE );
|
|
pWriteIOData->SetWin9xOperationPending( FALSE );
|
|
|
|
pWriteIOData->m_OutstandingWriteListLinkage.RemoveFromList();
|
|
pWriteIOData->m_OutstandingWriteListLinkage.InsertBefore( &WritesToBeProcessed );
|
|
}
|
|
|
|
SkipSendCompletion:
|
|
//
|
|
// the following line is present to prevent the compiler from whining
|
|
// about a blank line
|
|
//
|
|
;
|
|
}
|
|
|
|
//
|
|
// If there are no more outstanding reads, reset the write complete event.
|
|
// It will be signalled when the next posted write completes. No other read
|
|
// can be posted at this time because the write data lock is held.
|
|
//
|
|
if ( m_OutstandingWriteList.IsEmpty() != FALSE )
|
|
{
|
|
if ( DNResetEvent( hSendCompleteEvent ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to reset send event!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
}
|
|
UnlockWriteData();
|
|
|
|
//
|
|
// process all writes that have been pulled to the side.
|
|
//
|
|
while ( WritesToBeProcessed.GetNext() != &WritesToBeProcessed )
|
|
{
|
|
CModemWriteIOData *pTempWrite;
|
|
CDataPort *pDataPort;
|
|
|
|
|
|
pTempWrite = CModemWriteIOData::WriteDataFromBilink( WritesToBeProcessed.GetNext() );
|
|
pTempWrite->m_OutstandingWriteListLinkage.RemoveFromList();
|
|
pDataPort = pTempWrite->DataPort();
|
|
DNASSERT( pDataPort != NULL );
|
|
|
|
pDataPort->SendComplete( pTempWrite, pTempWrite->jkm_hSendResult );
|
|
// pDataPort->SendFromWriteQueue();
|
|
// pDataPort->DecRef();
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::CompleteOutstandingReceives - check for completed receives and
|
|
// indicate completion for them.
|
|
//
|
|
// Entry: Receive complete event
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::CompleteOutstandingReceives"
|
|
|
|
void CModemThreadPool::CompleteOutstandingReceives( const DNHANDLE hReceiveCompleteEvent )
|
|
{
|
|
CBilink *pCurrentOutstandingRead;
|
|
CBilink ReadsToBeProcessed;
|
|
|
|
|
|
DNASSERT( hReceiveCompleteEvent != NULL );
|
|
ReadsToBeProcessed.Initialize();
|
|
LockReadData();
|
|
|
|
//
|
|
// Loop through the list of outstanding reads and pull out the ones that need
|
|
// to be serviced. We don't want to service them while the read data lock
|
|
// is taken.
|
|
//
|
|
pCurrentOutstandingRead = m_OutstandingReadList.GetNext();
|
|
while ( pCurrentOutstandingRead != &m_OutstandingReadList )
|
|
{
|
|
CDataPort *pDataPort;
|
|
CModemReadIOData *pReadIOData;
|
|
DWORD dwFlags;
|
|
|
|
|
|
pReadIOData = CModemReadIOData::ReadDataFromBilink( pCurrentOutstandingRead );
|
|
pCurrentOutstandingRead = pCurrentOutstandingRead->GetNext();
|
|
|
|
//
|
|
// Make sure this operation is really pending before attempting to check
|
|
// for completion. It's possible that the read was added to the list, but
|
|
// we haven't actually called Winsock yet.
|
|
//
|
|
if ( pReadIOData->Win9xOperationPending() != FALSE )
|
|
{
|
|
if ( GetOverlappedResult( HANDLE_FROM_DNHANDLE(pReadIOData->DataPort()->GetFileHandle()),
|
|
pReadIOData->Overlap(),
|
|
&pReadIOData->jkm_dwOverlappedBytesReceived,
|
|
FALSE
|
|
) != FALSE )
|
|
{
|
|
DBG_CASSERT( ERROR_SUCCESS == 0 );
|
|
// pReadIOData->jkm_hReceiveReturn = DPN_OK;
|
|
pReadIOData->m_dwWin9xReceiveErrorReturn = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
switch( dwError )
|
|
{
|
|
//
|
|
// ERROR_IO_INCOMPLETE = treat as I/O complete. Event isn't
|
|
// signalled, but that's expected because
|
|
// it's cleared before checking for I/O
|
|
//
|
|
case ERROR_IO_INCOMPLETE:
|
|
{
|
|
pReadIOData->jkm_dwOverlappedBytesReceived = pReadIOData->m_dwBytesToRead;
|
|
pReadIOData->m_dwWin9xReceiveErrorReturn = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ERROR_IO_PENDING = io still pending
|
|
//
|
|
case ERROR_IO_PENDING:
|
|
{
|
|
DNASSERT( FALSE );
|
|
goto SkipReceiveCompletion;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other error, stop if not 'known'
|
|
//
|
|
default:
|
|
{
|
|
switch ( dwError )
|
|
{
|
|
//
|
|
// ERROR_OPERATION_ABORTED = operation was cancelled (COM port closed)
|
|
// ERROR_INVALID_HANDLE = operation was cancelled (COM port closed)
|
|
//
|
|
case ERROR_OPERATION_ABORTED:
|
|
case ERROR_INVALID_HANDLE:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DisplayErrorCode( 0, dwError );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
pReadIOData->m_dwWin9xReceiveErrorReturn = dwError;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DNASSERT( pReadIOData->Win9xOperationPending() != FALSE );
|
|
pReadIOData->SetWin9xOperationPending( FALSE );
|
|
|
|
pReadIOData->m_OutstandingReadListLinkage.RemoveFromList();
|
|
pReadIOData->m_OutstandingReadListLinkage.InsertBefore( &ReadsToBeProcessed );
|
|
}
|
|
|
|
SkipReceiveCompletion:
|
|
//
|
|
// the following line is present to prevent the compiler from whining
|
|
// about a blank line
|
|
//
|
|
;
|
|
}
|
|
|
|
//
|
|
// If there are no more outstanding reads, reset the read complete event.
|
|
// It will be signalled when the next posted read completes. No other read
|
|
// can be posted at this time because the read data lock is held.
|
|
//
|
|
if ( m_OutstandingReadList.IsEmpty() != FALSE )
|
|
{
|
|
if ( DNResetEvent( hReceiveCompleteEvent ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Failed to reset receive event!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
}
|
|
|
|
UnlockReadData();
|
|
|
|
//
|
|
// loop through the list of reads that have completed and dispatch them
|
|
//
|
|
while ( ReadsToBeProcessed.GetNext() != &ReadsToBeProcessed )
|
|
{
|
|
CModemReadIOData *pTempRead;
|
|
CDataPort *pDataPort;
|
|
|
|
|
|
pTempRead = CModemReadIOData::ReadDataFromBilink( ReadsToBeProcessed.GetNext() );
|
|
pTempRead->m_OutstandingReadListLinkage.RemoveFromList();
|
|
|
|
pDataPort = pTempRead->DataPort();
|
|
DNASSERT( pDataPort != NULL );
|
|
pDataPort->ProcessReceivedData( pTempRead->jkm_dwOverlappedBytesReceived, pTempRead->m_dwWin9xReceiveErrorReturn );
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::PrimaryWin9xThread - main thread to do everything that the SP is
|
|
// supposed to do under Win9x.
|
|
//
|
|
// Entry: Pointer to startup parameter
|
|
//
|
|
// Exit: Error Code
|
|
//
|
|
// Note: The startup parameter is allocated for this thread and must be
|
|
// deallocated by this thread when it exits
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::PrimaryWin9xThread"
|
|
|
|
DWORD WINAPI CModemThreadPool::PrimaryWin9xThread( void *pParam )
|
|
{
|
|
WIN9X_CORE_DATA CoreData;
|
|
DWORD dwCurrentTime;
|
|
DWORD dwMaxWaitTime;
|
|
BOOL fComInitialized;
|
|
|
|
|
|
CModemThreadPool *const pThisThreadPool = static_cast<WIN9X_THREAD_DATA *>( pParam )->pThisThreadPool;
|
|
|
|
|
|
DNASSERT( pParam != NULL );
|
|
DNASSERT( pThisThreadPool != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
memset( &CoreData, 0x00, sizeof CoreData );
|
|
fComInitialized = FALSE;
|
|
|
|
//
|
|
// before we do anything we need to make sure COM is happy
|
|
//
|
|
switch ( COM_CoInitialize( NULL ) )
|
|
{
|
|
//
|
|
// no problem
|
|
//
|
|
case S_OK:
|
|
{
|
|
fComInitialized = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM already initialized, huh?
|
|
//
|
|
case S_FALSE:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM init failed!
|
|
//
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "Primary Win9x thread failed to initialize COM!" );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
DNASSERT( CoreData.fTimerJobsActive == FALSE );
|
|
|
|
//
|
|
// set enums to happen infinitely in the future
|
|
//
|
|
CoreData.dwNextTimerJobTime = GETTIMESTAMP() - 1;
|
|
|
|
//
|
|
// set wait handles
|
|
//
|
|
CoreData.hWaitHandles[ EVENT_INDEX_STOP_ALL_THREADS ] = pThisThreadPool->m_hStopAllThreads;
|
|
CoreData.hWaitHandles[ EVENT_INDEX_PENDING_JOB ] = pThisThreadPool->m_JobQueue.GetPendingJobHandle();
|
|
CoreData.hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ] = pThisThreadPool->GetSendCompleteEvent();
|
|
CoreData.hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ] = pThisThreadPool->GetReceiveCompleteEvent();
|
|
CoreData.hWaitHandles[ EVENT_INDEX_TAPI_MESSAGE ] = pThisThreadPool->GetTAPIMessageEvent();
|
|
|
|
DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_STOP_ALL_THREADS ] != NULL );
|
|
DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_PENDING_JOB ] != NULL );
|
|
DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ] != NULL );
|
|
DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ] != NULL );
|
|
DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_TAPI_MESSAGE ] );
|
|
|
|
//
|
|
// go until we're told to stop
|
|
//
|
|
CoreData.fLooping = TRUE;
|
|
while ( CoreData.fLooping != FALSE )
|
|
{
|
|
DWORD dwWaitReturn;
|
|
|
|
|
|
//
|
|
// Update the job time so we know how long to wait. We can
|
|
// only get here if a socket was just added to the socket list, or
|
|
// we've been servicing sockets.
|
|
//
|
|
dwCurrentTime = GETTIMESTAMP();
|
|
if ( (int) ( dwCurrentTime - CoreData.dwNextTimerJobTime ) >= 0 )
|
|
{
|
|
pThisThreadPool->LockTimerData();
|
|
CoreData.fTimerJobsActive = pThisThreadPool->ProcessTimerJobs( &pThisThreadPool->m_TimerJobList,
|
|
&CoreData.dwNextTimerJobTime );
|
|
if ( CoreData.fTimerJobsActive != FALSE )
|
|
{
|
|
DPFX(DPFPREP, 8, "There are active jobs left with Winsock1 sockets active!" );
|
|
}
|
|
pThisThreadPool->UnlockTimerData();
|
|
}
|
|
|
|
dwMaxWaitTime = CoreData.dwNextTimerJobTime - dwCurrentTime;
|
|
|
|
//
|
|
// Check for I/O
|
|
//
|
|
dwWaitReturn = DNWaitForMultipleObjectsEx( LENGTHOF( CoreData.hWaitHandles ), // count of handles
|
|
CoreData.hWaitHandles, // handles to wait on
|
|
FALSE, // don't wait for all to be signalled
|
|
dwMaxWaitTime, // wait timeout
|
|
TRUE // we're alertable for APCs
|
|
);
|
|
switch ( dwWaitReturn )
|
|
{
|
|
//
|
|
// timeout, don't do anything, we'll probably process timer jobs on
|
|
// the next loop
|
|
//
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_PENDING_JOB ):
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_STOP_ALL_THREADS ):
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_SEND_COMPLETE ):
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_RECEIVE_COMPLETE ):
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_TAPI_MESSAGE ):
|
|
{
|
|
pThisThreadPool->ProcessWin9xEvents( &CoreData );
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// There are I/O completion routines scheduled on this thread.
|
|
// This is not a good thing!
|
|
//
|
|
case WAIT_IO_COMPLETION:
|
|
{
|
|
DPFX(DPFPREP, 1, "WARNING: APC was serviced on the primary Win9x IO service thread! What is the application doing??" );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// wait failed
|
|
//
|
|
case WAIT_FAILED:
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Primary Win9x thread wait failed!" );
|
|
DisplayDNError( 0, dwError );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// problem
|
|
//
|
|
default:
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Primary Win9x thread unknown problem in wait!" );
|
|
DisplayDNError( 0, dwError );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pThisThreadPool->DecrementActiveThreadCount();
|
|
|
|
DNFree( pParam );
|
|
|
|
if ( fComInitialized != FALSE )
|
|
{
|
|
COM_CoUninitialize();
|
|
fComInitialized = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WINNT
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::WinNTIOCompletionThread - thread to service I/O completion port
|
|
//
|
|
// Entry: Pointer to startup parameter
|
|
//
|
|
// Exit: Error Code
|
|
//
|
|
// Note: The startup parameter is allocated for this thread and must be
|
|
// deallocated by this thread when it exits
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::WinNTIOCompletionThread"
|
|
|
|
DWORD WINAPI CModemThreadPool::WinNTIOCompletionThread( void *pParam )
|
|
{
|
|
IOCOMPLETION_THREAD_DATA *pInput;
|
|
BOOL fLooping;
|
|
HANDLE hIOCompletionPort;
|
|
BOOL fComInitialized;
|
|
|
|
|
|
DNASSERT( pParam != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pInput = static_cast<IOCOMPLETION_THREAD_DATA*>( pParam );
|
|
DNASSERT( pInput->pThisThreadPool != NULL );
|
|
fLooping = TRUE;
|
|
hIOCompletionPort = pInput->pThisThreadPool->m_hIOCompletionPort;
|
|
DNASSERT( hIOCompletionPort != NULL );
|
|
fComInitialized = FALSE;
|
|
|
|
//
|
|
// before we do anything we need to make sure COM is happy
|
|
//
|
|
switch ( COM_CoInitialize( NULL ) )
|
|
{
|
|
//
|
|
// no problem
|
|
//
|
|
case S_OK:
|
|
{
|
|
fComInitialized = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM already initialized, huh?
|
|
//
|
|
case S_FALSE:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM init failed!
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
DPFX(DPFPREP, 0, "Failed to initialize COM!" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// go until we're told to stop
|
|
//
|
|
while ( fLooping != FALSE )
|
|
{
|
|
BOOL fStatusReturn;
|
|
DWORD dwBytesTransferred;
|
|
ULONG_PTR uCompletionKey;
|
|
OVERLAPPED *pOverlapped;
|
|
|
|
|
|
DNASSERT( hIOCompletionPort != NULL );
|
|
fStatusReturn = GetQueuedCompletionStatus( hIOCompletionPort, // handle of completion port
|
|
&dwBytesTransferred, // pointer to number of bytes read
|
|
&uCompletionKey, // pointer to completion key
|
|
&pOverlapped, // pointer to overlapped structure
|
|
INFINITE // wait forever
|
|
);
|
|
if ( ( fStatusReturn == FALSE ) && ( pOverlapped == FALSE ) )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem getting item from completion port!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
else
|
|
{
|
|
switch ( uCompletionKey )
|
|
{
|
|
//
|
|
// ReadFile or WriteFile completed. Check error status and
|
|
// complete the appropriate operation.
|
|
//
|
|
case IO_COMPLETION_KEY_IO_COMPLETE:
|
|
{
|
|
CIOData *pIOData;
|
|
DWORD dwError;
|
|
|
|
|
|
DNASSERT( pOverlapped != NULL );
|
|
if ( fStatusReturn != FALSE )
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
pIOData = CIOData::IODataFromOverlap( pOverlapped );
|
|
if ( pIOData->NTIOOperationType() == NT_IO_OPERATION_RECEIVE )
|
|
{
|
|
DNASSERT( ( dwError == ERROR_SUCCESS ) || ( dwBytesTransferred == 0 ) );
|
|
pIOData->DataPort()->ProcessReceivedData( dwBytesTransferred, dwError );
|
|
}
|
|
else
|
|
{
|
|
HRESULT hOperationResult;
|
|
|
|
|
|
DNASSERT( pIOData->NTIOOperationType() == NT_IO_OPERATION_SEND );
|
|
switch ( dwError )
|
|
{
|
|
//
|
|
// no problem
|
|
//
|
|
case ERROR_SUCCESS:
|
|
{
|
|
hOperationResult = DPN_OK;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ERROR_OPERATION_ABORTED = operation was stopped, most likely
|
|
// because of a user cancel
|
|
//
|
|
case ERROR_OPERATION_ABORTED:
|
|
{
|
|
hOperationResult = DPNERR_USERCANCEL;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
DPFX(DPFPREP, 0, "Failed on I/O completion send!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
hOperationResult = DPNERR_GENERIC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pIOData->DataPort()->SendComplete( static_cast<CModemWriteIOData*>( pIOData ), hOperationResult );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// SP is closing, stop all threads
|
|
//
|
|
case IO_COMPLETION_KEY_SP_CLOSE:
|
|
{
|
|
DNASSERT( DNWaitForSingleObjectEx( pInput->pThisThreadPool->m_hStopAllThreads, 0, TRUE ) == WAIT_OBJECT_0 );
|
|
fLooping = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// a new job was submitted to the job queue, or the SP is closing from above
|
|
//
|
|
case IO_COMPLETION_KEY_NEW_JOB:
|
|
{
|
|
THREAD_POOL_JOB *pJobInfo;
|
|
|
|
|
|
//
|
|
// SP is still running, process our job
|
|
//
|
|
pJobInfo = pInput->pThisThreadPool->GetWorkItem();
|
|
if ( pJobInfo != NULL )
|
|
{
|
|
switch ( pJobInfo->JobType )
|
|
{
|
|
//
|
|
// enum refresh
|
|
//
|
|
case JOB_REFRESH_TIMER_JOBS:
|
|
{
|
|
DPFX(DPFPREP, 8, "IOCompletion job REFRESH_TIMER_JOBS" );
|
|
DNASSERT( pJobInfo->JobData.JobRefreshTimedJobs.uDummy == 0 );
|
|
|
|
pInput->pThisThreadPool->WakeNTTimerThread();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// issue callback for this job
|
|
//
|
|
case JOB_DELAYED_COMMAND:
|
|
{
|
|
DPFX(DPFPREP, 8, "IOCompletion job DELAYED_COMMAND" );
|
|
DNASSERT( pJobInfo->JobData.JobDelayedCommand.pCommandFunction != NULL );
|
|
|
|
pJobInfo->JobData.JobDelayedCommand.pCommandFunction( pJobInfo );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other job
|
|
//
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "IOCompletion job unknown!" );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
pJobInfo->JobType = JOB_UNINITIALIZED;
|
|
g_ModemThreadPoolJobPool.Release( pJobInfo );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// TAPI message, pointer to line message is in pOverlapped and
|
|
// we are responsible for freeing it via LocalFree()
|
|
//
|
|
case IO_COMPLETION_KEY_TAPI_MESSAGE:
|
|
{
|
|
LINEMESSAGE *pLineMessage;
|
|
CDataPort *pModemPort;
|
|
DPNHANDLE hModemPort;
|
|
|
|
|
|
DNASSERT( pOverlapped != NULL );
|
|
DBG_CASSERT( sizeof( pLineMessage ) == sizeof( pOverlapped ) );
|
|
pLineMessage = reinterpret_cast<LINEMESSAGE*>( pOverlapped );
|
|
|
|
DBG_CASSERT( sizeof( pModemPort ) == sizeof( pLineMessage->dwCallbackInstance ) );
|
|
hModemPort = (DPNHANDLE)( pLineMessage->dwCallbackInstance );
|
|
|
|
pModemPort = static_cast<CDataPort*>( pInput->pThisThreadPool->DataPortFromHandle( hModemPort ) );
|
|
if ( pModemPort != NULL )
|
|
{
|
|
pModemPort->ProcessTAPIMessage( pLineMessage );
|
|
|
|
if ( LocalFree( pOverlapped ) != NULL )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem with LocalFree in NTProcessTAPIMessage" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
pModemPort->DecRef();
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 5, "Dropping TAPI message on closed modem port!" );
|
|
DisplayTAPIMessage( 5, pLineMessage );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// unknown I/O completion message type
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pInput->pThisThreadPool->DecrementActiveNTCompletionThreadCount();
|
|
DNFree( pInput );
|
|
|
|
if ( fComInitialized != FALSE )
|
|
{
|
|
COM_CoUninitialize();
|
|
fComInitialized = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WINNT
|
|
|
|
#ifdef WINNT
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::WinNTTimerThread - timer thread for NT
|
|
//
|
|
// Entry: Pointer to startup parameter
|
|
//
|
|
// Exit: Error Code
|
|
//
|
|
// Note: The startup parameter is a static memory chunk and cannot be freed.
|
|
// Cleanup of this memory is the responsibility of this thread.
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::WinNTTimerThread"
|
|
|
|
DWORD WINAPI CModemThreadPool::WinNTTimerThread( void *pParam )
|
|
{
|
|
CModemThreadPool *pThisThreadPool;
|
|
BOOL fLooping;
|
|
DWORD dwWaitReturn;
|
|
DWORD dwNextEnumTime;
|
|
DNHANDLE hEvents[ 2 ];
|
|
BOOL fComInitialized;
|
|
|
|
|
|
DNASSERT( pParam != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
DNASSERT( pParam != NULL );
|
|
pThisThreadPool = static_cast<CModemThreadPool*>( pParam );
|
|
DNASSERT( pThisThreadPool->m_JobQueue.GetPendingJobHandle() != NULL );
|
|
|
|
dwNextEnumTime = GETTIMESTAMP() - 1;
|
|
|
|
hEvents[ EVENT_INDEX_STOP_ALL_THREADS ] = pThisThreadPool->m_hStopAllThreads;
|
|
hEvents[ EVENT_INDEX_WAKE_NT_TIMER_THREAD ] = pThisThreadPool->m_JobQueue.GetPendingJobHandle();
|
|
|
|
fComInitialized = FALSE;
|
|
|
|
|
|
//
|
|
// before we do anything we need to make sure COM is happy
|
|
//
|
|
switch ( COM_CoInitialize( NULL ) )
|
|
{
|
|
//
|
|
// no problem
|
|
//
|
|
case S_OK:
|
|
{
|
|
fComInitialized = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM already initialized, huh?
|
|
//
|
|
case S_FALSE:
|
|
{
|
|
DNASSERT( FALSE );
|
|
fComInitialized = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// COM init failed!
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
DPFX(DPFPREP, 0, "Failed to initialize COM!" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// there were no active enums so we want to wait forever for something to
|
|
// happen
|
|
//
|
|
fLooping = TRUE;
|
|
|
|
//
|
|
// go until we're told to stop
|
|
//
|
|
while ( fLooping != FALSE )
|
|
{
|
|
DWORD dwCurrentTime;
|
|
DWORD dwMaxWaitTime;
|
|
|
|
|
|
dwCurrentTime = GETTIMESTAMP();
|
|
|
|
if ( (int) ( dwNextEnumTime - dwCurrentTime ) <= 0 )
|
|
{
|
|
|
|
//
|
|
// acknowledge that we've handled this event and then process the
|
|
// enums
|
|
//
|
|
pThisThreadPool->LockTimerData();
|
|
|
|
if ( DNResetEvent( hEvents[ EVENT_INDEX_WAKE_NT_TIMER_THREAD ] ) == FALSE )
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
dwError = GetLastError();
|
|
DPFX(DPFPREP, 0, "Problem resetting event to wake NT timer thread!" );
|
|
DisplayErrorCode( 0, dwError );
|
|
}
|
|
|
|
pThisThreadPool->ProcessTimerJobs( &pThisThreadPool->m_TimerJobList, &dwNextEnumTime );
|
|
pThisThreadPool->UnlockTimerData();
|
|
}
|
|
|
|
dwMaxWaitTime = dwNextEnumTime - dwCurrentTime;
|
|
|
|
DPFX(DPFPREP, 9, "Waiting %u ms until next timed job.", dwMaxWaitTime);
|
|
|
|
dwWaitReturn = DNWaitForMultipleObjectsEx( LENGTHOF( hEvents ), // number of events
|
|
hEvents, // event list
|
|
FALSE, // wait for any one event to be signalled
|
|
dwMaxWaitTime, // timeout
|
|
TRUE // be nice and allow APCs
|
|
);
|
|
switch ( dwWaitReturn )
|
|
{
|
|
//
|
|
// SP closing
|
|
//
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_STOP_ALL_THREADS ):
|
|
{
|
|
DPFX(DPFPREP, 8, "NT timer thread thread detected SPClose!" );
|
|
fLooping = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Enum wakeup event, someone added an enum to the list. Clear
|
|
// our enum time and go back to the top of the loop where we
|
|
// will process enums.
|
|
//
|
|
case ( WAIT_OBJECT_0 + EVENT_INDEX_WAKE_NT_TIMER_THREAD ):
|
|
{
|
|
dwNextEnumTime = GETTIMESTAMP();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Wait timeout. We're probably going to process enums, go back
|
|
// to the top of the loop.
|
|
//
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// wait failed
|
|
//
|
|
case WAIT_FAILED:
|
|
{
|
|
DPFX(DPFPREP, 0, "NT timer thread WaitForMultipleObjects failed: 0x%x", dwWaitReturn );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// problem
|
|
//
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPFX(DPFPREP, 8, "NT timer thread is exiting!" );
|
|
pThisThreadPool->LockTimerData();
|
|
|
|
pThisThreadPool->m_fNTTimerThreadRunning = FALSE;
|
|
pThisThreadPool->DecrementActiveThreadCount();
|
|
|
|
pThisThreadPool->UnlockTimerData();
|
|
|
|
|
|
if ( fComInitialized != FALSE )
|
|
{
|
|
COM_CoUninitialize();
|
|
fComInitialized = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//**********************************************************************
|
|
#endif // WINNT
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::DialogThreadProc - thread proc for spawning dialogs
|
|
//
|
|
// Entry: Pointer to startup parameter
|
|
//
|
|
// Exit: Error Code
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::DialogThreadProc"
|
|
|
|
DWORD WINAPI CModemThreadPool::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 );
|
|
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 );
|
|
|
|
pThreadParam->pThisThreadPool->DecrementActiveThreadCount();
|
|
DNFree( pParam );
|
|
|
|
if ( fComInitialized != FALSE )
|
|
{
|
|
COM_CoUninitialize();
|
|
fComInitialized = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//**********************************************************************
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ProcessWin9xEvents - process a Win9x events
|
|
//
|
|
// Entry: Pointer core data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ProcessWin9xEvents"
|
|
|
|
void CModemThreadPool::ProcessWin9xEvents( WIN9X_CORE_DATA *const pCoreData )
|
|
{
|
|
BOOL fAllIOComplete;
|
|
DNASSERT( pCoreData != NULL );
|
|
|
|
|
|
//
|
|
// this funciton checks each of the handles to see if they're signalled
|
|
// to prevent I/O from starving the rest of the handles
|
|
//
|
|
fAllIOComplete = TRUE;
|
|
|
|
//
|
|
// New job. Account for the time spent in the wait. Don't
|
|
// account for time after the job is complete because it's
|
|
// possible that the job was an job submission which will want
|
|
// to reset the wait time.
|
|
//
|
|
switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_PENDING_JOB ], 0 ) )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
{
|
|
DPFX(DPFPREP, 8, "Primary Win9x thread has a pending job!" );
|
|
ProcessWin9xJob( pCoreData );
|
|
break;
|
|
}
|
|
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// TAPI message
|
|
//
|
|
switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_TAPI_MESSAGE ], 0 ) )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
{
|
|
DPFX(DPFPREP, 8, "Processing TAPI event!" );
|
|
ProcessTapiEvent();
|
|
break;
|
|
}
|
|
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// send complete
|
|
//
|
|
switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ], 0 ) )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
{
|
|
// DPFX(DPFPREP, 0, "\n\n\nPrimary Win9x thread servicing sends!\n\n\n" );
|
|
CompleteOutstandingSends( pCoreData->hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ] );
|
|
break;
|
|
}
|
|
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// receive complete
|
|
//
|
|
switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ], 0 ) )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
{
|
|
// DPFX(DPFPREP, 0, "\n\n\nPrimary Win9x thread servicing receives!\n\n\n" );
|
|
CompleteOutstandingReceives( pCoreData->hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ] );
|
|
break;
|
|
}
|
|
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// SP closing
|
|
//
|
|
switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_STOP_ALL_THREADS ], 0 ) )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
{
|
|
DPFX(DPFPREP, 8, "Primary Win9x thread exit because SP closing!" );
|
|
pCoreData->fLooping = FALSE;
|
|
break;
|
|
}
|
|
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If there is I/O pending the Read/Write handles are probably still signalled.
|
|
// Wait 5 milliseconds to process it before running through the handles again.
|
|
//
|
|
LockReadData();
|
|
LockWriteData();
|
|
|
|
if ( ( m_OutstandingReadList.IsEmpty() == FALSE ) ||
|
|
( m_OutstandingWriteList.IsEmpty() == FALSE ) )
|
|
{
|
|
fAllIOComplete = FALSE;
|
|
}
|
|
|
|
UnlockReadData();
|
|
UnlockWriteData();
|
|
|
|
if ( fAllIOComplete == FALSE )
|
|
{
|
|
SleepEx( 5, TRUE );
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
#ifdef WIN95
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ProcessWin9xJob - process a Win9x job
|
|
//
|
|
// Entry: Pointer core data
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CModemThreadPool::ProcessWin9xJob"
|
|
|
|
void CModemThreadPool::ProcessWin9xJob( WIN9X_CORE_DATA *const pCoreData )
|
|
{
|
|
THREAD_POOL_JOB *pJobInfo;
|
|
|
|
|
|
//
|
|
// remove and process a single job from the list
|
|
//
|
|
pJobInfo = GetWorkItem();
|
|
if ( pJobInfo != NULL )
|
|
{
|
|
switch ( pJobInfo->JobType )
|
|
{
|
|
//
|
|
// enum refresh
|
|
//
|
|
case JOB_REFRESH_TIMER_JOBS:
|
|
{
|
|
DPFX(DPFPREP, 8, "WorkThread job REFRESH_ENUM" );
|
|
DNASSERT( pJobInfo->JobData.JobRefreshTimedJobs.uDummy == 0 );
|
|
LockTimerData();
|
|
pCoreData->fTimerJobsActive = ProcessTimerJobs( &m_TimerJobList, &pCoreData->dwNextTimerJobTime );
|
|
UnlockTimerData();
|
|
|
|
if ( pCoreData->fTimerJobsActive != FALSE )
|
|
{
|
|
DPFX(DPFPREP, 8, "There are active timer jobs left after processing a Win9x REFRESH_TIMER_JOBS" );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// issue callback for this job
|
|
//
|
|
case JOB_DELAYED_COMMAND:
|
|
{
|
|
DPFX(DPFPREP, 8, "WorkThread job DELAYED_COMMAND" );
|
|
DNASSERT( pJobInfo->JobData.JobDelayedCommand.pCommandFunction != NULL );
|
|
pJobInfo->JobData.JobDelayedCommand.pCommandFunction( pJobInfo );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// other job
|
|
//
|
|
default:
|
|
{
|
|
DPFX(DPFPREP, 0, "WorkThread Win9x job unknown!" );
|
|
DNASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
pJobInfo->JobType = JOB_UNINITIALIZED;
|
|
g_ModemThreadPoolJobPool.Release( pJobInfo );
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
#endif // WIN95
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// CModemThreadPool::ProcessTapiEvent - process TAPI event
|
|
//
|
|
// Entry: Nothing
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
void CModemThreadPool::ProcessTapiEvent( void )
|
|
{
|
|
LONG lTapiReturn;
|
|
LINEMESSAGE LineMessage;
|
|
|
|
|
|
lTapiReturn = p_lineGetMessage( GetTAPIInfo()->hApplicationInstance, &LineMessage, 0 );
|
|
if ( lTapiReturn != LINEERR_NONE )
|
|
{
|
|
DPFX(DPFPREP, 0, "Failed to process Win9x TAPI message!" );
|
|
DisplayTAPIError( 0, lTapiReturn );
|
|
}
|
|
else
|
|
{
|
|
CDataPort *pModemPort;
|
|
DPNHANDLE hModemPort;
|
|
|
|
|
|
DNASSERT( sizeof( hModemPort ) == sizeof( LineMessage.dwCallbackInstance ) );
|
|
hModemPort = (DPNHANDLE)( LineMessage.dwCallbackInstance );
|
|
|
|
pModemPort = static_cast<CDataPort*>( DataPortFromHandle( hModemPort ) );
|
|
if ( pModemPort != NULL )
|
|
{
|
|
pModemPort->ProcessTAPIMessage( &LineMessage );
|
|
pModemPort->DecRef();
|
|
}
|
|
}
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ThreadPoolJob_Alloc - allocate a new job
|
|
//
|
|
// Entry: Pointer to new entry
|
|
//
|
|
// Exit: Boolean indicating success
|
|
// TRUE = initialization successful
|
|
// FALSE = initialization failed
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "ThreadPoolJob_Alloc"
|
|
|
|
BOOL ThreadPoolJob_Alloc( void *pvItem, void* pvContext )
|
|
{
|
|
BOOL fReturn;
|
|
THREAD_POOL_JOB *pJob;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fReturn = TRUE;
|
|
pJob = static_cast<THREAD_POOL_JOB*>( pvItem );
|
|
|
|
memset( pJob, 0x00, sizeof( *pJob ) );
|
|
|
|
return fReturn;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ThreadPoolJob_Get - a job is being removed from the pool
|
|
//
|
|
// Entry: Pointer to job
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "ThreadPoolJob_Get"
|
|
|
|
void ThreadPoolJob_Get( void *pvItem, void* pvContext )
|
|
{
|
|
THREAD_POOL_JOB *pJob;
|
|
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pJob = static_cast<THREAD_POOL_JOB*>( pvItem );
|
|
DNASSERT( pJob->JobType == JOB_UNINITIALIZED );
|
|
DNASSERT( pJob->pNext == NULL );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ThreadPoolJob_Release - a job is being returned to the pool
|
|
//
|
|
// Entry: Pointer to job
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "ThreadPoolJob_Release"
|
|
|
|
void ThreadPoolJob_Release( void *pvItem )
|
|
{
|
|
THREAD_POOL_JOB *pJob;
|
|
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
pJob = static_cast<THREAD_POOL_JOB*>( pvItem );
|
|
|
|
DNASSERT( pJob->JobType == JOB_UNINITIALIZED );
|
|
pJob->pNext = NULL;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ModemTimerEntry_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 "ModemTimerEntry_Alloc"
|
|
|
|
BOOL ModemTimerEntry_Alloc( void *pvItem, void* pvContext )
|
|
{
|
|
BOOL fReturn;
|
|
TIMER_OPERATION_ENTRY *pTimerEntry;
|
|
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
fReturn = TRUE;
|
|
pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
|
|
memset( pTimerEntry, 0x00, sizeof( *pTimerEntry ) );
|
|
pTimerEntry->pContext = NULL;
|
|
pTimerEntry->Linkage.Initialize();
|
|
|
|
return fReturn;
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ModemTimerEntry_Get - get new timer job entry from pool
|
|
//
|
|
// Entry: Pointer to new entry
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "ModemTimerEntry_Get"
|
|
|
|
void ModemTimerEntry_Get( void *pvItem, void* pvContext )
|
|
{
|
|
TIMER_OPERATION_ENTRY *pTimerEntry;
|
|
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
|
|
pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
|
|
|
|
pTimerEntry->Linkage.Initialize();
|
|
DNASSERT( pTimerEntry->pContext == NULL );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ModemTimerEntry_Release - return timer job entry to pool
|
|
//
|
|
// Entry: Pointer to entry
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "ModemTimerEntry_Release"
|
|
|
|
void ModemTimerEntry_Release( void *pvItem )
|
|
{
|
|
TIMER_OPERATION_ENTRY *pTimerEntry;
|
|
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
|
|
pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
|
|
pTimerEntry->pContext= NULL;
|
|
|
|
DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
//**********************************************************************
|
|
// ------------------------------
|
|
// ModemTimerEntry_Dealloc - deallocate a timer job entry
|
|
//
|
|
// Entry: Pointer to entry
|
|
//
|
|
// Exit: Nothing
|
|
// ------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "ModemTimerEntry_Dealloc"
|
|
|
|
void ModemTimerEntry_Dealloc( void *pvItem )
|
|
{
|
|
TIMER_OPERATION_ENTRY *pTimerEntry;
|
|
|
|
|
|
DNASSERT( pvItem != NULL );
|
|
|
|
//
|
|
// initialize
|
|
//
|
|
pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
|
|
|
|
//
|
|
// return associated poiner to write data
|
|
//
|
|
DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
|
|
DNASSERT( pTimerEntry->pContext == NULL );
|
|
}
|
|
//**********************************************************************
|
|
|
|
|
|
|