2025-04-27 07:49:33 -04:00

2379 lines
61 KiB
C++

/*==========================================================================
*
* Copyright (C) 1998-2000 Microsoft Corporation. All Rights Reserved.
*
* File: WorkThread.cpp
* Content: main job processing functions
*
*
* History:
* Date By Reason
* ==== == ======
* 11/25/98 jtk Created
***************************************************************************/
#include "dnmdmi.h"
#define DPF_MODNAME "WorkThread"
//**********************************************************************
// Constant definitions
//**********************************************************************
//
// events for NT enum thread
//
#define EVENT_INDEX_ENUM_WAKEUP 1
//
// version ID for Win32 OSes
//
#define WIN_98_VERSION 4
#define WIN_NT4_VERSION 4
//
// events for Win9x enum thread
//
#define EVENT_INDEX_SP_CLOSE 0
#define EVENT_INDEX_PENDING_JOB 1
#define EVENT_INDEX_TAPI_MESSAGE 2
//
// times to wait in milliseconds when polling for work thread shutdown
//
#define WORK_THREAD_CLOSE_WAIT_TIME 10000
#define WORK_THREAD_CLOSE_SLEEP_TIME 100
//**********************************************************************
// Macro definitions
//**********************************************************************
//**********************************************************************
// Structure definitions
//**********************************************************************
//
// information passed to the Win9x workhorse thread
//
typedef struct _WIN9X_THREAD_DATA
{
CWorkThread *pThisObject; // pointer to this object
} WIN9X_THREAD_DATA;
//
// information passed to the IOCompletion thread
//
typedef struct _IOCOMPLETION_THREAD_DATA
{
CWorkThread *pThisObject; // pointer to this object
} IOCOMPLETION_THREAD_DATA;
// structure for common data in Win9x thread
typedef struct _WIN9X_CORE_DATA
{
DWORD dwHandleCount; // total handle count
DWORD dwActivePortCount; // count of active COM ports
DWORD dwBaseHandleCount; // count of base handles (SP_CLOSE, PENDING_JOB, TAPI_MESSAGE)
DWORD dwCurrentTimeout; // current wait timeout
DWORD dwLastEnumTime; // last time enums ran
HANDLE hHandles[ MAX_WIN9X_HANDLE_COUNT ]; // handles to wait on
JKIO_DATA *ActivePortDataPointers[ MAX_ACTIVE_WIN9X_ENDPOINTS * 2 ]; // pointers to COM IO structures
} WIN9X_CORE_DATA;
//**********************************************************************
// Variable definitions
//**********************************************************************
//**********************************************************************
// Function prototypes
//**********************************************************************
//**********************************************************************
// Function definitions
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::CWorkThread - constructor
//
// Entry: Nothing
//
// Exit: Nothing
// ------------------------------
CWorkThread::CWorkThread():m_pJobQueueHead( NULL ),
m_pJobQueueTail( NULL ),
m_uThreadCount( 0 ),
m_hPendingJob( NULL ),
m_fNTEnumThreadRunning( FALSE ),
m_hWakeNTEnumThread( NULL ),
m_pSPData( NULL )
//:m_dwThreadCount( 0 ),m_hPendingJob( NULL ),
// m_hTAPIEvent( NULL ),m_hSPClose( NULL ),m_pThis( NULL ),
// m_fNTEnumThreadRunning( FALSE ),m_hWakeNTEnumThread( NULL )
{
DEBUG_ONLY( m_fInitialized = FALSE );
m_DNSPInterface.pCOMInterface = NULL;
memset( &m_NTEnumThreadData, 0x00, sizeof( m_NTEnumThreadData ) );
memset( &m_EnumList, 0x00, sizeof( m_EnumList ) );
m_EnumList.Linkage.Initialize();
// memset( &m_EnumList, 0x00, sizeof( m_EnumList ) );
// memset( &m_JobQueue, 0x00, sizeof( m_JobQueue ) );
// InitializeCriticalSection( &m_Lock );
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::~CWorkThread - destructor
//
// Entry: Nothing
//
// Exit: Error Code
// ------------------------------
CWorkThread::~CWorkThread()
{
DNASSERT( m_fInitialized == FALSE );
DNASSERT( m_pJobQueueHead == NULL );
DNASSERT( m_pJobQueueTail == NULL );
DNASSERT( m_uThreadCount == 0 );
DNASSERT( m_hPendingJob == NULL );
DNASSERT( m_EnumList.Linkage.IsEmpty() != FALSE );
DNASSERT( m_fNTEnumThreadRunning == FALSE );
DNASSERT( m_hWakeNTEnumThread == NULL );
DNASSERT( m_pSPData == NULL );
// DNASSERT( m_dwThreadCount == 0 );
// DNASSERT( m_hPendingJob == NULL );
// DNASSERT( m_hSPClose == NULL );
// DNASSERT( m_hTAPIEvent == NULL );
// DNASSERT( m_pThis == NULL );
// DNASSERT( m_fNTEnumThreadRunning == FALSE );
// DeleteCriticalSection( &m_Lock );
DBG_CASSERT( LENGTHOF( m_NTEnumThreadData.hEventList ) == 2 );
DNASSERT( m_NTEnumThreadData.hEventList[ 0 ] == NULL );
DNASSERT( m_NTEnumThreadData.hEventList[ 1 ] == NULL );
DNASSERT( m_NTEnumThreadData.pThisWorkThread == NULL );
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::Initialize - initialize work threads
//
// Entry: DirectNet interface
//
// Exit: Error Code
// ------------------------------
HRESULT CWorkThread::Initialize( const DNSPINTERFACE DNSPInterface )
{
HRESULT hr;
HRESULT hTempResult;
DNASSERT( DNSPInterface.pCOMInterface != NULL );
// DNASSERT( hSPClose != NULL );
// // don't check TAPI event, it will be NULL for serial port
//
//
// initialize
//
hr = DPN_OK;
m_DNSPInterface = DNSPInterface;
m_pSPData = m_DNSPInterface.pDataInterface->pSPData;
DNASSERT( m_pSPData != NULL );
//
// initialize critical sections
//
// object lock
if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
{
hr = DPNERR_OUTOFMEMORY;
DPFX(DPFPREP, 0, "Initialize: Failed to initialize lock!" );
goto Failure;
}
DebugSetCriticalSectionRecursionCount( &m_Lock, 0 );
// job data lock
if ( DNInitializeCriticalSection( &m_JobDataLock ) == FALSE )
{
hr = DPNERR_OUTOFMEMORY;
DPFX(DPFPREP, 0, "Initialize: failed to initialize job data lock!" );
goto Failure;
}
DebugSetCriticalSectionRecursionCount( &m_JobDataLock, 0 );
// enum data lock
if ( DNInitializeCriticalSection( &m_EnumDataLock ) == FALSE )
{
hr = DPNERR_OUTOFMEMORY;
DPFX(DPFPREP, 0, "Initialize: failed to initialize enum data lock!" );
goto Failure;
}
DebugSetCriticalSectionRecursionCount( &m_EnumDataLock, 0 );
// m_hSPClose = hSPClose;
// m_hTAPIEvent = hTAPIEvent;
//
// initialize pools
//
// job pool
FPM_Initialize( &m_JobPool, // pointer to pool
sizeof( WORK_THREAD_JOB ), // size of pool entry
WorkThreadJob_Alloc, // function called on pool entry initial allocation
WorkThreadJob_Get, // function called on entry extraction from pool
WorkThreadJob_Release, // function called on entry return to pool
WorkThreadJob_Dealloc // function called on entry free
);
// enum entry pool
FPM_Initialize( &m_EnumEntryPool, // pointer to pool
sizeof( ENUM_ENTRY ), // size of pool entry
EnumEntry_Alloc, // function called on pool entry initial allocation
EnumEntry_Get, // function called on entry extraction from pool
EnumEntry_Release, // function called on entry return to pool
EnumEntry_Dealloc // function called on entry free
);
//
// what OS are we working with?
//
#ifdef WINNT
//
// WinNT
//
SYSTEM_INFO SystemInfo;
UINT_PTR uDesiredThreads;
//
// get machine information to spin up IOCompletionPort threads
// ( ( processors * 2 ) + 2 ) as suggested by 'Multithreading
// Applications in Win32' book
//
memset( &SystemInfo, 0x00, sizeof( SystemInfo ) );
GetSystemInfo( &SystemInfo );
DNASSERT( m_pSPData->GetIOCompletionPort() != NULL );
DNASSERT( m_uThreadCount == 0 );
uDesiredThreads = ( SystemInfo.dwNumberOfProcessors * 2 ) + 2;
DNASSERT( uDesiredThreads != 0 );
while ( uDesiredThreads > 0 )
{
HANDLE hThread;
DWORD dwThreadID;
IOCOMPLETION_THREAD_DATA *pIOCompletionThreadData;
uDesiredThreads--;
//
// If we can allocated thread data then start a thread
//
pIOCompletionThreadData = static_cast<IOCOMPLETION_THREAD_DATA*>( DNMalloc( sizeof( *pIOCompletionThreadData ) ) );
if ( pIOCompletionThreadData != NULL )
{
pIOCompletionThreadData->pThisObject = this;
hThread = CreateThread( NULL, // pointer to security attributes (none)
0, // stack size (default)
IOCompletionThread, // 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 thread
// handle because we don't need it anymore
//
IncrementActiveThreadCount();
if ( CloseHandle( 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: 0x%d", uDesiredThreads );
DisplayErrorCode( 0, dwError );
DNFree( pIOCompletionThreadData );
}
}
}
//
// the SP can function with at least one thread
//
if ( m_uThreadCount == 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
//
HANDLE hThread;
DWORD dwThreadID;
WIN9X_THREAD_DATA *pInput;
//
// create event for new job in job list (only under Win9x)
//
DNASSERT( m_hPendingJob == NULL );
m_hPendingJob = CreateEvent( NULL, // pointer to security attributes (default)
TRUE, // manual reset
FALSE, // start unsignalled
NULL // pointer to name (none)
);
if ( m_hPendingJob == NULL )
{
hr = DPNERR_OUTOFMEMORY;
DPFX(DPFPREP, 0, "Cannot create event for m_hPendingJob!" );
goto Failure;
}
// create main worker thread to handle everything
pInput = static_cast<WIN9X_THREAD_DATA*>( DNMalloc( sizeof( *pInput ) ) );
if ( pInput == NULL )
{
DPFX(DPFPREP, 0, "Problem allocating memory for Win9x thread!" );
hr = DPNERR_OUTOFMEMORY;
goto Failure;
}
memset( pInput, 0x00, sizeof( *pInput ) );
pInput->pThisObject = this;
//
// create one worker thread and attempt to boost its priority
//
hThread = CreateThread( NULL, // pointer to security attributes (none)
0, // stack size (default)
Win9xThread, // pointer to thread function
pInput, // pointer to input parameter
0, // let it run
&dwThreadID // pointer to destination of thread ID
);
if ( hThread == NULL )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem creating Win9x thread!" );
DisplayErrorCode( 0, dwError );
hr = DPNERR_OUTOFMEMORY;
DNASSERT( pInput != NULL );
DNFree( pInput );
pInput = NULL;
goto Failure;
}
DNASSERT( hThread != NULL );
#ifdef ADJUST_THREAD_PRIORITY
if ( SetThreadPriority( hThread, THREAD_PRIORITY_ABOVE_NORMAL ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Could not boost thread priority on Win9x work thread!" );
DisplayErrorCode( 0, dwError );
//
// Not fatal, just continue.
//
}
#endif // ADJUST_THREAD_PRIORITY
//
// note that we've started a thread, and close the thread handle
//
IncrementActiveThreadCount();
if ( CloseHandle( hThread ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem closing handle to Win9xThread!" );
DisplayErrorCode( 0, dwError );
}
#endif
DEBUG_ONLY( m_fInitialized = TRUE );
Exit:
if ( hr != DPN_OK )
{
DPFX(DPFPREP, 0, "Problem with CreateWorkThreads" );
DisplayDNError( 0, hr );
}
return hr;
Failure:
hTempResult = StopAllThreads();
if ( hTempResult != DPN_OK )
{
DPFX(DPFPREP, 0, "Problem stopping all threads!" );
DisplayDNError( 0, hTempResult );
}
hTempResult = Deinitialize();
if ( hTempResult != DPN_OK )
{
DPFX(DPFPREP, 0, "Initialize: Problem deinitializing work thread on failure!" );
DisplayDNError( 0, hTempResult );
}
goto Exit;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::StopAllThreads - stop all work threads
//
// Entry: Nothing
//
// Exit: Error Code
// ------------------------------
HRESULT CWorkThread::StopAllThreads( void )
{
HRESULT hr;
UINT_PTR uLoopCount;
DWORD dwWaitReturn;
//
// initialize
//
hr = DPN_OK;
//
// compute wait timeslice for all threads to quit
//
uLoopCount = WORK_THREAD_CLOSE_WAIT_TIME / WORK_THREAD_CLOSE_SLEEP_TIME;
//
// verify that threads are stopping
//
DNASSERT( m_pSPData != NULL );
dwWaitReturn = WaitForSingleObject( m_pSPData->GetSPCloseHandle(), 0 );
switch( dwWaitReturn )
{
case WAIT_OBJECT_0:
{
break;
}
case WAIT_ABANDONED:
{
DNASSERT( FALSE );
break;
}
case WAIT_TIMEOUT:
{
DNASSERT( FALSE );
break;
}
default:
{
DNASSERT( FALSE );
break;
}
}
//
// WinNT: Spool an IO completion message for each outstanding thead. this may
// cause extra IO completion messages for threads not waiting on the completion
// port, but the OS will clean those up.
//
#ifdef WINNT
UINT_PTR uIndex;
uIndex = m_uThreadCount;
while ( uIndex > 0 )
{
uIndex--;
if ( PostQueuedCompletionStatus( m_pSPData->GetIOCompletionPort(), // 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 );
DNASSERT( FALSE );
}
}
#endif
//
// check for outstanding threads
//
DPFX(DPFPREP, 0,"Number of outstanding threads: %d", m_uThreadCount );
while ( ( uLoopCount > 0 ) && ( m_uThreadCount != 0 ) )
{
DPFX(DPFPREP, 8, "Waiting for %d threads to quit: %d", m_uThreadCount, uLoopCount );
uLoopCount--;
SleepEx( WORK_THREAD_CLOSE_SLEEP_TIME, TRUE );
}
DNASSERT( uLoopCount != 0 );
DNASSERT( m_uThreadCount == 0 );
m_uThreadCount = 0;
return hr;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::Deinitialize - destroy work threads
//
// Entry: Nothing
//
// Exit: Error Code
// ------------------------------
HRESULT CWorkThread::Deinitialize( void )
{
HRESULT hr;
//
// initialize
//
hr = DPN_OK;
// while ( Queue_IsEmpty( &m_JobQueue ) == FALSE )
// {
// JOB_HEADER *pJobHeader;
//
//
// pJobHeader = static_cast<JOB_HEADER*>( Queue_DeQ( &m_JobQueue ) );
// DNASSERT( pJobHeader != NULL );
// DNFree( pJobHeader );
// }
//
// DNASSERT( Queue_IsEmpty( &m_JobQueue ) );
// Queue_Deinitialize( &m_JobQueue );
//
// deinitialize pools
//
// enum entry pool
FPM_Deinitialize( &m_EnumEntryPool );
// job pool
FPM_Deinitialize( &m_JobPool );
// // close handle signalling new jobs
// if ( m_hPendingJob != NULL )
// {
// if ( CloseHandle( m_hPendingJob ) == FALSE )
// {
// DPFX(DPFPREP, 0, "Problem with CWorkThread::Deinitialize::CloseHandle( m_hPendingJob )!" );
// DisplayErrorCode( 0, GetLastError() );
// }
//
// m_hPendingJob = NULL;
// }
//
// DEBUG_ONLY( m_hSPClose = NULL );
// DEBUG_ONLY( m_hTAPIEvent = NULL );
// DEBUG_ONLY( m_pThis = NULL );
m_DNSPInterface.pCOMInterface = NULL;
m_pSPData = NULL;
DNDeleteCriticalSection( &m_EnumDataLock );
DNDeleteCriticalSection( &m_JobDataLock );
DNDeleteCriticalSection( &m_Lock );
DEBUG_ONLY( m_fInitialized = FALSE );
return hr;
}
//**********************************************************************
////**********************************************************************
//// ------------------------------
//// CWorkThread::ProcessEnums - process enumerations
////
//// Entry: Pointer to new wait timeout
//// Pointer to last enum time
////
//// Exit: Boolean indicating active enums exist
//// TRUE = there are active enums
//// FALSE = there are no active enums
//// ------------------------------
//BOOL CWorkThread::ProcessEnums( DWORD *pdwWaitTimeout, DWORD *pdwLastEnumTime )
//{
// BOOL fReturn;
// DWORD dwDeltaT;
// DWORD dwCurrentTime;
// ENUM_ENTRY *pEnumEntry;
// DWORD dwActiveEnumCount;
//
//
// DNASSERT( pdwWaitTimeout != NULL );
// DNASSERT( pdwLastEnumTime != NULL );
//
// Lock();
//
// // initialize
// fReturn = FALSE;
// DBG_CASSERT( OFFSETOF( ENUM_ENTRY, Linkage ) == 0 );
// pEnumEntry = reinterpret_cast<ENUM_ENTRY*>( m_EnumList.Linkage.next );
// *pdwWaitTimeout = INFINITE;
// dwActiveEnumCount = 0;
//
// // compute time delta between last enums
// dwCurrentTime = GetTickCount();
// dwDeltaT = dwCurrentTime - *pdwLastEnumTime;
// *pdwLastEnumTime = dwCurrentTime;
// if ( static_cast<INT>( dwDeltaT ) < 0 )
// {
// dwDeltaT = -( static_cast<INT>( dwDeltaT ) );
// }
//
// // loop through all enums
// while ( pEnumEntry != NULL )
// {
// // note that there's an active enum
// dwActiveEnumCount++;
//
// // are we just waiting for enum responses?
// if ( pEnumEntry->dwRetryCount == 0 )
// {
// // are we waiting forever for responses?
// if ( pEnumEntry->fWaitForever == FALSE )
// {
// // not waiting forever, decrement wait timer
// pEnumEntry->dwTimeout -= dwDeltaT;
// if ( static_cast<INT>( pEnumEntry->dwTimeout ) <= 0 )
// {
// ENUM_ENTRY *pTemp;
//
//
// pTemp = pEnumEntry;
//
// // back up one node so the loop will advance us to the next node
// // we're always guaranteed that at least the dummy node is before us
// DBG_CASSERT( OFFSETOF( ENUM_ENTRY, Linkage ) == 0 );
// pEnumEntry = reinterpret_cast<ENUM_ENTRY*>( pEnumEntry->Linkage.GetPrev() );
//
//// pTemp->pEnumCompleteFn( DPN_OK );
// pTemp->pEndpoint->EnumComplete( DPN_OK );
//
// RemoveEnumEntry( pTemp );
//
// // note that this enum is no longer active
// DNASSERT( dwActiveEnumCount != 0 );
// dwActiveEnumCount--;
// }
// else
// {
// // we're not done enuming, wake up for us if we're next
// if ( pEnumEntry->dwTimeout < ( *pdwWaitTimeout ) )
// {
// *pdwWaitTimeout = pEnumEntry->dwTimeout;
// }
// }
// }
// }
// else
// {
// // we're still sending, adjust enum timeout
// pEnumEntry->dwTimeToNextEnum -= dwDeltaT;
// if ( static_cast<INT>( pEnumEntry->dwTimeToNextEnum ) <= 0 )
// {
// HRESULT hTempResult;
//
//
// // timeout, send new enum
// if ( ( hTempResult = pEnumEntry->pEnumSendFn( pEnumEntry->pEndpoint, &pEnumEntry->MessageInfo ) ) != DPN_OK )
// {
// DPFX(DPFPREP, 0, "Problem with enum send function!" );
// DisplayDNError( 0, hTempResult );
// }
//
// if ( pEnumEntry->fEnumForever == FALSE )
// {
// pEnumEntry->dwRetryCount--;
// }
//
// // set up for next enum
// pEnumEntry->dwTimeToNextEnum += pEnumEntry->dwRetryInterval;
// }
//
// // adjust for long waits
// if ( static_cast<INT>( pEnumEntry->dwTimeToNextEnum ) <= 0 )
// {
// INT iMultiplier;
//
// iMultiplier = static_cast<INT>( pEnumEntry->dwTimeToNextEnum ) / static_cast<INT>( pEnumEntry->dwRetryInterval );
// pEnumEntry->dwTimeToNextEnum = pEnumEntry->dwTimeToNextEnum - ( ( iMultiplier - 1 ) * pEnumEntry->dwRetryInterval );
// }
//
// // is this the next enum to fire?
// DNASSERT( static_cast<INT>( pEnumEntry->dwTimeToNextEnum ) > 0 );
// if ( pEnumEntry->dwTimeToNextEnum < (*pdwWaitTimeout) )
// {
// // we've got the shortest timeout time, wake thread up for us
// *pdwWaitTimeout = pEnumEntry->dwTimeToNextEnum;
// }
// }
//
// // next entry
// DBG_CASSERT( sizeof( pEnumEntry ) == sizeof( pEnumEntry->Linkage.next ) );
// DBG_CASSERT( OFFSETOF( ENUM_ENTRY, Linkage ) == 0 );
// pEnumEntry = reinterpret_cast<ENUM_ENTRY*>( pEnumEntry->Linkage.next );
// }
//
// Unlock();
//
// if ( dwActiveEnumCount != 0 )
// {
// DNASSERT( static_cast<INT>( dwActiveEnumCount ) > 0 );
// fReturn = TRUE;
// }
//
// return fReturn;
//}
////**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::SubmitWorkItem - submit a work item for processing and inform workhorse that
// another job is available
//
// Entry: Pointer to job information
//
// Exit: Error code
// ------------------------------
HRESULT CWorkThread::SubmitWorkItem( WORK_THREAD_JOB *const pJobInfo )
{
HRESULT hr;
DNASSERT( pJobInfo != NULL );
AssertCriticalSectionIsTakenByThisThread( &m_JobDataLock, TRUE );
//
// initialize
//
hr = DPN_OK;
//
// add job to queue
//
EnqueueJob( pJobInfo );
#ifdef WINNT
//
// WinNT, submit new I/O completion item
//
DNASSERT( m_hPendingJob == NULL );
if ( PostQueuedCompletionStatus( m_pSPData->GetIOCompletionPort(), // 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_hPendingJob != NULL );
if ( SetEvent( m_hPendingJob ) == FALSE )
{
DWORD dwError;
hr = DPNERR_GENERIC;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Cannot set event for pending job!" );
DisplayErrorCode( 0, dwError );
goto Failure;
}
#endif
Exit:
if ( hr != DPN_OK )
{
DPFX(DPFPREP, 0, "Problem with SubmitWorkItem!" );
DisplayDNError( 0, hr );
}
return hr;
Failure:
goto Exit;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::GetWorkItem - get a work item from the job queue
//
// Entry: Nothing
//
// Exit: Pointer to job information (may be NULL)
// ------------------------------
WORK_THREAD_JOB *CWorkThread::GetWorkItem( void )
{
WORK_THREAD_JOB *pReturn;
//
// initialize
//
pReturn = NULL;
LockJobData();
pReturn = DequeueJob();
//
// if we're under Win9x (we have a 'pending job' handle),
// see if the handle needs to be reset
//
if ( m_hPendingJob != NULL )
{
if ( JobQueueIsEmpty() != FALSE )
{
if ( ResetEvent( m_hPendingJob ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Poblem resetting event for pending Win9x jobs!" );
DisplayErrorCode( 0, dwError );
}
}
}
UnlockJobData();
return pReturn;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::SubmitEnumJob - add an enum job to the enum list
//
// Entry: Pointer to enum data
// Pointer to associated endpoint
//
// Exit: Error code
// ------------------------------
HRESULT CWorkThread::SubmitEnumJob( const SPENUMQUERYDATA *const pQueryData,
CEndpoint *const pEndpoint )
{
HRESULT hr;
ENUM_ENTRY *pEntry;
WORK_THREAD_JOB *pJob;
DNASSERT( pQueryData != NULL );
DNASSERT( pEndpoint != NULL );
//
// initialize
//
hr = DPN_OK;
DNASSERT( m_DNSPInterface.pCOMInterface != NULL );
// pEntry = NULL;
// pJob = NULL;
//
// Lock();
LockEnumData();
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.
//
DNASSERT( m_DNSPInterface.pDataInterface != NULL );
#ifdef WINNT
hr = StartNTEnumThread();
if ( hr != DPN_OK )
{
DPFX(DPFPREP, 0, "Cannot spin up NT enum thread!" );
DisplayDNError( 0, hr );
goto Failure;
}
#endif
//
// allocate new enum entry
//
pEntry = static_cast<ENUM_ENTRY*>( m_EnumEntryPool.Get( &m_EnumEntryPool ) );
if ( pEntry == NULL )
{
hr = DPNERR_OUTOFMEMORY;
DPFX(DPFPREP, 0, "Cannot allocate memory to add to enum list!" );
goto Failure;
}
DNASSERT( pEntry->pEnumCommandData == NULL );
//
// check retry count to determine if we're enumerating forever
//
if ( pQueryData->dwRetryCount == 0 )
{
pEntry->uRetryCount = 1;
pEntry->fEnumForever = TRUE;
}
else
{
pEntry->uRetryCount = pQueryData->dwRetryCount;
pEntry->fEnumForever = FALSE;
}
DBG_CASSERT( sizeof( pQueryData->dwRetryInterval ) == sizeof( DWORD ) );
pEntry->RetryInterval.Time32.TimeHigh = 0;
pEntry->RetryInterval.Time32.TimeLow = pQueryData->dwRetryInterval;
pEntry->pEnumCommandData = static_cast<CCommandData*>( pQueryData->hCommand );
pEntry->pEnumCommandData->AddRef();
//
// check timeout to determine if we're waiting forever
//
if ( pQueryData->dwTimeout == 0 )
{
// DBG_CASSERT( sizeof( pEntry->Timeout.Time64 ) == sizeof( DWORD ) );
pEntry->Timeout.Time32.TimeHigh = -1;
pEntry->Timeout.Time32.TimeLow = -1;
pEntry->fWaitForever = TRUE;
}
else
{
DBG_CASSERT( sizeof( pQueryData->dwTimeout ) == sizeof( DWORD ) );
pEntry->Timeout.Time32.TimeHigh = 0;
pEntry->Timeout.Time32.TimeLow = pQueryData->dwTimeout;
pEntry->fWaitForever = FALSE;
}
//
// set this enum to fire as soon as it gets a chance
//
memset( &pEntry->NextEnumTime, 0x00, sizeof( pEntry->NextEnumTime ) );
pEntry->pEndpoint = pEndpoint;
pJob = static_cast<WORK_THREAD_JOB*>( m_JobPool.Get( &m_JobPool ) );
if ( pJob == NULL )
{
hr = DPNERR_OUTOFMEMORY;
DPFX(DPFPREP, 0, "Cannot allocate memory for enum job!" );
goto Failure;
}
//
// create job for workhorse thread
//
pJob->pCancelFunction = CancelRefreshEnum;
pJob->JobType = JOB_REFRESH_ENUM;
// set our dummy paramter to simulate passing data
DEBUG_ONLY( pJob->JobData.JobRefreshEnum.uDummy = 0 );
//
// 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 job queue locked
//
hr = SubmitWorkItem( pJob );
if ( hr != DPN_OK )
{
DPFX(DPFPREP, 0, "Problem submitting enum work item" );
DisplayDNError( 0, hr );
goto Failure;
}
//
// link to rest of list
//
pEntry->Linkage.InsertAfter( &m_EnumList.Linkage );
Exit:
UnlockEnumData();
UnlockJobData();
if ( hr != DPN_OK )
{
DPFX(DPFPREP, 0, "Problem with SubmitEnumJob" );
DisplayDNError( 0, hr );
}
return hr;
Failure:
if ( pEntry != NULL )
{
if ( pEntry->pEnumCommandData != NULL )
{
if ( pEntry->pEnumCommandData->Release() == 0 )
{
m_pSPData->ReturnCommand( pEntry->pEnumCommandData );
}
DEBUG_ONLY( pEntry->pEnumCommandData = NULL );
}
m_EnumEntryPool.Release( &m_EnumEntryPool, pEntry );
DEBUG_ONLY( pEntry = NULL );
}
if ( pJob != NULL )
{
m_JobPool.Release( &m_JobPool, 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;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::CancelRefreshEnum - cancel job to refresh enums
//
// Entry: Job data
//
// Exit: Nothing
// ------------------------------
void CWorkThread::CancelRefreshEnum( WORK_THREAD_JOB *const pJob )
{
DNASSERT( pJob != NULL );
//
// this function doesn't need to do anything
//
}
//**********************************************************************
////**********************************************************************
//// ------------------------------
//// CWorkThread::StopEnumJob - remove enum job from list
////
//// Entry: Pointer to command
////
//// Exit: Nothing
////
//// Note: This function is for the forced removal of a job from the enum
//// list. It is assumed that the caller of this function will
//// clean up any messes.
//// ------------------------------
//void CWorkThread::StopEnumJob( CCommandData *const pCommand )
//{
// CBilink *pTempEntry;
//
//
// DNASSERT( pCommand != NULL );
//
// // initialize
// Lock();
//
// pTempEntry = m_EnumList.Linkage.GetNext();
// while ( pTempEntry != &m_EnumList )
// {
// ENUM_ENTRY *pEnumEntry;
//
//
// pEnumEntry = pTempEntry->EnumEntryFromBilink( pTempEntry );
// if ( pEnumEntry->pCommandData == pCommand )
// {
// RemoveEnumEntry( reinterpret_cast<ENUM_ENTRY*>( pTempEntry ) );
//
// // stop loop
// pTempEntry = NULL;
// }
// else
// {
// pTempEntry = pTempEntry->GetNext();
// }
// }
//
// Unlock();
//}
////**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::SubmitDelayedCommand - submit request to enum query to remote session
//
// Entry: Pointer to callback function
// Pointer to callback context
//
// Exit: Error code
// ------------------------------
HRESULT CWorkThread::SubmitDelayedCommand( JOB_FUNCTION *const pCommandFunction,
JOB_FUNCTION *const pCancelFunction,
void *pContext )
{
HRESULT hr;
WORK_THREAD_JOB *pJob;
BOOL fJobDataLocked;
DNASSERT( pCommandFunction != NULL );
DNASSERT( pCancelFunction != NULL );
//
// initialize
//
hr = DPN_OK;
pJob = NULL;
fJobDataLocked = FALSE;
pJob = static_cast<WORK_THREAD_JOB*>( m_JobPool.Get( &m_JobPool ) );
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 = pCommandFunction;
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 )
{
m_JobPool.Release( &m_JobPool, pJob );
pJob = NULL;
}
goto Exit;
}
//**********************************************************************
////**********************************************************************
//// ------------------------------
//// CWorkThread::SubmitAddPort - add a port to the Win9x watch list
////
//// Entry: Endpoint reference
////
//// Exit: Error code
//// ------------------------------
//HRESULT CWorkThread::SubmitAddPort( CEndpoint &Endpoint )
//{
// HRESULT hr;
// JOB_DATA_ADD_PORT *pJobData;
//
//
// // initialize
// hr = DPN_OK;
// pJobData = NULL;
//
// // allocate memory for job data
// pJobData = static_cast<JOB_DATA_ADD_PORT*>( DNMalloc( sizeof( *pJobData ) ) );
// if ( pJobData == NULL )
// {
// hr = DPNERR_OUTOFMEMORY;
// DPFX(DPFPREP, 0, "Cannot allocate memory for SubmitAddPort job!" );
// goto Failure;
// }
//
// // set information
// pJobData->Header.dwCommandID = 0;
// pJobData->Header.JobType = JOB_ADD_WIN9X_PORT;
// pJobData->Header.pProcessFunction = NULL;
// pJobData->Header.pCancelFunction = NULL;
// pJobData->pReadPortData = Endpoint.GetReadData();
// pJobData->pWritePortData = Endpoint.GetWriteData();
//
// DBG_CASSERT( OFFSETOF( JOB_DATA_ADD_PORT, Header ) == 0 );
// if ( ( hr = SubmitWorkItem( &pJobData->Header ) ) != DPN_OK )
// {
// DPFX(DPFPREP, 0, "Problem submitting AddPort job!" );
// DisplayDNError( 0, hr );
// goto Failure;
// }
//
//Exit:
// return hr;
//
//Failure:
// if ( pJobData != NULL )
// {
// DNFree( pJobData );
// }
//
// goto Exit;
//}
////**********************************************************************
//
//
////**********************************************************************
//// ------------------------------
//// CWorkThread::SubmitRemovePort - remove a port from the Win9x watch list
////
//// Entry: Pointer to read data
//// Pointer to write data
////
//// Exit: Error code
//// ------------------------------
//HRESULT CWorkThread::SubmitRemovePort( IO_DATA *pReadData, IO_DATA *pWriteData )
//{
// HRESULT hr;
// JOB_DATA_REMOVE_PORT *pJobData;
//
//
// // initialize
// hr = DPN_OK;
// pJobData = NULL;
//
// // allocate memory for job data
// pJobData = static_cast<JOB_DATA_REMOVE_PORT*>( DNMalloc( sizeof( *pJobData ) ) );
// if ( pJobData == NULL )
// {
// hr = DPNERR_OUTOFMEMORY;
// DPFX(DPFPREP, 0, "Cannot allocate memory for SubmitRemovePort job!" );
// goto Failure;
// }
//
// // set information
// pJobData->Header.dwCommandID = 0;
// pJobData->Header.JobType = JOB_REMOVE_WIN9X_PORT;
// pJobData->Header.pProcessFunction = NULL;
// pJobData->Header.pCancelFunction = NULL;
// pJobData->pReadPortData = pReadData;
// pJobData->pWritePortData = pWriteData;
//
// DBG_CASSERT( OFFSETOF( JOB_DATA_REMOVE_PORT, Header ) == 0 );
// if ( ( hr = SubmitWorkItem( &pJobData->Header ) ) != DPN_OK )
// {
// DPFX(DPFPREP, 0, "Problem submitting RemovePort job!" );
// DisplayDNError( 0, hr );
// goto Failure;
// }
//
//Exit:
// return hr;
//
//Failure:
// if ( pJobData != NULL )
// {
// DNFree( pJobData );
// }
//
// goto Exit;
//}
////**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::ProcessEnums - process enumerations
//
// Entry: Pointer to destination for time of next enum
//
// Exit: Boolean indicating active enums exist
// TRUE = there are active enums
// FALSE = there are no active enums
// ------------------------------
BOOL CWorkThread::ProcessEnums( DN_TIME *const pNextEnumTime )
{
BOOL fReturn;
ENUM_ENTRY *pEnumEntry;
INT_PTR iActiveEnumCount;
DN_TIME CurrentTime;
LockEnumData();
//
// initialize
//
fReturn = FALSE;
DBG_CASSERT( OFFSETOF( ENUM_ENTRY, Linkage ) == 0 );
pEnumEntry = reinterpret_cast<ENUM_ENTRY*>( m_EnumList.Linkage.GetNext() );
iActiveEnumCount = 0;
memset( pNextEnumTime, 0xFF, sizeof( *pNextEnumTime ) );
DNTimeGet( &CurrentTime );
//
// if we're under NT, acknowledge that we've handled this event
//
if ( m_hWakeNTEnumThread != NULL )
{
if ( ResetEvent( m_hWakeNTEnumThread ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem resetting event to wake NT enum thread!" );
DisplayErrorCode( 0, dwError );
}
}
//
// loop through all enums
//
while ( pEnumEntry != &m_EnumList )
{
//
// Note that there's an active enum. If we retire this enum, this
// count will be decremented.
//
iActiveEnumCount++;
//
// 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 exceed its wait time.
//
if ( pEnumEntry->uRetryCount == 0 )
{
if ( DNTimeCompare( &pEnumEntry->Timeout, &CurrentTime ) <= 0 )
{
ENUM_ENTRY *pTemp;
//
// Back up one node so the next loop iteration will advance us
// to the next node. We're guaranteed this is possible because
// the list has a dummy node at the front.
//
pTemp = pEnumEntry;
DBG_CASSERT( OFFSETOF( ENUM_ENTRY, Linkage ) == 0 );
pEnumEntry = reinterpret_cast<ENUM_ENTRY*>( pEnumEntry->Linkage.GetPrev() );
pTemp->pEndpoint->EnumComplete( DPN_OK );
RemoveEnumEntry( pTemp );
//
// note that this enum is no longer active
//
DNASSERT( iActiveEnumCount > 0 );
iActiveEnumCount--;
}
else
{
//
// This enum isn't complete, check to see if it's the next enum
// to need service.
//
if ( DNTimeCompare( &pEnumEntry->Timeout, pNextEnumTime ) < 0 )
{
DBG_CASSERT( sizeof( *pNextEnumTime ) == sizeof( pEnumEntry->Timeout ) );
memcpy( pNextEnumTime, &pEnumEntry->Timeout, sizeof( *pNextEnumTime ) );
}
}
}
else
{
//
// This enum is still sending. Determine if it's time to send a new enum
// and adjust the wakeup time if appropriate.
//
if ( DNTimeCompare( &pEnumEntry->NextEnumTime, &CurrentTime ) <= 0 )
{
HRESULT hTempResult;
//
// Timeout, send new enum and set it to fire again with its
// normal 'timeout'.
//
hTempResult = pEnumEntry->pEndpoint->SendEnumData();
if ( hTempResult != DPN_OK )
{
DPFX(DPFPREP, 0, "Problem with enum send function!" );
DisplayDNError( 0, hTempResult );
}
//
// If this enum isn't running forever, decrement the retry count.
// If there are no more retries, set up wait time. If the enum
// is waiting forever, set max wait timeout.
//
if ( pEnumEntry->fEnumForever == FALSE )
{
pEnumEntry->uRetryCount--;
if ( pEnumEntry->uRetryCount == 0 )
{
if ( pEnumEntry->fWaitForever == FALSE )
{
//
// Compute stopping time for this enum's 'Timeout' phase and
// see if this will be the next enum to need service. ASSERT
// if the math wraps.
//
DNTimeAdd( &pEnumEntry->Timeout, &CurrentTime, &pEnumEntry->Timeout );
DNASSERT( pEnumEntry->Timeout.Time32.TimeHigh >= CurrentTime.Time32.TimeHigh );
if ( DNTimeCompare( &pEnumEntry->Timeout, pNextEnumTime ) < 0 )
{
DBG_CASSERT( sizeof( *pNextEnumTime ) == sizeof( pEnumEntry->Timeout ) );
memcpy( pNextEnumTime, &pEnumEntry->Timeout, sizeof( *pNextEnumTime ) );
}
}
else
{
// debug me
DNASSERT( FALSE );
//
// 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( pEnumEntry->Timeout.Time32.TimeLow == -1 );
DNASSERT( pEnumEntry->Timeout.Time32.TimeHigh == -1 );
}
goto SkipNextRetryTimeComputation;
}
}
DNTimeAdd( &CurrentTime, &pEnumEntry->RetryInterval, &pEnumEntry->NextEnumTime );
}
//
// is this the next enum to fire?
//
if ( DNTimeCompare( &pEnumEntry->NextEnumTime, pNextEnumTime ) < 0 )
{
DBG_CASSERT( sizeof( *pNextEnumTime ) == sizeof( pEnumEntry->Timeout ) );
memcpy( pNextEnumTime, &pEnumEntry->NextEnumTime, sizeof( *pNextEnumTime ) );
}
SkipNextRetryTimeComputation:
//
// the following blank line is there to shut up the compiler
//
;
}
//
// proceed to next entry
//
DBG_CASSERT( OFFSETOF( ENUM_ENTRY, Linkage ) == 0 );
pEnumEntry = reinterpret_cast<ENUM_ENTRY*>( pEnumEntry->Linkage.GetNext() );
}
UnlockEnumData();
if ( iActiveEnumCount != 0 )
{
DNASSERT( iActiveEnumCount > 0 );
fReturn = TRUE;
}
return fReturn;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::StartNTEnumThread - start the enum thread for NT
//
// Entry: Nothing
//
// Exit: Error code
//
// Note: This function assumes that the enum data is locked.
// ------------------------------
HRESULT CWorkThread::StartNTEnumThread( void )
{
HRESULT hr;
HANDLE hThread;
DWORD dwThreadID;
//
// initialize
//
hr = DPN_OK;
if ( m_fNTEnumThreadRunning != FALSE )
{
//
// the enum thread is already running, poke it to note new enums
//
DNASSERT( m_hWakeNTEnumThread != NULL );
if ( SetEvent( m_hWakeNTEnumThread ) == FALSE )
{
DWORD dwError;
hr = DPNERR_OUTOFMEMORY;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem setting event to wake NTEnumThread!" );
DisplayErrorCode( 0, dwError );
goto Failure;
}
goto Exit;
}
DNASSERT( m_hWakeNTEnumThread == NULL );
m_hWakeNTEnumThread = CreateEvent( NULL, // pointer to security attributes (none)
TRUE, // manual reset
FALSE, // start unsignalled
NULL // pointer to name (none)
);
if ( m_hWakeNTEnumThread == NULL )
{
DWORD dwError;
hr = DPNERR_OUTOFMEMORY;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Could not create event for waking NT enum thread!" );
DisplayErrorCode( 0, dwError );
goto Failure;
}
DNASSERT( m_NTEnumThreadData.pThisWorkThread == NULL );
m_NTEnumThreadData.pThisWorkThread = this;
DNASSERT( m_NTEnumThreadData.hEventList[ EVENT_INDEX_SP_CLOSE ] == NULL );
m_NTEnumThreadData.hEventList[ EVENT_INDEX_SP_CLOSE ] = m_pSPData->GetSPCloseHandle();
DNASSERT( m_NTEnumThreadData.hEventList[ EVENT_INDEX_SP_CLOSE ] != NULL );
DNASSERT( m_NTEnumThreadData.hEventList[ EVENT_INDEX_ENUM_WAKEUP ] == NULL );
m_NTEnumThreadData.hEventList[ EVENT_INDEX_ENUM_WAKEUP ] = m_hWakeNTEnumThread;
DNASSERT( m_NTEnumThreadData.hEventList[ EVENT_INDEX_ENUM_WAKEUP ] != NULL );
hThread = CreateThread( NULL, // pointer to security attributes (none)
0, // stack size (default)
WinNTEnumThread, // thread function
&m_NTEnumThreadData, // 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 enum thread!" );
DisplayErrorCode( 0, dwError );
goto Failure;
}
//
// note that the thread is running
//
m_uThreadCount++;
m_fNTEnumThreadRunning = TRUE;
if ( CloseHandle( hThread ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem closing handle after starting NTEnumThread!" );
DisplayErrorCode( 0, dwError );
}
Exit:
return hr;
Failure:
if ( m_hWakeNTEnumThread != NULL )
{
if ( CloseHandle( m_hWakeNTEnumThread ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem closing handle for 'WakeNTEnumThread'" );
DisplayErrorCode( 0, dwError );
}
m_hWakeNTEnumThread = NULL;
}
goto Exit;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::WakeNTEnumThread - wake the enum thread because an enum has
// been added
//
// Entry: Nothing
//
// Exit: Nothing
// ------------------------------
void CWorkThread::WakeNTEnumThread( void )
{
if ( SetEvent( m_NTEnumThreadData.hEventList[ EVENT_INDEX_ENUM_WAKEUP ] ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem setting event to wake up NT enum thread!" );
DisplayErrorCode( 0, dwError );
}
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::RemoveEnumEntry - remove enum job from list
//
// Entry: Pointer to enum entry
//
// Exit: Nothing
//
// Note: This function assumes that the enum list is apprpriately locked
// ------------------------------
void CWorkThread::RemoveEnumEntry( ENUM_ENTRY *const pEnumData )
{
DNASSERT( pEnumData != NULL );
AssertCriticalSectionIsTakenByThisThread( &m_EnumDataLock, TRUE );
pEnumData->Linkage.RemoveFromList();
//
// return any commands, but don't do any signalling of commands being complete,
// that's someone else's job
//
DNASSERT( pEnumData->pEnumCommandData != NULL );
if ( pEnumData->pEnumCommandData->Release() == 0 )
{
m_pSPData->ReturnCommand( pEnumData->pEnumCommandData );
}
DEBUG_ONLY( pEnumData->pEnumCommandData = NULL );
//
// return entry to pool
//
pEnumData->pEndpoint = NULL;
m_EnumEntryPool.Release( &m_EnumEntryPool, pEnumData );
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::Win9xThread - 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
// ------------------------------
DWORD WINAPI CWorkThread::Win9xThread( void *pParam )
{
WIN9X_THREAD_DATA *pInput;
DNASSERT( pParam != NULL );
//
// initialize
//
DNASSERT( FALSE );
pInput = static_cast<WIN9X_THREAD_DATA *>( pParam );
pInput->pThisObject->DecrementActiveThreadCount();
DNFree( pParam );
return 0;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::IOCompletionThread - 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
// ------------------------------
DWORD WINAPI CWorkThread::IOCompletionThread( void *pParam )
{
IOCOMPLETION_THREAD_DATA *pInput;
BOOL fLooping;
HANDLE hIOCompletionPort;
DNASSERT( pParam != NULL );
//
// initialize
//
pInput = static_cast<IOCOMPLETION_THREAD_DATA*>( pParam );
DNASSERT( pInput->pThisObject != NULL );
fLooping = TRUE;
hIOCompletionPort = pInput->pThisObject->m_pSPData->GetIOCompletionPort();
DNASSERT( hIOCompletionPort != NULL );
//
// go until we're told to stop
//
while ( fLooping != FALSE )
{
BOOL fStatusReturn;
DWORD dwBytesTransferred;
ULONG_PTR uCompletionKey;
OVERLAPPED *pOverlapped;
//
// get data from completion port
//
DNASSERT( hIOCompletionPort != NULL );
fStatusReturn = GetQueuedCompletionStatus( hIOCompletionPort, // handle of completion port
&dwBytesTransferred, // pointer to number of bytes transferred
&uCompletionKey, // pointer to completion key
&pOverlapped, // pointer to overlapped structure
INFINITE // wait forever
);
//
// did we fail miserably?
//
if ( ( fStatusReturn == FALSE ) && ( pOverlapped == FALSE ) )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem getting item from completion port!" );
DisplayErrorCode( 0, dwError );
}
else
{
//
// what happened?
//
switch ( uCompletionKey )
{
//
// SP is closing, stop this threads
//
case IO_COMPLETION_KEY_SP_CLOSE:
{
fLooping = FALSE;
break;
}
// // TAPI message received
// case IO_COMPLETION_KEY_TAPI_MESSAGE:
// {
// LINEMESSAGE *pLineMessage;
//
//
// // pointer to line message is in pOverlapped, we are responsible for freeing it
// DNASSERT( pOverlapped != NULL );
// DBG_CASSERT( sizeof( pLineMessage ) == sizeof( pOverlapped ) );
// pLineMessage = reinterpret_cast<LINEMESSAGE*>( pOverlapped );
// ProcessWinNTTAPIMessage( pInput->pThisObject->m_pThis, pLineMessage );
// if ( LocalFree( pOverlapped ) != NULL )
// {
// DPFX(DPFPREP, 0, "Problem with LocalFree in NTProcessTAPIMessage" );
// DisplayErrorCode( 0, GetLastError() );
// }
//
// break;
// }
//
// ReadFile or WriteFile completed
//
case IO_COMPLETION_KEY_IO_COMPLETE:
{
SPAM_IO_DATA *pIOData;
DWORD dwError;
DNASSERT( pOverlapped != NULL );
DBG_CASSERT( sizeof( pIOData ) == sizeof( pOverlapped ) );
DBG_CASSERT( OFFSETOF( SPAM_IO_DATA, Overlap ) == 0 );
pIOData = reinterpret_cast<SPAM_IO_DATA*>( pOverlapped );
if ( ( fStatusReturn == FALSE ) ||
( ( fStatusReturn != FALSE ) && ( dwBytesTransferred == 0 ) ) )
{
dwError = GetLastError();
}
else
{
dwError = ERROR_SUCCESS;
}
(pIOData->pDataPort->*pIOData->pUnifiedCompletionFunction)( dwBytesTransferred, dwError );
break;
}
// a new job was submitted to the job queue
case IO_COMPLETION_KEY_NEW_JOB:
{
WORK_THREAD_JOB *pJobInfo;
pJobInfo = pInput->pThisObject->GetWorkItem();
if ( pJobInfo != NULL )
{
switch ( pJobInfo->JobType )
{
// enum refresh
case JOB_REFRESH_ENUM:
{
DPFX(DPFPREP, 8, "IOCompletion job REFRESH_ENUM" );
DNASSERT( pJobInfo->JobData.JobRefreshEnum.uDummy == 0 );
pInput->pThisObject->WakeNTEnumThread();
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
case JOB_UNINITIALIZED:
default:
{
DPFX(DPFPREP, 8, "IOCompletion job unknown!" );
DNASSERT( FALSE );
break;
}
}
pJobInfo->JobType = JOB_UNINITIALIZED;
pInput->pThisObject->m_JobPool.Release( &pInput->pThisObject->m_JobPool, pJobInfo );
}
break;
}
//
// unknown completion return
//
default:
{
DNASSERT( FALSE );
break;
}
}
}
}
pInput->pThisObject->DecrementActiveThreadCount();
DNFree( pParam );
return 0;
}
//**********************************************************************
#ifdef WINNT
//**********************************************************************
// ------------------------------
// CWorkThread::WinNTEnumThread - enumeration 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.
// ------------------------------
DWORD WINAPI CWorkThread::WinNTEnumThread( void *pParam )
{
NT_ENUM_THREAD_DATA *pInput;
CWorkThread *pThisWorkThread;
BOOL fLooping;
BOOL fEnumsActive;
DWORD dwWaitReturn;
DN_TIME NextEnumTime;
DNASSERT( pParam != NULL );
//
// initialize
//
DNASSERT( pParam != NULL );
pInput = static_cast<NT_ENUM_THREAD_DATA*>( pParam );
DNASSERT( pInput->hEventList != NULL );
DNASSERT( pInput->pThisWorkThread != NULL );
pThisWorkThread = pInput->pThisWorkThread;
memset( &NextEnumTime, 0xFF, sizeof( NextEnumTime ) );
Reloop:
//
// there were no active enums so we want to wait forever for something to
// happen
//
fEnumsActive = FALSE;
fLooping = TRUE;
//
// go until we're told to stop
//
while ( fLooping != FALSE )
{
DN_TIME CurrentTime;
DN_TIME DeltaT;
DWORD dwMaxWaitTime;
DNTimeGet( &CurrentTime );
if ( DNTimeCompare( &NextEnumTime, &CurrentTime ) <= 0 )
{
pThisWorkThread->ProcessEnums( &NextEnumTime );
}
DNTimeSubtract( &NextEnumTime, &CurrentTime, &DeltaT );
dwMaxWaitTime = pThisWorkThread->SaturatedWaitTime( DeltaT );
dwWaitReturn = WaitForMultipleObjectsEx( LENGTHOF( pInput->hEventList ), // number of events
pInput->hEventList, // event list
FALSE, // wait for any one event to be signalled
dwMaxWaitTime, // timeout for enuming
TRUE // be nice and allow APCs
);
switch ( dwWaitReturn )
{
//
// SP closing
//
case ( WAIT_OBJECT_0 + EVENT_INDEX_SP_CLOSE ):
{
DPFX(DPFPREP, 8, "NT enum 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_ENUM_WAKEUP ):
{
memset( &NextEnumTime, 0x00, sizeof( NextEnumTime ) );
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 Enum thread WaitForMultipleObjects failed: 0x%x", dwWaitReturn );
DNASSERT( FALSE );
break;
}
//
// problem
//
default:
{
DNASSERT( FALSE );
break;
}
}
}
DPFX(DPFPREP, 8, "NTEnum thread is exiting!" );
pThisWorkThread->LockEnumData();
//
// check for another enum being added since we decided to leave
//
dwWaitReturn = WaitForSingleObjectEx( pInput->hEventList[ EVENT_INDEX_ENUM_WAKEUP ], 0, TRUE );
switch ( dwWaitReturn )
{
//
// Other threads don't know that this thread wants to quit so there
// was another emum added before we could quit. Restart the loop.
//
case WAIT_OBJECT_0:
{
DNASSERT( FALSE );
memset( &NextEnumTime, 0x00, sizeof( NextEnumTime ) );
pThisWorkThread->UnlockEnumData();
goto Reloop;
break;
}
//
// just what we expected, nothing else to do, keep exiting
//
case WAIT_TIMEOUT:
{
break;
}
//
// something isn't right!
//
default:
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem with final wait in NT enum thread!" );
DisplayErrorCode( 0, dwError );
DNASSERT( FALSE );
break;
}
}
pThisWorkThread->m_fNTEnumThreadRunning = FALSE;
pThisWorkThread->DecrementActiveThreadCount();
pInput->pThisWorkThread = NULL;
pInput->hEventList[ EVENT_INDEX_SP_CLOSE ] = NULL;
if ( CloseHandle( pInput->hEventList[ EVENT_INDEX_ENUM_WAKEUP ] ) == FALSE )
{
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Problem closing event handle on exit of NTEnumThread!" );
DisplayErrorCode( 0, dwError );
}
pThisWorkThread->m_hWakeNTEnumThread = NULL;
pInput->hEventList[ EVENT_INDEX_ENUM_WAKEUP ] = NULL;
pThisWorkThread->UnlockEnumData();
return 0;
}
//**********************************************************************
#endif // WINNT
//**********************************************************************
// ------------------------------
// CWorkThread::WorkThreadJob_Alloc - allocate a new job
//
// Entry: Pointer to new entry
//
// Exit: Boolean indicating success
// TRUE = initialization successful
// FALSE = initialization failed
// ------------------------------
BOOL CWorkThread::WorkThreadJob_Alloc( void *pItem )
{
BOOL fReturn;
WORK_THREAD_JOB *pJob;
//
// initialize
//
fReturn = TRUE;
pJob = static_cast<WORK_THREAD_JOB*>( pItem );
DEBUG_ONLY( memset( pJob, 0x00, sizeof( *pJob ) ) );
return fReturn;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::WorkThreadJob_Get - a job is being removed from the pool
//
// Entry: Pointer to job
//
// Exit: Nothing
// ------------------------------
void CWorkThread::WorkThreadJob_Get( void *pItem )
{
WORK_THREAD_JOB *pJob;
//
// initialize
//
pJob = static_cast<WORK_THREAD_JOB*>( pItem );
DNASSERT( pJob->JobType == JOB_UNINITIALIZED );
//
// cannot ASSERT the the following because the pool manager uses that memory
//
// DNASSERT( pJob->pNext == NULL );
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::WorkThreadJob_Release - a job is being returned to the pool
//
// Entry: Pointer to job
//
// Exit: Nothing
// ------------------------------
void CWorkThread::WorkThreadJob_Release( void *pItem )
{
WORK_THREAD_JOB *pJob;
DNASSERT( pItem != NULL );
pJob = static_cast<WORK_THREAD_JOB*>( pItem );
DNASSERT( pJob->JobType == JOB_UNINITIALIZED );
pJob->pNext = NULL;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::WorkThreadJob_Dealloc - return job to memory manager
//
// Entry: Pointer to job
//
// Exit: Nothing
// ------------------------------
void CWorkThread::WorkThreadJob_Dealloc( void *pItem )
{
// don't do anything
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::EnumEntry_Alloc - allocate a new enum entry
//
// Entry: Pointer to new entry
//
// Exit: Boolean indicating success
// TRUE = initialization successful
// FALSE = initialization failed
// ------------------------------
BOOL CWorkThread::EnumEntry_Alloc( void *pItem )
{
BOOL fReturn;
ENUM_ENTRY *pEnumEntry;
DNASSERT( pItem != NULL );
//
// initialize
//
fReturn = TRUE;
pEnumEntry = static_cast<ENUM_ENTRY*>( pItem );
DEBUG_ONLY( memset( pEnumEntry, 0x00, sizeof( *pEnumEntry ) ) );
pEnumEntry->Linkage.Initialize();
return fReturn;
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::EnumEntry_Get - get new entry from pool
//
// Entry: Pointer to new entry
//
// Exit: Nothing
// ------------------------------
void CWorkThread::EnumEntry_Get( void *pItem )
{
ENUM_ENTRY *pEnumEntry;
DNASSERT( pItem != NULL );
pEnumEntry = static_cast<ENUM_ENTRY*>( pItem );
pEnumEntry->Linkage.Initialize();
DNASSERT( pEnumEntry->pEndpoint == NULL );
DNASSERT( pEnumEntry->pEnumCommandData == NULL );
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::EnumEntry_Release - return entry to pool
//
// Entry: Pointer to new entry
//
// Exit: Nothing
// ------------------------------
void CWorkThread::EnumEntry_Release( void *pItem )
{
ENUM_ENTRY *pEnumEntry;
DNASSERT( pItem != NULL );
pEnumEntry = static_cast<ENUM_ENTRY*>( pItem );
DNASSERT( pEnumEntry->Linkage.IsEmpty() != FALSE );
DNASSERT( pEnumEntry->pEndpoint == NULL );
DNASSERT( pEnumEntry->pEnumCommandData == NULL );
}
//**********************************************************************
//**********************************************************************
// ------------------------------
// CWorkThread::EnumEntry_Dealloc - deallocate an enum entry
//
// Entry: Pointer to new entry
//
// Exit: Nothing
// ------------------------------
void CWorkThread::EnumEntry_Dealloc( void *pItem )
{
ENUM_ENTRY *pEnumEntry;
DNASSERT( pItem != NULL );
//
// initialize
//
pEnumEntry = static_cast<ENUM_ENTRY*>( pItem );
//
// return associated poiner to write data
//
// can't DNASSERT on Linkage because pool manager stomped on it
// DNASSERT( pEnumEntry->Linkage.IsEmpty() != FALSE );
DNASSERT( pEnumEntry->pEndpoint == NULL );
DNASSERT( pEnumEntry->pEnumCommandData == NULL );
}
//**********************************************************************