496 lines
15 KiB
C++
496 lines
15 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (C) 1999-2001 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
RESYNC.CPP
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
|
||
|
History:
|
||
|
|
||
|
a-davj 04-Mar-97 Created.
|
||
|
ivanbrug 01-Sep-2000 changed for svchost migration
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
#include <malloc.h>
|
||
|
#include <tchar.h>
|
||
|
|
||
|
#include "WinMgmt.h"
|
||
|
|
||
|
#include "resync.h"
|
||
|
|
||
|
|
||
|
// Timeout is a 64-bit value. See documentation on SetWaitableTimer
|
||
|
// for why we are setting it this way.
|
||
|
#define _SECOND 10000000
|
||
|
#define RESYNC_TIMEOUT_INTERVAL 10 * _SECOND
|
||
|
|
||
|
DWORD gdwADAPDelaySec = 0;
|
||
|
DWORD gdwLodCtrDelaySec = 0;
|
||
|
|
||
|
BOOL gfResyncInit = FALSE;
|
||
|
HANDLE ghWaitableTimer = NULL;
|
||
|
BOOL gfSpawnedResync = FALSE;
|
||
|
|
||
|
|
||
|
HANDLE ghResyncThreadHandle = NULL;
|
||
|
HANDLE ghResyncThreadEvent = NULL;
|
||
|
CRITICAL_SECTION* g_pResyncCs = NULL;
|
||
|
DWORD gdwResyncThreadId = 0;
|
||
|
|
||
|
// A global handle used to store the last dredger we
|
||
|
// kicked off!
|
||
|
HANDLE ghChildProcessHandle = NULL;
|
||
|
|
||
|
|
||
|
void ResetResyncTimer( HANDLE hResyncTimer, BOOL bIsLoadCtr )
|
||
|
{
|
||
|
DWORD dwErr = 0;
|
||
|
__int64 qwDueTime = bIsLoadCtr?(gdwLodCtrDelaySec * _SECOND):(gdwADAPDelaySec * _SECOND); // RESYNC_TIMEOUT_INTERVAL;
|
||
|
|
||
|
// Convert it to relative time
|
||
|
qwDueTime *= -1;
|
||
|
|
||
|
// Copy the relative time into a LARGE_INTEGER.
|
||
|
LARGE_INTEGER li;
|
||
|
|
||
|
li.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF );
|
||
|
li.HighPart = (LONG) ( qwDueTime >> 32 );
|
||
|
|
||
|
if ( !SetWaitableTimer( hResyncTimer, &li, 0, NULL, NULL, FALSE ) )
|
||
|
{
|
||
|
dwErr = GetLastError();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// This thread controls the actual shelling of a resync perf operation
|
||
|
DWORD WINAPI ResyncPerfThread( void* pVoid )
|
||
|
{
|
||
|
RESYNCPERFDATASTRUCT* pResyncPerfData = (RESYNCPERFDATASTRUCT*) pVoid;
|
||
|
|
||
|
// We get the two handles, copy them and wait on them
|
||
|
// The first handle is the terminate event, the second is the
|
||
|
// timer on which to spin off the resync
|
||
|
|
||
|
BOOL bIsLodCtr = pResyncPerfData->m_bIsLodCtr;
|
||
|
HANDLE aHandles[2];
|
||
|
|
||
|
aHandles[0] = pResyncPerfData->m_hTerminate;
|
||
|
HANDLE hTimer = pResyncPerfData->m_hWaitableTimer;
|
||
|
|
||
|
CRITICAL_SECTION* pcs = pResyncPerfData->m_pcs;
|
||
|
|
||
|
BOOL bFullDredge = pResyncPerfData->m_fFullDredge;
|
||
|
|
||
|
delete pResyncPerfData;
|
||
|
pResyncPerfData = NULL;
|
||
|
|
||
|
// Reset the spawned flag
|
||
|
gfSpawnedResync = FALSE;
|
||
|
|
||
|
// Okay. Signal this event so the starting thread can get us going
|
||
|
SetEvent( ghResyncThreadEvent );
|
||
|
|
||
|
// Now, if ghChildProcessHandle is not NULL, then we've obviously kicked off a
|
||
|
// dredge before. See where the last one is at. If it's not done, wait for
|
||
|
// it to finish. We will always check this at the start of this chunk of code,
|
||
|
// since we are really the only location in which the process handle can ever get set,
|
||
|
// and there really shouldn't be more than one thread ever, waiting to start another
|
||
|
// dredge
|
||
|
|
||
|
if ( NULL != ghChildProcessHandle )
|
||
|
{
|
||
|
|
||
|
aHandles[1] = ghChildProcessHandle;
|
||
|
|
||
|
DWORD dwWait = WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE );
|
||
|
|
||
|
// If abort was signalled, leave!
|
||
|
if ( dwWait == WAIT_OBJECT_0 )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// If the process handle was signalled, close the process, reset the timer
|
||
|
// and we'll get ready to start the next dredge!
|
||
|
if ( dwWait == WAIT_OBJECT_0 + 1 )
|
||
|
{
|
||
|
EnterCriticalSection( pcs );
|
||
|
|
||
|
CloseHandle( ghChildProcessHandle );
|
||
|
ghChildProcessHandle = NULL;
|
||
|
ResetResyncTimer( hTimer, bIsLodCtr );
|
||
|
|
||
|
LeaveCriticalSection( pcs );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If the Child Process Handle is NULL, we've never dredged before, so we'll
|
||
|
// just reset the timer
|
||
|
ResetResyncTimer( hTimer, bIsLodCtr );
|
||
|
}
|
||
|
|
||
|
BOOL fHoldOff = TRUE;
|
||
|
|
||
|
// Reset this handle to the timer now
|
||
|
aHandles[1] = hTimer;
|
||
|
|
||
|
while ( fHoldOff )
|
||
|
{
|
||
|
// Wait for either the terminate event or the timer
|
||
|
DWORD dwWait = WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE );
|
||
|
|
||
|
// This means the terminate was signaled
|
||
|
if ( dwWait == WAIT_OBJECT_0 )
|
||
|
{
|
||
|
break;
|
||
|
} else
|
||
|
// This means the timer was signaled
|
||
|
if ( dwWait == WAIT_OBJECT_0 + 1 )
|
||
|
{
|
||
|
EnterCriticalSection( pcs );
|
||
|
|
||
|
// Finally, if the current thread id != gdwResyncThreadId, this means another
|
||
|
// resync perf thread got kicked off, inside of the critical section,
|
||
|
// so we should just let it wait on the timer. We don't really need to do
|
||
|
// this, since the main thread will wait on this thread to complete before
|
||
|
// it actually kicks off another thread.
|
||
|
|
||
|
if ( GetCurrentThreadId() != gdwResyncThreadId )
|
||
|
{
|
||
|
// Used the following int 3 for debugging
|
||
|
// _asm int 3;
|
||
|
LeaveCriticalSection( pcs );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Once we get through the critical section, check that the
|
||
|
// timer is still signalled. If it is not, this means that somebody
|
||
|
// got control of the critical section and reset the timer
|
||
|
|
||
|
if ( WaitForSingleObject( aHandles[1], 0 ) == WAIT_OBJECT_0 )
|
||
|
{
|
||
|
|
||
|
// Last quick sanity check on the abort event
|
||
|
if ( WaitForSingleObject( aHandles[0], 0 ) == WAIT_OBJECT_0 )
|
||
|
{
|
||
|
// Outa here!
|
||
|
LeaveCriticalSection( pcs );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Okay, we really will try to create the process now.
|
||
|
gfSpawnedResync = TRUE;
|
||
|
|
||
|
// We signalled to start the process, so make it so.
|
||
|
PROCESS_INFORMATION pi;
|
||
|
STARTUPINFO si;
|
||
|
memset(&si, 0, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
|
||
|
TCHAR * pWritebleBuffer = (TCHAR *)_alloca(sizeof(__T("WMIADAP.EXE /F"))+2);
|
||
|
|
||
|
lstrcpy(pWritebleBuffer,(bFullDredge?__T("WMIADAP.EXE /F"):__T("WMIADAP.EXE")));
|
||
|
|
||
|
BOOL bRes = CreateProcess(NULL,
|
||
|
pWritebleBuffer,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
FALSE,
|
||
|
CREATE_NO_WINDOW,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&si,
|
||
|
&pi);
|
||
|
if(bRes)
|
||
|
{
|
||
|
// Who cares about this one?
|
||
|
CloseHandle(pi.hThread);
|
||
|
|
||
|
// Clean up our old values
|
||
|
if ( NULL != ghChildProcessHandle )
|
||
|
{
|
||
|
CloseHandle( ghChildProcessHandle );
|
||
|
ghChildProcessHandle = NULL;
|
||
|
}
|
||
|
|
||
|
ghChildProcessHandle = pi.hProcess;
|
||
|
}
|
||
|
|
||
|
// We're done
|
||
|
fHoldOff = FALSE;
|
||
|
|
||
|
} // Check that we're still signalled, or we will just have to go back to waiting
|
||
|
|
||
|
LeaveCriticalSection( pcs );
|
||
|
|
||
|
} // IF timer was signalled
|
||
|
|
||
|
} // WHILE fHoldOff
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// For the waitable timer
|
||
|
//#define _SECOND 10000000
|
||
|
|
||
|
// Create all the things we need
|
||
|
BOOL InitResync( void )
|
||
|
{
|
||
|
if ( gfResyncInit )
|
||
|
return gfResyncInit;
|
||
|
|
||
|
|
||
|
if ( NULL == ghWaitableTimer )
|
||
|
{
|
||
|
ghWaitableTimer = CreateWaitableTimerW( NULL, TRUE, NULL );
|
||
|
|
||
|
// We gotta big problem
|
||
|
if ( NULL == ghWaitableTimer )
|
||
|
{
|
||
|
// Log an error here
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "Could not create a waitable timer for Resyncperf.\n" ) );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( NULL == ghResyncThreadEvent )
|
||
|
{
|
||
|
ghResyncThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||
|
|
||
|
// We gotta big problem
|
||
|
if ( NULL == ghResyncThreadEvent )
|
||
|
{
|
||
|
// Log an event here
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncThreadEvent event for Resyncperf.\n" ) );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// This critical section won't be freed or deleted because of
|
||
|
// potential timing issues. But since it's only one, I think
|
||
|
// we can live with it.
|
||
|
if ( NULL == g_pResyncCs )
|
||
|
{
|
||
|
g_pResyncCs = new CRITICAL_SECTION;
|
||
|
|
||
|
// We gotta big problem
|
||
|
if ( NULL == g_pResyncCs )
|
||
|
{
|
||
|
// Log an event here
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncCs critical section for Resyncperf.\n" ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
InitializeCriticalSection( g_pResyncCs );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
gfResyncInit = ( NULL != ghWaitableTimer &&
|
||
|
NULL != g_pResyncCs &&
|
||
|
NULL != ghResyncThreadEvent );
|
||
|
|
||
|
// Read the initialization information
|
||
|
|
||
|
Registry reg;
|
||
|
|
||
|
if ( Registry::no_error == reg.Open( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\WBEM\\CIMOM" ) )
|
||
|
{
|
||
|
long lError = reg.GetDWORD( L"ADAPDelay", &gdwADAPDelaySec );
|
||
|
|
||
|
if ( Registry::no_error == lError )
|
||
|
{
|
||
|
//This is what we want
|
||
|
}
|
||
|
else if ( ERROR_FILE_NOT_FOUND == reg.GetLastError() )
|
||
|
{
|
||
|
// Not set, so add it
|
||
|
reg.SetDWORD( L"ADAPDelay", WMIADAP_DEFAULT_DELAY );
|
||
|
gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Error
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf experienced an error while attempting to read the WMIADAPDelay value in the CIMOM subkey. Continuing using a default value.\n" ) );
|
||
|
gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY;
|
||
|
}
|
||
|
|
||
|
lError = reg.GetDWORD( L"LodCtrDelay", &gdwLodCtrDelaySec );
|
||
|
|
||
|
if ( Registry::no_error == lError )
|
||
|
{
|
||
|
//This is what we want
|
||
|
}
|
||
|
else if ( ERROR_FILE_NOT_FOUND == reg.GetLastError() )
|
||
|
{
|
||
|
// Not set, so add it
|
||
|
reg.SetDWORD( L"LodCtrDelay", WMIADAP_DEFAULT_DELAY );
|
||
|
gdwLodCtrDelaySec = WMIADAP_DEFAULT_DELAY_LODCTR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Error
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf experienced an error while attempting to read the WMIADAPDelay value in the CIMOM subkey. Continuing using a default value.\n" ) );
|
||
|
gdwLodCtrDelaySec = WMIADAP_DEFAULT_DELAY_LODCTR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Error
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf could not open the CIMOM subkey to read initialization data. Continuing using a default value.\n" ) );
|
||
|
gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY;
|
||
|
gdwLodCtrDelaySec = WMIADAP_DEFAULT_DELAY_LODCTR;
|
||
|
}
|
||
|
|
||
|
return gfResyncInit;
|
||
|
}
|
||
|
|
||
|
// PLEASE NOTE - THIS FUNCTION IS NOT REENTRANT! PLEASE DO NOT CALL IT ON MULTIPLE THREADS!
|
||
|
void ResyncPerf( HANDLE hTerminate, BOOL bIsLodCtr )
|
||
|
{
|
||
|
|
||
|
// Assume that we should check the timer
|
||
|
BOOL fFirstTime = !gfResyncInit;
|
||
|
|
||
|
if ( !InitResync() )
|
||
|
return;
|
||
|
|
||
|
|
||
|
EnterCriticalSection( g_pResyncCs );
|
||
|
|
||
|
// Now, if this or the first time, or the spawned resyncflag is set to TRUE, then we need
|
||
|
// to kick off another thread. By checking gfSpawnedResync in a critical section, since
|
||
|
// it only gets set in the same critical section, we ensure that we will resignal as needed
|
||
|
// as well as only kick off a thread when we really need to.
|
||
|
|
||
|
BOOL fSpawnThread = ( fFirstTime || gfSpawnedResync );
|
||
|
|
||
|
if ( !fSpawnThread )
|
||
|
{
|
||
|
// We are here because we don't appear to have spawned a resync.
|
||
|
// This is either because we are servicing many lodctr requests
|
||
|
// within our time delay, or a dredger was started and
|
||
|
// a previous request request to dredge is waiting for
|
||
|
// the process to complete. If the child process handle
|
||
|
// is not NULL, there is no real need to reset the
|
||
|
// waitable timer
|
||
|
|
||
|
if ( NULL == ghChildProcessHandle && ghResyncThreadHandle )
|
||
|
{
|
||
|
// Reset the timer here
|
||
|
ResetResyncTimer( ghWaitableTimer , bIsLodCtr );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection( g_pResyncCs );
|
||
|
|
||
|
|
||
|
if ( fSpawnThread )
|
||
|
{
|
||
|
HANDLE ahHandle[2];
|
||
|
|
||
|
if ( NULL != ghResyncThreadHandle )
|
||
|
{
|
||
|
ahHandle[0] = hTerminate;
|
||
|
ahHandle[1] = ghResyncThreadHandle;
|
||
|
|
||
|
// Wait for ten seconds on this handle. If it is not signalled, something is
|
||
|
// direly wrong. We're probably not going to be able to kick off a dredge
|
||
|
// so put some info to this effect in the error log. The only time we should
|
||
|
// have contention here, is when a lodctr event is signalled, just as the timer
|
||
|
// becomes signalled. The resync thread will wake up and start another dredge
|
||
|
// this thread will wait for the other thread to complete before continuing.
|
||
|
// We will kick off another resync thread, which will start another dredge,
|
||
|
// but it will wait for the first dredge to continue. This is a worst case
|
||
|
// scenario, and arguably kicking off two dredges isn't that bad of a bailout
|
||
|
|
||
|
DWORD dwRet = WaitForMultipleObjects( 2, ahHandle, FALSE, 10000 );
|
||
|
|
||
|
// We're done
|
||
|
if ( dwRet == WAIT_OBJECT_0 )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( dwRet != WAIT_OBJECT_0 + 1 )
|
||
|
{
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "The wait for a termination event or ResyncThreadHandle timed out in Resyncperf.\n" ) );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CloseHandle( ghResyncThreadHandle );
|
||
|
ghResyncThreadHandle = NULL;
|
||
|
}
|
||
|
|
||
|
EnterCriticalSection( g_pResyncCs );
|
||
|
|
||
|
DWORD dwThreadId = 0;
|
||
|
|
||
|
RESYNCPERFDATASTRUCT* pResyncData = new RESYNCPERFDATASTRUCT;
|
||
|
|
||
|
// Boy are we low on memory!
|
||
|
if ( NULL == pResyncData )
|
||
|
{
|
||
|
LeaveCriticalSection( g_pResyncCs );
|
||
|
|
||
|
// Log an event here
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "Could not create a RESYNCPERFDATASTRUCT in Resyncperf.\n" ) );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Store the data for the resync operation
|
||
|
pResyncData->m_hTerminate = hTerminate;
|
||
|
pResyncData->m_hWaitableTimer = ghWaitableTimer;
|
||
|
pResyncData->m_pcs = g_pResyncCs;
|
||
|
pResyncData->m_fFullDredge = fFirstTime;
|
||
|
pResyncData->m_bIsLodCtr = bIsLodCtr;
|
||
|
|
||
|
ghResyncThreadHandle = CreateThread( NULL, 0,
|
||
|
(LPTHREAD_START_ROUTINE)ResyncPerfThread, (void*) pResyncData,
|
||
|
0, &gdwResyncThreadId );
|
||
|
|
||
|
LeaveCriticalSection( g_pResyncCs );
|
||
|
|
||
|
|
||
|
if ( NULL == ghResyncThreadHandle )
|
||
|
{
|
||
|
LeaveCriticalSection( g_pResyncCs );
|
||
|
|
||
|
// Log an event here
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncPerfThread thread in Resyncperf.\n" ) );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Wait for the resync thread event to be signalled by the thread we just started.
|
||
|
// If it doesn't signal in 10 seconds, something is VERY wrong
|
||
|
DWORD dwWait = WaitForSingleObject( ghResyncThreadEvent, INFINITE );
|
||
|
|
||
|
if ( dwWait != WAIT_OBJECT_0 )
|
||
|
{
|
||
|
// Log an event
|
||
|
ERRORTRACE( ( LOG_WINMGMT, "The ResyncPerfThread thread never signaled the ghResyncThreadEvent in Resyncperf.\n" ) );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // IF fSpawnThread
|
||
|
|
||
|
}
|
||
|
|