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

876 lines
23 KiB
C++

/*++
Copyright (C) 1997-2001 Microsoft Corporation
Module Name:
MAINTOBJ.CPP
Abstract:
Maintenance object for keeping track of CComLink objects.
History:
a-davj 24-Sep-97 Created.
--*/
#include "precomp.h"
#include "wmishared.h"
DWORD ClientMaintThread ( LPDWORD pParam ) ;
//***************************************************************************
//
// LockOut::LockOut
// LockOut::~LockOut
//
// DESCRIPTION:
//
// Constructor and destructor
//
//***************************************************************************
LockOut::LockOut(CRITICAL_SECTION & cs)
{
m_cs = &cs;
EnterCriticalSection(m_cs);
}
LockOut::~LockOut()
{
LeaveCriticalSection(m_cs);
}
//***************************************************************************
//
// MaintObj::MaintObj
//
// DESCRIPTION:
//
// Constructor.
//
// PARAMETERS:
//
//
//***************************************************************************
MaintObj :: MaintObj ( BOOL a_Client )
{
InitializeCriticalSection ( & m_cs ) ;
m_ChangeEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
m_ClientThreadStartedEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
m_ClientThreadHandle = NULL;
m_ClientRole = a_Client;
}
//***************************************************************************
//
// MaintObj::~MaintObj
//
// DESCRIPTION:
//
// Destructor.
//
//***************************************************************************
MaintObj :: ~MaintObj ()
{
// If this object is supplying the thread, as is the case in a client,
// the thread should be terminated as a result of the global termiation
// event already being set. However, threads being threads, give it
// some time to die gracefully before resorting to a termination.
if ( m_ClientThreadHandle )
{
DWORD dwRes = WbemWaitForSingleObject ( m_ClientThreadHandle , 2000 ) ;
if ( dwRes == WAIT_TIMEOUT )
{
TerminateThread ( m_ClientThreadHandle , 1 ) ;
}
CloseHandle ( m_ClientThreadHandle ) ;
}
DeleteCriticalSection ( & m_cs ) ;
if ( m_ChangeEvent )
CloseHandle ( m_ChangeEvent ) ;
if ( m_ClientThreadStartedEvent )
CloseHandle ( m_ClientThreadStartedEvent ) ;
}
//***************************************************************************
//
// MaintObj::AddComLink
//
// DESCRIPTION:
//
// Called when it is time to add a new CComLink object to the list of things
// being maintained.
//
// PARAMETERS:
//
// pComLink Pointer to the object to be added to the list
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj :: AddComLink ( CComLink *a_ComLink )
{
LockOut lock ( m_cs ) ;
// Add the object pointer to the CComLink list
a_ComLink->AddRef2 ( NULL , NONE , DONOTHING ) ;
ComEntry *t_ComLinkNode = new ComEntry ( a_ComLink ) ;
if ( t_ComLinkNode == NULL )
{
return WBEM_E_OUT_OF_MEMORY;
}
HANDLE t_StreamHandle = a_ComLink->GetStreamHandle () ;
HRESULT scRet = m_ComLinkContainer.Add ( t_ComLinkNode ) ;
if ( SUCCEEDED ( scRet ) )
{
// Some comlinks use events to indicate time to read. If this is one
// of these, add it to the list.
if ( t_StreamHandle )
{
EventEntry * t_EventNode = new EventEntry ( t_StreamHandle );
m_EventContainer.Add ( t_EventNode ) ;
}
}
// If ComLinks has transitioned to one ( actually really need to test for transition from 0 to 1 )
// or event handle has been removed then update worket thread.
if ( m_ComLinkContainer.Size () == 1 || t_StreamHandle )
{
SetEvent ( m_ChangeEvent ) ;
}
return scRet;
}
void MaintObj::Indicate ( CComLink *a_ComLink )
{
LockOut lock ( m_cs ) ;
// Find the entry in the list an immediatly delete it
int t_Index ;
ComEntry *t_ComEntryNode = FindEntry ( a_ComLink, t_Index ) ;
if ( t_ComEntryNode == NULL )
{
return ;
}
m_ComLinkContainer.RemoveAt ( t_Index ) ;
delete t_ComEntryNode ;
a_ComLink->Release2 ( NULL , NONE ) ;
}
void MaintObj::UnLockedIndicate ( CComLink *a_ComLink )
{
// Find the entry in the list an immediatly delete it
int t_Index ;
ComEntry *t_ComEntryNode = UnLockedFindEntry ( a_ComLink, t_Index ) ;
if ( t_ComEntryNode == NULL )
{
return ;
}
m_ComLinkContainer.RemoveAt ( t_Index ) ;
delete t_ComEntryNode ;
a_ComLink->Release3 ( NULL , NONE ) ;
}
//***************************************************************************
//
// MaintObj::ShutDownComlink
//
// DESCRIPTION:
//
// Called when a comlink should be terminated. Note that the actual
// deletion is done in the maintenace loop since we dont want the CComLink
// object to delete itself in a member function.
//
// PARAMETERS:
//
// pComLink Pointer to the object to be added to the list
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj::ShutDownComlink ( CComLink *a_ComLink )
{
LockOut lock ( m_cs ) ;
HRESULT scRet = UnLockedShutDownComlink ( a_ComLink ) ;
return scRet;
}
//***************************************************************************
//
// MaintObj::ShutDownComlink
//
// DESCRIPTION:
//
// Called when a comlink should be terminated. Note that the actual
// deletion is done in the maintenace loop since we dont want the CComLink
// object to delete itself in a member function.
//
// PARAMETERS:
//
// pComLink Pointer to the object to be added to the list
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj::UnLockedShutDownComlink ( CComLink *a_ComLink )
{
HRESULT scRet = S_OK;
// Find the entry in the list an immediatly delete it
int t_Index ;
HANDLE t_StreamHandle = NULL;
ComEntry *t_ComEntryNode = UnLockedFindEntry ( a_ComLink, t_Index ) ;
if ( t_ComEntryNode != NULL )
{
m_ComLinkContainer.RemoveAt ( t_Index ) ;
delete t_ComEntryNode ;
t_StreamHandle = a_ComLink->GetStreamHandle ();
a_ComLink->UnLockedShutdown () ;
a_ComLink->Release3 ( NULL , NONE ) ;
}
// some comlinks have an event for reading which needs to be deleted.
// These cannot be deleted immediatly since the maintenance thread is
// currently waiting on these. So, mark it for deletion instead.
if ( t_StreamHandle )
{
int t_Size = m_EventContainer.Size() ;
for ( int t_Index = 0; t_Index < t_Size ; t_Index ++ )
{
EventEntry *t_EventNode = ( EventEntry * ) m_EventContainer [ t_Index ] ;
if(t_EventNode && ( t_EventNode->m_AsynchronousEventHandle == t_StreamHandle ) )
{
t_EventNode->m_DeleteASAP = TRUE;
break;
}
}
}
// If ComLinks has transitioned to zero or event handle has been removed then update worket thread.
if ( m_ComLinkContainer.Size () == 0 || t_StreamHandle )
{
SetEvent ( m_ChangeEvent ) ;
}
return scRet;
}
//***************************************************************************
//
// MaintObj::StartClientThreadIfNeeded
//
// DESCRIPTION:
//
// Only used by clients since the server uses the main thread. This is
// called when the locator determines that a non dcom connection is
// being make.
//
//***************************************************************************
HRESULT MaintObj::StartClientThreadIfNeeded ()
{
DWORD dwRes = WbemWaitForSingleObject ( m_ClientThreadStartedEvent , 0 ) ;
if( dwRes == WAIT_OBJECT_0 )
{
// Thread has already been started, one per process.
return S_FALSE ;
}
LockOut lock ( m_cs ) ;
DWORD t_ThreadId;
m_ClientThreadHandle = CreateThread (
NULL ,
0 ,
( LPTHREAD_START_ROUTINE ) ClientMaintThread ,
( LPVOID ) m_ClientThreadStartedEvent ,
0,
&t_ThreadId
) ;
if ( ! m_ClientThreadHandle )
{
return WBEM_E_FAILED ;
}
DWORD t_Timeout = GetTimeout () ;
dwRes = WbemWaitForSingleObject ( m_ClientThreadStartedEvent , t_Timeout ) ;
if ( dwRes == WAIT_OBJECT_0 )
{
return S_OK ;
}
else
{
return WBEM_E_FAILED ;
}
}
//***************************************************************************
//
// MaintObj::ShutDownAllComlinks
//
// DESCRIPTION:
//
// Constructor.
//
// PARAMETERS:
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj::ShutDownAllComlinks ()
{
LockOut lock ( m_cs ) ;
UnLockedShutDownAllComlinks () ;
return S_OK;
}
//***************************************************************************
//
// MaintObj::ShutDownAllComlinks
//
// DESCRIPTION:
//
// Constructor.
//
// PARAMETERS:
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj::UnLockedShutDownAllComlinks ()
{
while ( m_ComLinkContainer.Size () > 0 )
{
ComEntry *t_ComLinkNode = (ComEntry *) m_ComLinkContainer [ 0 ] ;
CComLink*t_ComLink = t_ComLinkNode->m_ComLink ;
UnLockedShutDownComlink ( t_ComLink ) ;
}
// Should be zero by the time we get here
int t_Size = m_EventContainer.Size () ;
for ( int t_Index = 0 ; t_Index < t_Size ; t_Index ++ )
{
EventEntry *t_EntryNode = ( EventEntry * ) m_EventContainer [ t_Index ];
if ( t_EntryNode )
{
delete t_EntryNode ;
}
}
return S_OK;
}
//***************************************************************************
//
// MaintObj::CheckForHungConnections()
//
// DESCRIPTION:
//
// Called periodically by the server maintenace thread to eliminate any
// clients which have died or gone away without releasing properly.
//
// PARAMETERS:
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj :: CheckForHungConnections ()
{
LockOut lock ( m_cs ) ;
DWORD t_RegistryTimeout = GetTimeout () ;
int t_Size = m_ComLinkContainer.Size () ;
for ( int t_Index = t_Size-1; t_Index >= 0; t_Index --)
{
ComEntry *t_ComLinkNode = ( ComEntry * ) m_ComLinkContainer [ t_Index ] ;
if ( t_ComLinkNode )
{
CComLink *t_ComLink = t_ComLinkNode->m_ComLink ;
if( t_ComLink )
{
DWORD t_LastActivity = GetCurrentTime () - t_ComLink->GetTimeOfLastRead () ;
// If now hearbeats have been received for some time, then
// start the termination process. Note that does the shut down
// if there are not any active calls being processed at this time.
if ( t_LastActivity > t_RegistryTimeout )
{
t_ComLink->StartTermination () ;
// If there are no busy threads then shutdown comlink. There seems to be a hole in this logic !!!!!
if ( gThrdPool.Busy () == FALSE )
{
ShutDownComlink ( t_ComLink ) ;
}
}
}
}
}
return S_OK;
}
//***************************************************************************
//
// MaintObj::SendHeartBeats()
//
// DESCRIPTION:
//
// Sends a periodic "Keep Alive" message to the partner.
//
// PARAMETERS:
//
// RETURN VALUE:
//
// S_OK if all is well, else an error code
//
//***************************************************************************
HRESULT MaintObj :: SendHeartBeats ()
{
LockOut lock ( m_cs ) ;
int t_Size = m_ComLinkContainer.Size () ;
for( int t_Index = 0; t_Index < t_Size ; t_Index ++ )
{
ComEntry *t_ComLinkNode = ( ComEntry * ) m_ComLinkContainer [ t_Index ] ;
if ( t_ComLinkNode )
{
CComLink *t_ComLink = t_ComLinkNode->m_ComLink ;
if ( t_ComLink )
{
t_ComLink->StrobeConnection () ;
}
}
}
return S_OK;
}
//***************************************************************************
//
// MaintObj::FindEntry()
//
// DESCRIPTION:
//
// Finds a particular CComLink in the list, returns a pointer and the index
// is set.
//
// PARAMETERS:
//
// pComLink CComLink object pointer
// iFind set to the index in our array.
//
// RETURN VALUE:
//
// Pointer to ComEntry object. Note that iFind is set via reference
//
//***************************************************************************
ComEntry *MaintObj :: UnLockedFindEntry ( CComLink *a_ComLink , int &a_Index )
{
a_Index = -1 ;
int t_Size = m_ComLinkContainer.Size();
for( int t_Index = 0; t_Index < t_Size; t_Index ++ )
{
ComEntry *t_ComLinkNode = ( ComEntry * ) m_ComLinkContainer [ t_Index ] ;
if ( a_ComLink == t_ComLinkNode->m_ComLink )
{
a_Index = t_Index ;
return t_ComLinkNode ;
}
}
return NULL ;
}
//***************************************************************************
//
// MaintObj::FindEntry()
//
// DESCRIPTION:
//
// Finds a particular CComLink in the list, returns a pointer and the index
// is set.
//
// PARAMETERS:
//
// pComLink CComLink object pointer
// iFind set to the index in our array.
//
// RETURN VALUE:
//
// Pointer to ComEntry object. Note that iFind is set via reference
//
//***************************************************************************
ComEntry *MaintObj :: FindEntry ( CComLink *a_ComLink , int &a_Index )
{
LockOut lock ( m_cs ) ;
return UnLockedFindEntry ( a_ComLink , a_Index ) ;
}
//***************************************************************************
//
// MaintObj::GetSocketComLink()
//
// DESCRIPTION:
//
// Given a socket number, this finds the CComLink object which is using
// this socket.
//
// PARAMETERS:
//
// s Socket number
//
// RETURN VALUE:
//
// CComlink object, NULL if not found.
//
//***************************************************************************
CComLink *MaintObj :: GetSocketComLink ( SOCKET s )
{
LockOut lock ( m_cs ) ;
int t_Size = m_ComLinkContainer.Size () ;
for( int t_Index = 0 ; t_Index < t_Size ; t_Index ++ )
{
ComEntry *t_ComLinkNode = ( ComEntry * ) m_ComLinkContainer [ t_Index ] ;
if ( t_ComLinkNode )
{
CComLink *t_ComLink = t_ComLinkNode->m_ComLink ;
if ( t_ComLink && t_ComLink->GetSocketHandle () == s )
{
return t_ComLink ;
}
}
}
return NULL;
}
//***************************************************************************
//
// MaintObj::GetEvents()
//
// DESCRIPTION:
//
// This is called by the maintenace thread an it returns a list of events
// to wait on and sets the time between pings. Note that the handle list
// includes the termination event, the change in state event, as well as
// any asynchronous read events.
//
// PARAMETERS:
//
// ghTerminate Terminate event
// piNum Set to the number of events returned
// pdwRest Set to the time between pings
// bServer True if being called by the server. Note that
// this currently isnt relevant.
// RETURN VALUE:
//
// pointer to a handle array. Note that the CALLER SHOULD FREE THIS when
// it is either done, or needs a fresh list.
//
//***************************************************************************
HANDLE *MaintObj::GetEvents (
HANDLE a_Terminate,
int &a_EventContainerSize ,
DWORD &a_EventTimeout
)
{
// note that this is called by the maintenance thread in responce
// to the change event being set.
LockOut lock ( m_cs ) ;
// Figure out how long to delay
DWORD t_ComLinkCount = m_ComLinkContainer.Size();
if ( t_ComLinkCount )
{
DWORD t_RegistryTimeout = GetTimeout () / STROBES_PER_TIMEOUT_PERIOD ;
a_EventTimeout = t_RegistryTimeout ;
}
else
{
a_EventTimeout = INFINITE;
}
// find out how many events need to be watched for the comlinks
int t_Size = m_EventContainer.Size() ;
int t_NumberOfAsyncEvents = 0 ;
int t_NumberToDelete = 0 ;
for ( int t_Index = 0; t_Index < t_Size ; t_Index ++ )
{
EventEntry *t_AsyncEntry = ( EventEntry * ) m_EventContainer [ t_Index ] ;
if ( t_AsyncEntry )
{
if ( t_AsyncEntry->m_DeleteASAP )
{
t_NumberToDelete ++ ;
}
else
{
t_NumberOfAsyncEvents ++ ;
}
}
}
a_EventContainerSize = t_NumberOfAsyncEvents + 2 ;
HANDLE *t_EventContainer = new HANDLE [ a_EventContainerSize ] ;
t_EventContainer[0] = a_Terminate;
t_EventContainer[1] = m_ChangeEvent;
// Add each comlink event entry to the list
int t_ContainerIndex = 2;
for ( t_Index = 0; t_Index < t_Size ; t_Index ++ )
{
EventEntry *t_AsyncEntry = ( EventEntry * ) m_EventContainer [ t_Index ] ;
if ( t_AsyncEntry )
{
if ( t_AsyncEntry->m_DeleteASAP == FALSE )
{
t_EventContainer [ t_ContainerIndex ] = t_AsyncEntry->m_AsynchronousEventHandle ;
t_ContainerIndex ++ ;
}
}
}
if ( t_NumberToDelete != 0 )
{
// Delete any unused event objects
for ( t_Index = t_Size - 1 ; t_Index >= 0; t_Index -- )
{
EventEntry *t_AsyncEntry = ( EventEntry * ) m_EventContainer [ t_Index ] ;
if ( t_AsyncEntry )
{
if ( t_AsyncEntry->m_DeleteASAP )
{
m_EventContainer.RemoveAt ( t_Index ) ;
delete t_AsyncEntry ;
}
}
}
m_EventContainer.Compress () ;
}
ResetEvent ( m_ChangeEvent ) ;
return t_EventContainer ;
}
//***************************************************************************
//
// MaintObj::ServiceEvent()
//
// DESCRIPTION:
//
// Called by the maintenance thread when one of the events used by CComLinks
// to indicate read ready data is set.
//
// PARAMETERS:
//
// hEvent Event that was set.
//
//***************************************************************************
void MaintObj :: ServiceEvent ( HANDLE a_Event )
{
LockOut lock ( m_cs ) ;
int t_Size = m_ComLinkContainer.Size () ;
for ( int t_Index = 0 ; t_Index < t_Size ; t_Index ++ )
{
ComEntry *t_ComLinkNode = ( ComEntry * ) m_ComLinkContainer [ t_Size ];
if ( t_ComLinkNode )
{
CComLink *t_ComLink = t_ComLinkNode->m_ComLink ;
if ( a_Event == t_ComLink->GetStreamHandle () )
{
t_ComLink->ProcessEvent () ;
return ;
}
}
}
ResetEvent ( a_Event ) ; //hmm, never found it, reset so we dont go into infinite loop
}
BOOL MaintObj :: ClientRole ()
{
return m_ClientRole ;
}
//***************************************************************************
//
// DWORD ClientMaintThread
//
// DESCRIPTION:
//
// Periodically pings the connection and thus detects when the connection
// has died.
//
// PARAMETERS:
//
// pParam points to the comlink object that is to be watched.
//
// RETURN VALUE:
//
// N/A
//***************************************************************************
DWORD ClientMaintThread ( LPDWORD pParam )
{
DWORD t_EventTimeout = 0 ;
// Do some initial work and signal when we are ready.
HANDLE t_ThreadStartedEvent = ( HANDLE ) pParam ;
SetEvent ( t_ThreadStartedEvent ) ;
int t_EventContainerSize ;
HANDLE *t_EventContainer = gMaintObj.GetEvents ( g_Terminate , t_EventContainerSize , t_EventTimeout ) ;
while(1)
{
DWORD dwRet = MsgWaitForMultipleObjects ( t_EventContainerSize , t_EventContainer , FALSE, t_EventTimeout , QS_ALLINPUT ) ;
// Check for termination, if that is the case free up and exit.
// ============================================================
if ( dwRet == 0xffffffff )
{
DWORD t_GetLastError = GetLastError () ;
}
else if ( dwRet == WAIT_OBJECT_0 )
{
// Ok, delete the comlink objects and exit!
delete [] t_EventContainer;
gMaintObj.ShutDownAllComlinks () ;
ExitThread(0);
}
else if ( dwRet == ( WAIT_OBJECT_0 + 1 ) )
{
// There has been a change in either the number of connections, or
// the number of active connections.
delete [] t_EventContainer ;
t_EventContainer = gMaintObj.GetEvents ( g_Terminate , t_EventContainerSize , t_EventTimeout ) ;
}
else if ( ( dwRet >= ( WAIT_OBJECT_0 + t_EventContainerSize ) ) && ( dwRet < ( WAIT_OBJECT_0 + t_EventContainerSize ) ) )
{
gMaintObj.ServiceEvent ( t_EventContainer [ dwRet - WAIT_OBJECT_0 ] ) ;
}
else if ( dwRet == WAIT_TIMEOUT )
{
// time period elapsed
DEBUGTRACE((LOG,"\nHeart Beat"));
if ( gMaintObj.ClientRole () )
{
gMaintObj.SendHeartBeats ();
}
DEBUGTRACE((LOG,"\nHung Connections"));
gMaintObj.CheckForHungConnections () ;
DEBUGTRACE((LOG,"\nCompleted Check for Hung Connections"));
}
else
{
MSG t_Message ;
BOOL t_Status = PeekMessage ( &t_Message , NULL , 0 , 0 , PM_REMOVE ) ;
if ( t_Status )
{
TranslateMessage ( &t_Message ) ;
DispatchMessage ( &t_Message ) ;
}
}
}
return 0;
}