|
|
/*++
Copyright (c) 2001-2001 Microsoft Corporation
Module Name:
timeouts.cxx
Abstract:
Implement connection timeout quality-of-service (QoS) functionality.
The following timers must be monitored during the lifetime of a connection:
* Connection Timeout * Header Wait Timeout * Entity Body Receive Timeout * Response Processing Timeout * Minimum Bandwidth (implemented as a Timeout)
When any one of these timeout values expires, the connection should be terminated.
The timer information is maintained in a timeout info block, UL_TIMEOUT_INFO_ENTRY, as part of the UL_HTTP_CONNECTION object.
A timer can be Set or Reset. Setting a timer calculates when the specific timer should expire, and updates the timeout info block. Resetting a timer turns a specific timer off. Both Setting and Resettnig a timer will cause the timeout block to be re-evaluated to find the least valued expiry time.
// TODO:
The timeout manager uses a Timer Wheel(c) technology, as used by NT's TCP/IP stack for monitoring TCB timeouts. We will reimplement and modify the logic they use. The linkage for the Timer Wheel(c) queues is provided in the timeout info block.
// TODO: CONVERT TO USING Timer Wheel Ticks instead of SystemTime.
There are three separate units of time: SystemTime (100ns intervals), Timer Wheel Ticks (SystemTime / slot interval length), and Timer Wheel Slot (Timer Wheel Ticks modulo the number of slots in the Timer Wheel).
Author:
Eric Stenson (EricSten) 24-Mar-2001
Revision History:
This was originally implemented as the Connection Timeout Monitor.
--*/
#include "precomp.h"
#include "timeoutsp.h"
//
// Private globals.
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, UlInitializeTimeoutMonitor )
#pragma alloc_text( PAGE, UlTerminateTimeoutMonitor )
#pragma alloc_text( PAGE, UlSetTimeoutMonitorInformation )
#pragma alloc_text( PAGE, UlpTimeoutMonitorWorker )
#pragma alloc_text( PAGE, UlSetPerSiteConnectionTimeoutValue )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlpSetTimeoutMonitorTimer NOT PAGEABLE -- UlpTimeoutMonitorDpcRoutine NOT PAGEABLE -- UlpTimeoutCheckExpiry NOT PAGEABLE -- UlpTimeoutInsertTimerWheelEntry NOT PAGEABLE -- UlTimeoutRemoveTimerWheelEntry NOT PAGEABLE -- UlInitializeConnectionTimerInfo NOT PAGEABLE -- UlSetConnectionTimer NOT PAGEABLE -- UlSetMinKBSecTimer NOT PAGEABLE -- UlResetConnectionTimer NOT PAGEABLE -- UlEvaluateTimerState #endif // 0
//
// Connection Timeout Montior globals
//
LONG g_TimeoutMonitorInitialized = FALSE; KDPC g_TimeoutMonitorDpc; KTIMER g_TimeoutMonitorTimer; KEVENT g_TimeoutMonitorTerminationEvent; KEVENT g_TimeoutMonitorAddListEvent; UL_WORK_ITEM g_TimeoutMonitorWorkItem;
//
// Timeout constants
//
ULONG g_TM_MinKBSecDivisor; // Bytes/Sec
LONGLONG g_TM_ConnectionTimeout; // 100ns ticks (Global...can be overriden)
LONGLONG g_TM_HeaderWaitTimeout; // 100ns ticks
//
// NOTE: Must be in sync with the _CONNECTION_TIMEOUT_TIMERS enum
//
CHAR *g_aTimeoutTimerNames[] = { "ConnectionIdle", // TimerConnectionIdle
"HeaderWait", // TimerHeaderWait
"MinKBSec", // TimerMinKBSec
"EntityBody", // TimerEntityBody
"Response", // TimerResponse
};
//
// Timer Wheel(c)
//
static LIST_ENTRY g_TimerWheel[TIMER_WHEEL_SLOTS+1]; // TODO: alloc on its own page.
static UL_SPIN_LOCK g_TimerWheelMutex; static USHORT g_TimerWheelCurrentSlot;
//
// Public functions.
//
/***************************************************************************++
Routine Description:
Initializes the Timeout Monitor and kicks off the first polling interval
Arguments:
(none)
--***************************************************************************/ VOID UlInitializeTimeoutMonitor( VOID ) { int i; LARGE_INTEGER Now;
//
// Sanity check.
//
PAGED_CODE();
UlTrace(TIMEOUTS, ( "http!UlInitializeTimeoutMonitor\n" ));
ASSERT( FALSE == g_TimeoutMonitorInitialized );
//
// Set default configuration information.
//
g_TM_ConnectionTimeout = 15 * 60 * C_NS_TICKS_PER_SEC; // 15 min
g_TM_HeaderWaitTimeout = 15 * 60 * C_NS_TICKS_PER_SEC; // 15 min
g_TM_MinKBSecDivisor = 0; // 0 == Disabled
//
// Init Timer Wheel(c) state
//
//
// Set current slot
//
KeQuerySystemTime(&Now); g_TimerWheelCurrentSlot = UlpSystemTimeToTimerWheelSlot(Now.QuadPart);
//
// Init Timer Wheel(c) slots & mutex
//
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); // InitializeListHead requirement
for ( i = 0; i < TIMER_WHEEL_SLOTS ; i++ ) { InitializeListHead( &(g_TimerWheel[i]) ); }
InitializeListHead( &(g_TimerWheel[TIMER_OFF_SLOT]) );
UlInitializeSpinLock( &(g_TimerWheelMutex), "TimeoutMonitor" );
//
// Init DPC object & set DPC routine
//
KeInitializeDpc( &g_TimeoutMonitorDpc, // DPC object
&UlpTimeoutMonitorDpcRoutine, // DPC routine
NULL // context
);
KeInitializeTimer( &g_TimeoutMonitorTimer );
//
// Event to control rescheduling of the timeout monitor timer
//
KeInitializeEvent( &g_TimeoutMonitorAddListEvent, NotificationEvent, TRUE );
//
// Initialize the termination event.
//
KeInitializeEvent( &g_TimeoutMonitorTerminationEvent, NotificationEvent, FALSE );
//
// Init done!
//
InterlockedExchange( &g_TimeoutMonitorInitialized, TRUE );
//
// Kick-off the first monitor sleep period
//
UlpSetTimeoutMonitorTimer(); }
/***************************************************************************++
Routine Description:
Terminate the Timeout Monitor, including any pending timer events.
Arguments:
(none)
--***************************************************************************/ VOID UlTerminateTimeoutMonitor( VOID ) { //
// Sanity check.
//
PAGED_CODE();
UlTrace(TIMEOUTS, ( "http!UlTerminateTimeoutMonitor\n" ));
//
// Clear the "initialized" flag. If the timeout monitor runs soon,
// it will see this flag, set the termination event, and exit
// quickly.
//
if ( TRUE == InterlockedCompareExchange( &g_TimeoutMonitorInitialized, FALSE, TRUE) ) { //
// Cancel the timeout monitor timer. If it fails, the monitor
// is running. Wait for it to complete.
//
if ( !KeCancelTimer( &g_TimeoutMonitorTimer ) ) { KeWaitForSingleObject( (PVOID)&g_TimeoutMonitorTerminationEvent, UserRequest, KernelMode, FALSE, NULL ); }
}
UlTrace(TIMEOUTS, ( "http!UlTerminateTimeoutMonitor: Done!\n" ));
} // UlpTerminateTimeoutMonitor
/***************************************************************************++
Routine Description:
Sets the global Timeout Monitor configuration information
Arguments:
pInfo pointer to HTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT structure
--***************************************************************************/ VOID UlSetTimeoutMonitorInformation( IN PHTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT pInfo ) { LONGLONG localValue; LONGLONG newValue; LONGLONG originalValue;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( pInfo );
UlTrace(TIMEOUTS, ( "http!UlSetTimeoutMonitorInformation:\n" " ConnectionTimeout: %d\n" " HeaderWaitTimeout: %d\n" " MinFileKbSec: %d\n", pInfo->ConnectionTimeout, pInfo->HeaderWaitTimeout, pInfo->MinFileKbSec ));
if ( pInfo->ConnectionTimeout ) { UlInterlockedExchange64( &g_TM_ConnectionTimeout, (LONGLONG)(pInfo->ConnectionTimeout * C_NS_TICKS_PER_SEC) ); }
if ( pInfo->HeaderWaitTimeout ) { UlInterlockedExchange64( &g_TM_HeaderWaitTimeout, (LONGLONG)(pInfo->HeaderWaitTimeout * C_NS_TICKS_PER_SEC) ); }
if ( pInfo->MinFileKbSec ) { InterlockedExchange( (PLONG)&g_TM_MinKBSecDivisor, pInfo->MinFileKbSec ); }
} // UlSetTimeoutMonitorInformation
/***************************************************************************++
Routine Description:
Sets up a timer event to fire after the polling interval has expired.
Arguments:
(none)
--***************************************************************************/ VOID UlpSetTimeoutMonitorTimer( VOID ) { LARGE_INTEGER TimeoutMonitorInterval;
ASSERT( TRUE == g_TimeoutMonitorInitialized );
UlTrace(TIMEOUTS, ( "http!UlpSetTimeoutMonitorTimer\n" ));
//
// Don't want to execute this more often than few seconds.
// In particular, do not want to execute this every 0 seconds, as the
// machine will become completely unresponsive.
//
//
// negative numbers mean relative time
//
TimeoutMonitorInterval.QuadPart = -DEFAULT_POLLING_INTERVAL;
KeSetTimer( &g_TimeoutMonitorTimer, TimeoutMonitorInterval, &g_TimeoutMonitorDpc );
}
/***************************************************************************++
Routine Description:
Dispatch routine called by the timer event that queues up the Timeout Montior
Arguments:
(all ignored)
--***************************************************************************/ VOID UlpTimeoutMonitorDpcRoutine( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) {
if( g_TimeoutMonitorInitialized ) { //
// Do that timeout monitor thang.
//
UL_QUEUE_WORK_ITEM( &g_TimeoutMonitorWorkItem, &UlpTimeoutMonitorWorker );
}
} // UlpTimeoutMonitorDpcRoutine
/***************************************************************************++
Routine Description:
Timeout Monitor thread
Arguments:
pWorkItem (ignored)
--***************************************************************************/ VOID UlpTimeoutMonitorWorker( IN PUL_WORK_ITEM pWorkItem ) { ULONG TimeoutMonitorEntriesSeen; LARGE_INTEGER Now;
//
// sanity check
//
PAGED_CODE();
UlTrace(TIMEOUTS, ( "http!UlpTimeoutMonitorWorker\n" ));
//
// Check for things to expire now.
//
UlpTimeoutCheckExpiry();
UlTrace(TIMEOUTS, ( "http!UlpTimeoutMonitorWorker: g_TimerWheelCurrentSlot is now %d\n", g_TimerWheelCurrentSlot ));
if ( g_TimeoutMonitorInitialized ) { //
// Reschedule ourselves
//
UlpSetTimeoutMonitorTimer(); } else { //
// Signal shutdown event
//
KeSetEvent( &g_TimeoutMonitorTerminationEvent, 0, FALSE ); }
} // UlpTimeoutMonitor
/***************************************************************************++
Routine Description:
Walks a given timeout watch list looking for items that should be expired
Returns:
Count of connections remaining on list (after all expired connections removed)
Notes:
Possible Issue: Since we use the system time to check to see if something should be expired, it's possible we will mis-expire some connections if the system time is set forward.
Similarly, if the clock is set backward, we may not expire connections as expected.
--***************************************************************************/ ULONG UlpTimeoutCheckExpiry( VOID ) { LARGE_INTEGER Now; KIRQL OldIrql; PLIST_ENTRY pEntry; PLIST_ENTRY pHead; PUL_HTTP_CONNECTION pHttpConn; PUL_TIMEOUT_INFO_ENTRY pInfo; LIST_ENTRY ZombieList; ULONG Entries; USHORT Limit; USHORT CurrentSlot;
PAGED_CODE();
//
// Init zombie list
//
InitializeListHead(&ZombieList);
//
// Get current time
//
KeQuerySystemTime(&Now);
Limit = UlpSystemTimeToTimerWheelSlot( Now.QuadPart ); ASSERT( TIMER_OFF_SLOT != Limit );
//
// Lock Timer Wheel(c)
//
UlAcquireSpinLock( &g_TimerWheelMutex, &OldIrql );
CurrentSlot = g_TimerWheelCurrentSlot;
//
// Walk the slots up until Limit
//
Entries = 0;
while ( CurrentSlot != Limit ) { pHead = &(g_TimerWheel[CurrentSlot]); pEntry = pHead->Flink;
ASSERT( pEntry );
//
// Walk this slot's queue
//
while ( pEntry != pHead ) { pInfo = CONTAINING_RECORD( pEntry, UL_TIMEOUT_INFO_ENTRY, QueueEntry );
ASSERT( MmIsAddressValid(pInfo) );
pHttpConn = CONTAINING_RECORD( pInfo, UL_HTTP_CONNECTION, TimeoutInfo );
ASSERT( (pHttpConn != NULL) && \ (pHttpConn->Signature == UL_HTTP_CONNECTION_POOL_TAG) );
//
// go to next node (in case we remove the current one from the list)
//
pEntry = pEntry->Flink; Entries++;
if (0 == pHttpConn->RefCount) { //
// If the ref-count has gone to zero, the httpconn will be
// cleaned up soon; ignore this item and let the cleanup
// do its job.
//
Entries--; continue; // inner while loop
}
//
// See if we should move this entry to a different slot
//
if ( pInfo->SlotEntry != CurrentSlot ) { ASSERT( IS_VALID_TIMER_WHEEL_SLOT(pInfo->SlotEntry) ); ASSERT( pInfo->QueueEntry.Flink != NULL );
//
// Move to correct slot
//
RemoveEntryList( &pInfo->QueueEntry );
InsertTailList( &(g_TimerWheel[pInfo->SlotEntry]), &pInfo->QueueEntry );
Entries--;
continue; // inner while loop
}
//
// See if we should expire this connection
//
UlAcquireSpinLockAtDpcLevel( &pInfo->Lock );
if ( pInfo->CurrentExpiry < Now.QuadPart ) { UlTrace(TIMEOUTS, ( "http!UlpTimeoutCheckExpiry: pInfo %p expired because %s timer\n", pInfo, g_aTimeoutTimerNames[pInfo->CurrentTimer] ));
//
// Expired. Remove entry from list & move to Zombie list
//
RemoveEntryList( &pInfo->QueueEntry );
pInfo->QueueEntry.Flink = NULL;
InsertTailList( &ZombieList, &pInfo->ZombieEntry );
//
// Add ref the pHttpConn to prevent it being killed before we
// can kill it ourselves. (zombifying)
//
UL_REFERENCE_HTTP_CONNECTION(pHttpConn); }
UlReleaseSpinLockFromDpcLevel( &pInfo->Lock );
} // Walk slot queue
CurrentSlot = ((CurrentSlot + 1) % TIMER_WHEEL_SLOTS);
} // ( CurrentSlot != Limit )
g_TimerWheelCurrentSlot = Limit;
UlReleaseSpinLock( &g_TimerWheelMutex, OldIrql );
//
// remove entries on zombie list
//
if ( !IsListEmpty(&ZombieList) ) { pEntry = ZombieList.Flink; while ( &ZombieList != pEntry ) {
pInfo = CONTAINING_RECORD( pEntry, UL_TIMEOUT_INFO_ENTRY, ZombieEntry );
ASSERT( MmIsAddressValid(pInfo) );
pHttpConn = CONTAINING_RECORD( pInfo, UL_HTTP_CONNECTION, TimeoutInfo );
ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConn) );
pEntry = pEntry->Flink;
UlCloseConnection(pHttpConn->pConnection, TRUE, NULL, NULL);
//
// Remove the reference we added when zombifying
//
UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
Entries--; } }
return Entries;
} // UlpTimeoutCheckExpiry
//
// New Timer Wheel(c) primatives
//
/***************************************************************************++
Routine Description:
Arguments:
--***************************************************************************/ VOID UlInitializeConnectionTimerInfo( PUL_TIMEOUT_INFO_ENTRY pInfo ) { LARGE_INTEGER Now; int i;
ASSERT( TRUE == g_TimeoutMonitorInitialized );
//
// Get current time
//
KeQuerySystemTime(&Now);
//
// Init Lock
//
UlInitializeSpinLock( &pInfo->Lock, "TimeoutInfoLock" );
//
// Timer state
//
ASSERT( 0 == TimerConnectionIdle );
pInfo->Timers[TimerConnectionIdle] = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_ConnectionTimeout);
for ( i = 1; i < TimerMaximumTimer; i++ ) { pInfo->Timers[i] = TIMER_OFF_TICK; }
pInfo->CurrentTimer = TimerConnectionIdle; pInfo->CurrentExpiry = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_ConnectionTimeout); pInfo->MinKBSecSystemTime = TIMER_OFF_SYSTIME;
pInfo->ConnectionTimeoutValue = g_TM_ConnectionTimeout;
//
// Wheel state
//
pInfo->SlotEntry = UlpTimerWheelTicksToTimerWheelSlot( pInfo->CurrentExpiry ); UlpTimeoutInsertTimerWheelEntry(pInfo);
} // UlInitializeConnectionTimerInfo
/***************************************************************************++
Routine Description:
Arguments:
--***************************************************************************/ VOID UlpTimeoutInsertTimerWheelEntry( PUL_TIMEOUT_INFO_ENTRY pInfo ) { KIRQL OldIrql;
ASSERT( NULL != pInfo ); ASSERT( TRUE == g_TimeoutMonitorInitialized ); ASSERT( IS_VALID_TIMER_WHEEL_SLOT(pInfo->SlotEntry) );
UlTrace(TIMEOUTS, ( "http!UlTimeoutInsertTimerWheelEntry: pInfo %p Slot %d\n", pInfo, pInfo->SlotEntry ));
UlAcquireSpinLock( &g_TimerWheelMutex, &OldIrql );
InsertTailList( &(g_TimerWheel[pInfo->SlotEntry]), &pInfo->QueueEntry );
UlReleaseSpinLock( &g_TimerWheelMutex, OldIrql );
} // UlTimeoutInsertTimerWheel
/***************************************************************************++
Routine Description:
Arguments:
--***************************************************************************/ VOID UlTimeoutRemoveTimerWheelEntry( PUL_TIMEOUT_INFO_ENTRY pInfo ) { KIRQL OldIrql;
ASSERT( NULL != pInfo ); ASSERT( !IsListEmpty(&pInfo->QueueEntry) );
UlTrace(TIMEOUTS, ( "http!UlTimeoutRemoveTimerWheelEntry: pInfo %p\n", pInfo ));
UlAcquireSpinLock( &g_TimerWheelMutex, &OldIrql );
if (pInfo->QueueEntry.Flink != NULL) { RemoveEntryList( &pInfo->QueueEntry );
pInfo->QueueEntry.Flink = NULL; }
UlReleaseSpinLock( &g_TimerWheelMutex, OldIrql );
} // UlTimeoutRemoveTimerWheelEntry
/***************************************************************************++
Routine Description:
Set the per Site Connection Timeout Value override
Arguments:
pInfo Timeout info block
TimeoutValue Override value
--***************************************************************************/ VOID UlSetPerSiteConnectionTimeoutValue( PUL_TIMEOUT_INFO_ENTRY pInfo, LONGLONG TimeoutValue ) { ASSERT( NULL != pInfo ); ASSERT( 0L != TimeoutValue );
PAGED_CODE();
UlTrace(TIMEOUTS, ( "http!UlSetPerSiteConnectionTimeoutValue: pInfo %p TimeoutValue %I64X.\n", pInfo, TimeoutValue ));
ExInterlockedCompareExchange64( &pInfo->ConnectionTimeoutValue, // Destination
&TimeoutValue, // Exchange
&pInfo->ConnectionTimeoutValue, // Comperand
&pInfo->Lock.KSpinLock // Lock
);
} // UlSetPerSiteConnectionTimeoutValue
/***************************************************************************++
Routine Description:
Starts a given timer in the timer info block.
Arguments:
pInfo Timer info block
Timer Timer to set
--***************************************************************************/ VOID UlSetConnectionTimer( PUL_TIMEOUT_INFO_ENTRY pInfo, CONNECTION_TIMEOUT_TIMER Timer ) { LARGE_INTEGER Now;
ASSERT( NULL != pInfo ); ASSERT( IS_VALID_TIMEOUT_TIMER(Timer) ); ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
UlTrace(TIMEOUTS, ( "http!UlSetConnectionTimer: pInfo %p Timer %s\n", pInfo, g_aTimeoutTimerNames[Timer] ));
//
// Get current time
//
KeQuerySystemTime(&Now);
//
// Set timer to apropriate value
//
switch ( Timer ) { case TimerConnectionIdle: case TimerEntityBody: case TimerResponse: // all can be handled with the same timeout value
pInfo->Timers[Timer] = TIMER_WHEEL_TICKS(Now.QuadPart + pInfo->ConnectionTimeoutValue); break;
case TimerHeaderWait: pInfo->Timers[TimerHeaderWait] = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_HeaderWaitTimeout); break;
// NOTE: TimerMinKBSec is handled in UlSetMinKBSecTimer()
default: UlTrace(TIMEOUTS, ( "http!UlSetConnectionTimer: Bad Timer! (%d)\n", Timer ));
ASSERT( FALSE );
}
} // UlSetConnectionTimer
/***************************************************************************++
Routine Description:
Turns on the MinKBSec timer, adds the number of secs given the minimum bandwidth specified.
Arguments:
pInfo Timer info block
BytesToSend Bytes to be sent
--***************************************************************************/ VOID UlSetMinKBSecTimer( PUL_TIMEOUT_INFO_ENTRY pInfo, LONGLONG BytesToSend ) { LONGLONG XmitTicks; KIRQL OldIrql; ULONG NewTick; BOOLEAN bCallEvaluate = FALSE;
ASSERT( NULL != pInfo );
UlTrace(TIMEOUTS, ( "http!UlSetMinKBSecTimer: pInfo %p BytesToSend %ld\n", pInfo, BytesToSend ));
if ( g_TM_MinKBSecDivisor ) { if ( 0L != BytesToSend ) { //
// Calculate the estimated time required to send BytesToSend
//
XmitTicks = BytesToSend / g_TM_MinKBSecDivisor;
if (0 == XmitTicks) { XmitTicks = C_NS_TICKS_PER_SEC; } else { XmitTicks *= C_NS_TICKS_PER_SEC; }
UlAcquireSpinLock( &pInfo->Lock, &OldIrql );
if ( TIMER_OFF_SYSTIME == pInfo->MinKBSecSystemTime ) { LARGE_INTEGER Now;
//
// Get current time
//
KeQuerySystemTime(&Now);
pInfo->MinKBSecSystemTime = (Now.QuadPart + XmitTicks);
} else { pInfo->MinKBSecSystemTime += XmitTicks; }
NewTick = TIMER_WHEEL_TICKS(pInfo->MinKBSecSystemTime); if ( NewTick != pInfo->Timers[TimerMinKBSec] ) { bCallEvaluate = TRUE; pInfo->Timers[TimerMinKBSec] = NewTick; }
UlReleaseSpinLock( &pInfo->Lock, OldIrql );
if ( TRUE == bCallEvaluate ) { UlEvaluateTimerState(pInfo); }
}
}
} // UlSetMinKBSecTimer
/***************************************************************************++
Routine Description:
Turns off a given timer in the timer info block.
Arguments:
pInfo Timer info block
Timer Timer to reset
--***************************************************************************/ VOID UlResetConnectionTimer( PUL_TIMEOUT_INFO_ENTRY pInfo, CONNECTION_TIMEOUT_TIMER Timer ) { ASSERT( NULL != pInfo ); ASSERT( IS_VALID_TIMEOUT_TIMER(Timer) ); ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
UlTrace(TIMEOUTS, ( "http!UlResetConnectionTimer: pInfo %p Timer %s\n", pInfo, g_aTimeoutTimerNames[Timer] ));
//
// Turn off timer
//
pInfo->Timers[Timer] = TIMER_OFF_TICK;
if (TimerMinKBSec == Timer) { pInfo->MinKBSecSystemTime = TIMER_OFF_SYSTIME; }
} // UlResetConnectionTimer
/***************************************************************************++
Routine Description:
Turns off all timers
Arguments:
pInfo Timer info block
--***************************************************************************/ VOID UlResetAllConnectionTimers( PUL_TIMEOUT_INFO_ENTRY pInfo ) { int i;
ASSERT( NULL != pInfo ); ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
for ( i = 0; i < TimerMaximumTimer; i++ ) { pInfo->Timers[i] = TIMER_OFF_TICK; }
pInfo->CurrentTimer = TimerConnectionIdle; pInfo->CurrentExpiry = TIMER_OFF_TICK; pInfo->MinKBSecSystemTime = TIMER_OFF_SYSTIME; }
//
// Private functions
//
/***************************************************************************++
Routine Description:
Arguments:
--***************************************************************************/ VOID UlEvaluateTimerState( PUL_TIMEOUT_INFO_ENTRY pInfo ) { int i; ULONG MinTimeout = TIMER_OFF_TICK; CONNECTION_TIMEOUT_TIMER MinTimeoutTimer = TimerConnectionIdle;
ASSERT( NULL != pInfo ); ASSERT( !UlDbgSpinLockOwned( &pInfo->Lock ) );
for ( i = 0; i < TimerMaximumTimer; i++ ) { if (pInfo->Timers[i] < MinTimeout) { MinTimeout = pInfo->Timers[i]; MinTimeoutTimer = (CONNECTION_TIMEOUT_TIMER) i; } }
//
// If we've found a different expiry time, update expiry state.
//
if (pInfo->CurrentExpiry != MinTimeout) { KIRQL OldIrql;
#if DBG
LARGE_INTEGER Now;
KeQuerySystemTime(&Now); ASSERT( MinTimeout >= TIMER_WHEEL_TICKS(Now.QuadPart) ); #endif // DBG
//
// Calculate new slot
//
InterlockedExchange( (LONG *) &pInfo->SlotEntry, UlpTimerWheelTicksToTimerWheelSlot(MinTimeout) );
//
// Move to new slot if necessary
//
if ( (pInfo->SlotEntry != TIMER_OFF_SLOT) && (MinTimeout < pInfo->CurrentExpiry) ) { //
// Only move if it's on the Wheel; If Flink is null, it's in
// the process of being expired.
//
if ( NULL != pInfo->QueueEntry.Flink ) { UlAcquireSpinLock( &g_TimerWheelMutex, &OldIrql );
UlTrace(TIMEOUTS, ( "http!UlEvaluateTimerInfo: pInfo %p: Moving to new slot %d\n", pInfo, pInfo->SlotEntry )); RemoveEntryList( &pInfo->QueueEntry );
InsertTailList( &(g_TimerWheel[pInfo->SlotEntry]), &pInfo->QueueEntry );
UlReleaseSpinLock( &g_TimerWheelMutex, OldIrql );
}
} //
// Update timer wheel state
//
pInfo->CurrentExpiry = MinTimeout; pInfo->CurrentTimer = MinTimeoutTimer;
}
} // UlpEvaluateTimerState
|