Windows-Server-2003/base/cluster/resmon/poller.c

641 lines
19 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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