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.
640 lines
19 KiB
640 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1995-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
poller.c
|
|
|
|
Abstract:
|
|
|
|
This module polls the resource list
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 5-Dec-1995
|
|
|
|
Revision History:
|
|
Sivaprasad Padisetty (sivapad) 06-18-1997 Added the COM support
|
|
|
|
--*/
|
|
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "resmonp.h"
|
|
#include "stdio.h"
|
|
|
|
#define RESMON_MODULE RESMON_MODULE_POLLER
|
|
|
|
//
|
|
// Global data defined by this module
|
|
//
|
|
|
|
BOOL RmpShutdown = FALSE;
|
|
|
|
//
|
|
// The following critical section protects both insertion of new event lists
|
|
// onto the event listhead, as well as adding new events to a given event list.
|
|
// This could be broken into one critical section for each purpose. The latter
|
|
// critical section would be part of each event list. The former would use the
|
|
// following lock.
|
|
//
|
|
|
|
CRITICAL_SECTION RmpEventListLock; // Lock for processing event lists
|
|
|
|
|
|
//
|
|
// Function prototypes local to this module
|
|
//
|
|
DWORD
|
|
RmpComputeNextTimeout(
|
|
IN PPOLL_EVENT_LIST EventList
|
|
);
|
|
|
|
DWORD
|
|
RmpPollList(
|
|
IN PPOLL_EVENT_LIST EventList
|
|
);
|
|
|
|
VOID
|
|
RmpPollBucket(
|
|
IN PMONITOR_BUCKET Bucket
|
|
);
|
|
|
|
DWORD
|
|
RmpPollerThread(
|
|
IN LPVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Thread startup routine for the polling thread. The way this works, is that
|
|
other parts of the resource monitor add events to the list of events that
|
|
is being processed by this thread. When they are done, they signal this
|
|
thread, which makes a copy of the new lists, and then waits for an event to
|
|
happen or a timeout occurs.
|
|
|
|
Arguments:
|
|
|
|
Context - A pointer to the POLL_EVENT_LIST for this thread.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
Note:
|
|
|
|
This code assumes that the EventList pointed to by Context does NOT go
|
|
away while this thread is running. Further it assumes that the ResourceList
|
|
pointed to by the given EventList does not go away or change.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Timeout;
|
|
DWORD Status;
|
|
PPOLL_EVENT_LIST NewEventList = (PPOLL_EVENT_LIST)Context;
|
|
POLL_EVENT_LIST waitEventList; // Event list outstanding
|
|
DWORD WaitFailed = 0;
|
|
|
|
//
|
|
// Zero the local copy event list structure.
|
|
//
|
|
|
|
ZeroMemory( &waitEventList, sizeof(POLL_EVENT_LIST) );
|
|
|
|
//
|
|
// Don't allow system failures to generate popups.
|
|
//
|
|
|
|
SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
|
|
|
|
//
|
|
// Create notification event to indicate that this list
|
|
// has changed.
|
|
//
|
|
|
|
NewEventList->ListNotify = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (NewEventList->ListNotify == NULL) {
|
|
CL_UNEXPECTED_ERROR(GetLastError());
|
|
}
|
|
|
|
RmpAddPollEvent(NewEventList, NewEventList->ListNotify, NULL);
|
|
|
|
//
|
|
// Create a shutdown event
|
|
//
|
|
NewEventList->hEventShutdown = CreateEvent( NULL, // Security attributes
|
|
FALSE, // Auto reset event
|
|
FALSE, // Nonsignaled initial state
|
|
NULL ); // Name
|
|
|
|
if ( NewEventList->hEventShutdown == NULL )
|
|
{
|
|
CL_UNEXPECTED_ERROR(GetLastError());
|
|
}
|
|
|
|
//
|
|
// Add the shutdown event to the poll list
|
|
//
|
|
RmpAddPollEvent( NewEventList, NewEventList->hEventShutdown, NULL );
|
|
|
|
//
|
|
// Make a copy of the NewEventList first time through.
|
|
//
|
|
|
|
AcquireEventListLock( NewEventList );
|
|
|
|
CopyMemory( &waitEventList,
|
|
NewEventList,
|
|
sizeof(POLL_EVENT_LIST)
|
|
);
|
|
|
|
ReleaseEventListLock( NewEventList );
|
|
|
|
try_again:
|
|
//
|
|
// Compute initial timeout.
|
|
//
|
|
Timeout = RmpComputeNextTimeout( NewEventList );
|
|
|
|
//
|
|
// There are four functions performed by this thread...
|
|
//
|
|
// 1. Handle timers for polling.
|
|
// 2. Handle list notification changes and updates to the number of
|
|
// events handled by the WaitForMultipleObjects.
|
|
// 3. Handle events set by resource DLL's to deliver asynchronous
|
|
// event (failure) notifications.
|
|
// 4. Handle a shutdown request.
|
|
//
|
|
// N.B. Handles cannot go away while we are waiting... it is therefore
|
|
// best to set the event for the ListNotify event so we can redo the
|
|
// wait event list.
|
|
//
|
|
|
|
while (TRUE) {
|
|
//
|
|
// Wait for any of the events to be signaled.
|
|
//
|
|
CL_ASSERT(waitEventList.Handle[0] == NewEventList->ListNotify);
|
|
Status = WaitForMultipleObjects(waitEventList.EventCount,
|
|
&waitEventList.Handle[0],
|
|
FALSE,
|
|
Timeout);
|
|
if (Status == WAIT_TIMEOUT) {
|
|
//
|
|
// Time period has elapsed, go poll everybody
|
|
//
|
|
Timeout = RmpPollList( NewEventList );
|
|
WaitFailed = 0;
|
|
} else {
|
|
//
|
|
// If the first event is signaled, which is the ListNotify event,
|
|
// then the list changed or a new poll event was added.
|
|
//
|
|
if ( Status == WAIT_OBJECT_0 ) {
|
|
get_new_list:
|
|
WaitFailed = 0;
|
|
//
|
|
// The list has changed or we have a new event to wait for,
|
|
// recompute a new timeout and make a copy of the new event list
|
|
//
|
|
AcquireEventListLock( NewEventList );
|
|
|
|
CopyMemory( &waitEventList,
|
|
NewEventList,
|
|
sizeof(POLL_EVENT_LIST)
|
|
);
|
|
|
|
|
|
ReleaseEventListLock( NewEventList );
|
|
|
|
Timeout = RmpComputeNextTimeout( NewEventList );
|
|
|
|
} else if ( Status == WAIT_OBJECT_0 + 1 ) {
|
|
//
|
|
// This thread has been asked to shutdown, so exit.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE, "[RM] RmpPollerThread: Asked to exit...\n");
|
|
break;
|
|
}
|
|
else if ( Status == WAIT_FAILED ) {
|
|
//
|
|
// We've probably signaled an event, and closed the handle
|
|
// already. Wait on the Notify Event for just a little bit.
|
|
// If that event fires, then copy a new event list. But only
|
|
// try this 100 times.
|
|
//
|
|
if ( ++WaitFailed < 100 ) {
|
|
Status = WaitForSingleObject( waitEventList.ListNotify,
|
|
100 );
|
|
if ( RmpShutdown ) {
|
|
break;
|
|
}
|
|
if ( Status == WAIT_TIMEOUT ) {
|
|
continue;
|
|
} else {
|
|
goto get_new_list;
|
|
}
|
|
} else {
|
|
Status = GetLastError();
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// One of the resource events was signaled!
|
|
//
|
|
WaitFailed = 0;
|
|
CL_ASSERT( WAIT_OBJECT_0 == 0 );
|
|
RmpResourceEventSignaled( &waitEventList,
|
|
Status );
|
|
Timeout = RmpComputeNextTimeout( NewEventList );
|
|
}
|
|
}
|
|
}
|
|
|
|
ClRtlLogPrint( LOG_NOISE,
|
|
"[RM] PollerThread stopping. Shutdown = %1!u!, Status = %2!u!, "
|
|
"WaitFailed = %3!u!, NotifyEvent address = %4!u!.\n",
|
|
RmpShutdown,
|
|
Status,
|
|
WaitFailed,
|
|
waitEventList.ListNotify);
|
|
|
|
#if 1 // RodGa - this is for debug only!
|
|
WaitFailed = 0;
|
|
if ( Status == ERROR_INVALID_HANDLE ) {
|
|
DWORD i;
|
|
for ( i = 0; i < waitEventList.EventCount; i++ ) {
|
|
ClRtlLogPrint( LOG_NOISE, "[RM] Event address %1!u!, index %2!u!.\n",
|
|
waitEventList.Handle[i], i);
|
|
Status = WaitForSingleObject( waitEventList.Handle[i], 10 );
|
|
if ( (Status == WAIT_FAILED) &&
|
|
(GetLastError() == ERROR_INVALID_HANDLE) )
|
|
{
|
|
ClRtlLogPrint( LOG_UNUSUAL, "[RM] Event address %1!u!, index %2!u! is bad. Removing...\n",
|
|
waitEventList.Handle[i], i);
|
|
RmpRemovePollEvent( NewEventList, waitEventList.Handle[i] );
|
|
|
|
//
|
|
// Copy new list... and try again.
|
|
//
|
|
AcquireEventListLock( NewEventList );
|
|
|
|
CopyMemory( &waitEventList,
|
|
NewEventList,
|
|
sizeof(POLL_EVENT_LIST)
|
|
);
|
|
|
|
|
|
ReleaseEventListLock( NewEventList );
|
|
|
|
goto try_again;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CL_ASSERT( NewEventList->ListNotify );
|
|
CL_ASSERT( waitEventList.ListNotify == NewEventList->ListNotify );
|
|
CloseHandle( NewEventList->ListNotify );
|
|
NewEventList->ListNotify = NULL;
|
|
|
|
CL_ASSERT( NewEventList->hEventShutdown );
|
|
CloseHandle( NewEventList->hEventShutdown );
|
|
NewEventList->hEventShutdown = NULL;
|
|
|
|
return(0);
|
|
} // RmpPollerThread
|
|
|
|
|
|
|
|
DWORD
|
|
RmpComputeNextTimeout(
|
|
IN PPOLL_EVENT_LIST EventList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches the resource list to determine the number of milliseconds
|
|
until the next poll event.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
0 - A poll interval has already elapsed.
|
|
INFINITE - No resources to poll
|
|
number of milliseconds until the next poll event.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Timeout;
|
|
PMONITOR_BUCKET Bucket;
|
|
DWORDLONG NextDueTime;
|
|
DWORDLONG CurrentTime;
|
|
DWORDLONG WaitTime;
|
|
|
|
AcquireEventListLock( EventList );
|
|
if (!IsListEmpty(&EventList->BucketListHead)) {
|
|
Bucket = CONTAINING_RECORD(EventList->BucketListHead.Flink,
|
|
MONITOR_BUCKET,
|
|
BucketList);
|
|
NextDueTime = Bucket->DueTime;
|
|
Bucket = CONTAINING_RECORD(Bucket->BucketList.Flink,
|
|
MONITOR_BUCKET,
|
|
BucketList);
|
|
while (&Bucket->BucketList != &EventList->BucketListHead) {
|
|
if (Bucket->DueTime < NextDueTime) {
|
|
NextDueTime = Bucket->DueTime;
|
|
}
|
|
|
|
Bucket = CONTAINING_RECORD(Bucket->BucketList.Flink,
|
|
MONITOR_BUCKET,
|
|
BucketList);
|
|
}
|
|
|
|
//
|
|
// Compute the number of milliseconds from the current time
|
|
// until the next due time. This is our timeout value.
|
|
//
|
|
GetSystemTimeAsFileTime((LPFILETIME)&CurrentTime);
|
|
if (NextDueTime > CurrentTime) {
|
|
WaitTime = NextDueTime - CurrentTime;
|
|
CL_ASSERT(WaitTime < (DWORDLONG)0xffffffff * 10000); // check for excessive value
|
|
Timeout = (ULONG)(WaitTime / 10000);
|
|
} else {
|
|
//
|
|
// The next poll time has already passed, timeout immediately
|
|
// and go poll the list.
|
|
//
|
|
Timeout = 0;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Nothing to poll, so wait on the ListNotify event forever.
|
|
//
|
|
Timeout = INFINITE;
|
|
}
|
|
ReleaseEventListLock( EventList );
|
|
|
|
return(Timeout);
|
|
|
|
} // RmpComputeNextTimeout
|
|
|
|
|
|
|
|
DWORD
|
|
RmpPollList(
|
|
IN PPOLL_EVENT_LIST EventList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Polls all resources in the resource list whose timeouts have
|
|
expired. Recomputes the next timeout interval for each polled
|
|
resource.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The number of milliseconds until the next poll event.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
DWORD Timeout = INFINITE;
|
|
DWORDLONG NextDueTime;
|
|
DWORDLONG CurrentTime;
|
|
DWORDLONG WaitTime;
|
|
PMONITOR_BUCKET Bucket;
|
|
|
|
AcquireEventListLock( EventList );
|
|
|
|
if (!IsListEmpty(&EventList->BucketListHead)) {
|
|
Bucket = CONTAINING_RECORD(EventList->BucketListHead.Flink,
|
|
MONITOR_BUCKET,
|
|
BucketList);
|
|
NextDueTime = Bucket->DueTime;
|
|
while (&Bucket->BucketList != &EventList->BucketListHead) {
|
|
GetSystemTimeAsFileTime((LPFILETIME)&CurrentTime);
|
|
if (CurrentTime >= Bucket->DueTime) {
|
|
//
|
|
// This poll interval has expired. Compute the
|
|
// next poll interval and poll this bucket now.
|
|
//
|
|
CL_ASSERT( Bucket->Period != 0 );
|
|
Bucket->DueTime = CurrentTime + Bucket->Period;
|
|
|
|
RmpPollBucket(Bucket);
|
|
}
|
|
//
|
|
// If this bucket is the closest upcoming event,
|
|
// update NextDueTime.
|
|
//
|
|
if (Bucket->DueTime < NextDueTime) {
|
|
NextDueTime = Bucket->DueTime;
|
|
}
|
|
Bucket = CONTAINING_RECORD(Bucket->BucketList.Flink,
|
|
MONITOR_BUCKET,
|
|
BucketList);
|
|
}
|
|
|
|
//
|
|
// Compute new timeout value in milliseconds
|
|
//
|
|
GetSystemTimeAsFileTime((LPFILETIME)&CurrentTime);
|
|
if (CurrentTime > NextDueTime) {
|
|
//
|
|
// The next timeout has already expired
|
|
//
|
|
WaitTime = Timeout = 0;
|
|
} else {
|
|
WaitTime = NextDueTime - CurrentTime;
|
|
CL_ASSERT(WaitTime < (DWORDLONG)0xffffffff * 10000); // check for excessive value
|
|
Timeout = (ULONG)(WaitTime / 10000);
|
|
}
|
|
}
|
|
|
|
ReleaseEventListLock( EventList );
|
|
return(Timeout);
|
|
|
|
} // RmpPollList
|
|
|
|
|
|
|
|
VOID
|
|
RmpPollBucket(
|
|
IN PMONITOR_BUCKET Bucket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Polls all the resources in a given bucket. Updates their state and notifies
|
|
cluster manager as appropriate.
|
|
|
|
Arguments:
|
|
|
|
Bucket - Supplies the bucket containing the list of resources to be polled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY CurrentEntry;
|
|
PRESOURCE Resource;
|
|
BOOL Success = TRUE;
|
|
PRM_DUE_TIME_ENTRY pDueTimeEntry;
|
|
|
|
CurrentEntry = Bucket->ResourceList.Flink;
|
|
while (CurrentEntry != &Bucket->ResourceList) {
|
|
Resource = CONTAINING_RECORD(CurrentEntry,RESOURCE,ListEntry);
|
|
//
|
|
// The EventList Lock protects concurrent calls to individual
|
|
// resources. The EventList Lock was taken out in RmpPollList.
|
|
// If we increase the granularity of locking, and lock the resource
|
|
// then we'd add a lock here.
|
|
//
|
|
if (Resource->State == ClusterResourceOnline) {
|
|
|
|
//
|
|
// A resource that is online alternates between LooksAlive
|
|
// and IsAlive polling by doing an IsAlive poll instead of
|
|
// a LooksAlive poll every IsAliveCount iterations.
|
|
//
|
|
Resource->IsAliveCount += 1;
|
|
CL_ASSERT( Resource->IsAliveRollover != 0 );
|
|
if (Resource->IsAliveCount == Resource->IsAliveRollover) {
|
|
|
|
//
|
|
// Poll the IsAlive entrypoint.
|
|
//
|
|
|
|
RmpSetMonitorState(RmonIsAlivePoll, Resource);
|
|
pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName,
|
|
Resource->ResourceType,
|
|
Resource->ResourceName,
|
|
L"Is alive" );
|
|
#ifdef COMRES
|
|
Success = RESMON_ISALIVE (Resource) ;
|
|
#else
|
|
Success = (Resource->IsAlive)(Resource->Id);
|
|
#endif
|
|
RmpRemoveDeadlockMonitorList ( pDueTimeEntry );
|
|
RmpSetMonitorState(RmonIdle, NULL);
|
|
//
|
|
// If this was successful, then we will perform the LooksAlive
|
|
// test next time. Otherwise, we do the IsAlive check again.
|
|
//
|
|
if (Success) {
|
|
Resource->IsAliveCount = 0;
|
|
} else {
|
|
--Resource->IsAliveCount;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Poll the LooksAlive entrypoint.
|
|
//
|
|
if ( Resource->EventHandle == NULL ) {
|
|
RmpSetMonitorState(RmonLooksAlivePoll,Resource);
|
|
pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName,
|
|
Resource->ResourceType,
|
|
Resource->ResourceName,
|
|
L"Looks alive" );
|
|
|
|
#ifdef COMRES
|
|
Success = RESMON_LOOKSALIVE (Resource) ;
|
|
#else
|
|
Success = (Resource->LooksAlive)(Resource->Id);
|
|
#endif
|
|
RmpSetMonitorState(RmonIdle, NULL);
|
|
RmpRemoveDeadlockMonitorList ( pDueTimeEntry );
|
|
}
|
|
if ( !Success ) {
|
|
RmpSetMonitorState(RmonIsAlivePoll, Resource);
|
|
pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName,
|
|
Resource->ResourceType,
|
|
Resource->ResourceName,
|
|
L"Is alive (looksalive fail)" );
|
|
#ifdef COMRES
|
|
Success = RESMON_ISALIVE (Resource) ;
|
|
#else
|
|
Success = (Resource->IsAlive)(Resource->Id);
|
|
#endif
|
|
RmpSetMonitorState(RmonIdle, NULL);
|
|
RmpRemoveDeadlockMonitorList ( pDueTimeEntry );
|
|
}
|
|
}
|
|
if (!Success) {
|
|
//
|
|
// The resource has failed. Mark it as Failed and notify
|
|
// the cluster manager.
|
|
//
|
|
Resource->State = ClusterResourceFailed;
|
|
RmpPostNotify(Resource, NotifyResourceStateChange);
|
|
}
|
|
}
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
}
|
|
} // RmpPollBucket
|
|
|
|
|
|
|
|
VOID
|
|
RmpSignalPoller(
|
|
IN PPOLL_EVENT_LIST EventList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Interface to notify the poller thread that the resource list has
|
|
been changed or a new event has been added to the poll event list.
|
|
The poller thread should get a new event list and recompute its timeouts.
|
|
|
|
Arguments:
|
|
|
|
EventList - the event list that is to be notified.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL Success;
|
|
|
|
if (EventList->ListNotify != NULL) {
|
|
Success = SetEvent(EventList->ListNotify);
|
|
CL_ASSERT(Success);
|
|
}
|
|
|
|
} // RmpSignalPoller
|
|
|
|
|