602 lines
21 KiB
C++
602 lines
21 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
GC.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
The garbage collection mechanism common code. See the comment
|
|||
|
below for more details.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Kamen Moutafov (kamenm) Apr 2000
|
|||
|
|
|||
|
Garbage Collection Mechanism:
|
|||
|
This comment describes how the garbage collection mechanism works.
|
|||
|
The code itself is spread in variety in places.
|
|||
|
|
|||
|
Purpose:
|
|||
|
There are two types of garbage collection we perform - periodic
|
|||
|
and one-time. Periodic may be needed by the Osf idle connection
|
|||
|
cleanup mechanism, which tries to cleanup unused osf connections
|
|||
|
if the app explicitly asked for it via RpcMgmtEnableIdleCleanup.
|
|||
|
The one-time cleanup is used by the lingering associations. If
|
|||
|
an association is lingered, it will request cleanup to be performed
|
|||
|
after a certain period of time. The garbage collection needs to
|
|||
|
support both of those mechanisms.
|
|||
|
|
|||
|
Design Goals:
|
|||
|
Have minimal memory and CPU consumption requirements
|
|||
|
Don't cause periodic background activity if there is no
|
|||
|
garbage collection to be performed.
|
|||
|
Guarantee that garbage collection will be performed in
|
|||
|
a reasonable amount of time after its request time (i.e. 10 minutes
|
|||
|
to an hour at worst case)
|
|||
|
|
|||
|
Implementation:
|
|||
|
We use the worker threads in the thread pools to perform garbage
|
|||
|
collection. There are several thread pools - the Ioc thread pool
|
|||
|
(remote threads) as well as one thread pool for each LRPC address.
|
|||
|
Within each pool, from a gc perspective, we differentiate between
|
|||
|
two types of threads - threads on a short wait and threads on a
|
|||
|
long wait. Threads on a short wait are either threads waiting for
|
|||
|
something to happen with a timeout of gThreadTimeout or less, or
|
|||
|
threads performing a work item (threads doing both are also
|
|||
|
considered to be on a short wait). Threads on a long wait are
|
|||
|
threads waiting for more than that. As part of our thread management
|
|||
|
we will keep count of how many threads are on a short wait and how
|
|||
|
many are on a long wait.
|
|||
|
|
|||
|
All threads in all thread pools will attempt to do garbage collection
|
|||
|
when they timeout waiting for something to happen. Since all thread pools
|
|||
|
need at least one listening thread, all thread pools are guaranteed to
|
|||
|
have a thread timing out once every so often. The garbage collection attempt
|
|||
|
will be cut very short if there is nothing to garbage collect, so the
|
|||
|
attempt is not performance expensive in the common case. The function
|
|||
|
to attempt garbage collection is PerformGarbageCollection
|
|||
|
|
|||
|
If a thread times out on the completion port/LPC port, it will
|
|||
|
do garbage collection, and then will check whether there are
|
|||
|
more items to garbage collect (either one-time or periodic) and how
|
|||
|
many threads from this thread pool are on a short wait. If there is
|
|||
|
garbage collection to be done, and there are no other threads on short
|
|||
|
wait, this thread will not go on a long wait, but it will repeat its
|
|||
|
short wait. This ensures timely garbage collection. If all the threads
|
|||
|
have gone on a long wait, and a piece of code needs garbage collection,
|
|||
|
it will request the garbage collection and it will tickle a worker thread.
|
|||
|
The tickling consist of posting an empty message to the completion port
|
|||
|
or LPC port. All the synchronization b/n worker threads and threads
|
|||
|
requesting garbage collection is done using interlocks, to avoid perf
|
|||
|
hit. This introduces a couple of benign races through the code, which may
|
|||
|
prevent a thread from going on a long wait once, but that's ok.
|
|||
|
|
|||
|
In order to ensure that we do gc only when needed, in most cases we refcount
|
|||
|
the number of items that need garbage collection.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <precomp.hxx>
|
|||
|
#include <hndlsvr.hxx>
|
|||
|
#include <lpcpack.hxx>
|
|||
|
#include <lpcsvr.hxx>
|
|||
|
#include <osfpcket.hxx>
|
|||
|
#include <bitset.hxx>
|
|||
|
#include <queue.hxx>
|
|||
|
#include <ProtBind.hxx>
|
|||
|
#include <osfclnt.hxx>
|
|||
|
#include <rpcqos.h>
|
|||
|
#include <lpcclnt.hxx>
|
|||
|
|
|||
|
// used by periodic cleanup only - the period
|
|||
|
// on which to do cleanup. This is in seconds
|
|||
|
unsigned long WaitToGarbageCollectDelay = 0;
|
|||
|
|
|||
|
// The number of items on which garbage collection
|
|||
|
// is needed. If 0, no periodic garbage collection
|
|||
|
// is necessary. Each item that needs garbage collection
|
|||
|
// will InterlockIncrement this when it is created,
|
|||
|
// and will InterlockDecrement this when it is destroyed
|
|||
|
long PeriodicGarbageCollectItems = 0;
|
|||
|
|
|||
|
// set non-zero when we need to cleanup idle LRPC_SCONTEXTs
|
|||
|
unsigned int fEnableIdleLrpcSContextsCleanup = 0;
|
|||
|
|
|||
|
// set to non-zero when we enable garbage collection cleanup. This either
|
|||
|
// happens when the user calls it explicitly with
|
|||
|
// RpcMgmtEnableIdleCleanup or implicitly if we gather too many
|
|||
|
// connection in an association
|
|||
|
unsigned int fEnableIdleConnectionCleanup = 0;
|
|||
|
|
|||
|
unsigned int IocThreadStarted = 0;
|
|||
|
|
|||
|
// used by one-time garbage collection items only!
|
|||
|
long GarbageCollectionRequested = 0;
|
|||
|
|
|||
|
// The semantics of this variable should be
|
|||
|
// interpreted as follows - don't bother to cleanup
|
|||
|
// before this time stamp - you won't find anything.
|
|||
|
// This means that after this interval, there may be
|
|||
|
// many items to cleanup later on - it just says the
|
|||
|
// first is at this time.
|
|||
|
// The timestamp is in millseconds.
|
|||
|
DWORD NextOneTimeCleanup = 0;
|
|||
|
|
|||
|
|
|||
|
const int MaxPeriodsWithoutGC = 100;
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
GarbageCollectionNeeded (
|
|||
|
IN BOOL fOneTimeCleanup,
|
|||
|
IN unsigned long GarbageCollectInterval
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
A routine used by code throughout RPC to arrange
|
|||
|
for garbage collection to be performed. Currently,
|
|||
|
there are two types of garbage collecting -
|
|||
|
idle Osf connections and lingering associations.
|
|||
|
|
|||
|
Parameters:
|
|||
|
fOneTimeCleanup - if non-zero, this is a one time
|
|||
|
cleanup and GarbageCollectInterval is interpreted as the
|
|||
|
minimum time after which we want garbage collection
|
|||
|
performed. Note that the garbage collection code can kick
|
|||
|
off earlier than that. Appropriate arrangements must be
|
|||
|
made to protect items not due for garbage collection.
|
|||
|
If 0, this is a periodic cleanup, and
|
|||
|
|
|||
|
GarbageCollectInterval is interpreted as the period for
|
|||
|
which we wait before making the next garbage collection
|
|||
|
pass. Note that for the periodic cleanup, this is a hint
|
|||
|
that can be ignored - don't count on it. The time is in
|
|||
|
milliseconds.
|
|||
|
|
|||
|
Return Value:
|
|||
|
non-zero - garbage collection is available and will be done
|
|||
|
FALSE - garbage collection is not available
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RPC_STATUS RpcStatus = RPC_S_OK;
|
|||
|
THREAD * Thread;
|
|||
|
DWORD LocalTickCount;
|
|||
|
LOADABLE_TRANSPORT *LoadableTransport;
|
|||
|
LOADABLE_TRANSPORT *FirstTransport = NULL;
|
|||
|
DictionaryCursor cursor;
|
|||
|
BOOL fRetVal = FALSE;
|
|||
|
LRPC_ADDRESS *CurrentAddress;
|
|||
|
LRPC_ADDRESS *LrpcAddressToTickle = NULL;
|
|||
|
|
|||
|
if (fOneTimeCleanup)
|
|||
|
{
|
|||
|
LocalTickCount = GetTickCount();
|
|||
|
// N.B. There is a race here where two threads can set this -
|
|||
|
// the race is benign - the second thread will win and write
|
|||
|
// its time, which by virtue of the small race window will
|
|||
|
// be shortly after the first thread
|
|||
|
if (!GarbageCollectionRequested)
|
|||
|
{
|
|||
|
NextOneTimeCleanup = LocalTickCount + GarbageCollectInterval;
|
|||
|
GarbageCollectionRequested = 1;
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) GC requested - tick count %d\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), LocalTickCount);
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// WaitToGarbageCollectDelay is a global variable - avoid sloshing
|
|||
|
if (WaitToGarbageCollectDelay == 0)
|
|||
|
WaitToGarbageCollectDelay = GarbageCollectInterval;
|
|||
|
|
|||
|
InterlockedIncrement(&PeriodicGarbageCollectItems);
|
|||
|
}
|
|||
|
|
|||
|
// is the completion port started? If yes, we will use it as the
|
|||
|
// preferred method of garbage collection
|
|||
|
if (IocThreadStarted)
|
|||
|
{
|
|||
|
// if we use the completion port, we either need a thread on a
|
|||
|
// short wait (i.e. it will perform garbage collection soon
|
|||
|
// anyway), or we need to tickle a thread on a long wait. We know
|
|||
|
// that one of these will be true, because we always keep
|
|||
|
// listening threads on the completion port - the only
|
|||
|
// question is whether it is on a long or short wait thread that
|
|||
|
// we have.
|
|||
|
|
|||
|
// this dictionary is guaranteed to never grow beyond the initial
|
|||
|
// dictionary size and elements from it are never deleted - therefore,
|
|||
|
// it is safe to iterate it without holding a mutex - we may miss
|
|||
|
// an element if it was just being added, but that's ok. The important
|
|||
|
// thing is that we can't fault
|
|||
|
LoadedLoadableTransports->Reset(cursor);
|
|||
|
while ((LoadableTransport
|
|||
|
= LoadedLoadableTransports->Next(cursor)) != 0)
|
|||
|
{
|
|||
|
|
|||
|
if (LoadableTransport->GetThreadsDoingShortWait() > 0)
|
|||
|
{
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: there are Ioc threads on short wait - don't tickle\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|||
|
#endif
|
|||
|
// there is a transport with threads on short wait
|
|||
|
// garbage collection will be performed soon even without
|
|||
|
// our help - we can bail out
|
|||
|
FirstTransport = NULL;
|
|||
|
fRetVal = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (FirstTransport == NULL)
|
|||
|
FirstTransport = LoadableTransport;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
else if (LrpcAddressList
|
|||
|
&& (((RTL_CRITICAL_SECTION *)(NtCurrentPeb()->LoaderLock))->OwningThread != NtCurrentTeb()->ClientId.UniqueThread))
|
|||
|
{
|
|||
|
|
|||
|
LrpcMutexRequest();
|
|||
|
|
|||
|
// else, if there are Lrpc Addresses, check whether they are doing short wait
|
|||
|
// and can gc for us
|
|||
|
CurrentAddress = LrpcAddressList;
|
|||
|
while (CurrentAddress)
|
|||
|
{
|
|||
|
// can this address gc for us?
|
|||
|
if (CurrentAddress->GetNumberOfThreadsDoingShortWait() > 0)
|
|||
|
{
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: there are threads on short wait (%d) on address %X - don't tickle\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), CurrentAddress,
|
|||
|
CurrentAddress->GetNumberOfThreadsDoingShortWait());
|
|||
|
#endif
|
|||
|
LrpcAddressToTickle = NULL;
|
|||
|
fRetVal = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((LrpcAddressToTickle == NULL) && (CurrentAddress->IsPreparedForLoopbackTickling()))
|
|||
|
{
|
|||
|
LrpcAddressToTickle = CurrentAddress;
|
|||
|
}
|
|||
|
CurrentAddress = CurrentAddress->GetNextAddress();
|
|||
|
}
|
|||
|
|
|||
|
// N.B. It is possible that Osf associations need cleanup, but only LRPC worker
|
|||
|
// threads are available, and moreover, no LRPC associations were created, which
|
|||
|
// means none of the Lrpc addresses is prepared for loopback tickling. If this is
|
|||
|
// the case, choose the first address, and make sure it is prepared for tickling
|
|||
|
if ((LrpcAddressToTickle == NULL) && (fRetVal == FALSE))
|
|||
|
{
|
|||
|
LrpcAddressToTickle = LrpcAddressList;
|
|||
|
|
|||
|
// prepare the selected address for tickling
|
|||
|
fRetVal = LrpcAddressToTickle->PrepareForLoopbackTicklingIfNecessary();
|
|||
|
if (fRetVal == FALSE)
|
|||
|
{
|
|||
|
// if this fails, zero out the address for tickling. This
|
|||
|
// will cause this function to return failure
|
|||
|
LrpcAddressToTickle = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LrpcMutexClear();
|
|||
|
}
|
|||
|
else if (fEnableIdleConnectionCleanup)
|
|||
|
{
|
|||
|
// if fEnableIdleConnectionCleanup is set, we have to create a thread if there is't one yet
|
|||
|
RpcStatus = CreateGarbageCollectionThread();
|
|||
|
if (RpcStatus == RPC_S_OK)
|
|||
|
{
|
|||
|
// the thread creation was successful - tell our caller we
|
|||
|
// will be doing garbage collection
|
|||
|
fRetVal = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// neither Ioc nor the LRPC thread pools have threads on short wait
|
|||
|
// We have to tickle somebody - we try the Ioc thread pool first
|
|||
|
if (FirstTransport)
|
|||
|
{
|
|||
|
// we couldn't find any transport with threads on short wait -
|
|||
|
// tickle a thread from the RPC transport in order to ensure timely
|
|||
|
// cleanup
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: No Ioc threads on short wait found - tickling one\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|||
|
#endif
|
|||
|
RpcStatus = TickleIocThread();
|
|||
|
if (RpcStatus == RPC_S_OK)
|
|||
|
fRetVal = TRUE;
|
|||
|
}
|
|||
|
else if (LrpcAddressToTickle)
|
|||
|
{
|
|||
|
// try to tickle the LRPC address
|
|||
|
fRetVal = LrpcAddressToTickle->LoopbackTickle();
|
|||
|
}
|
|||
|
|
|||
|
return fRetVal;
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS CreateGarbageCollectionThread (
|
|||
|
void
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Make a best effort to create a garbage collection thread. In this
|
|||
|
implementation we simply choose to create a completion port thread,
|
|||
|
as it has many uses.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
RPC_S_OK on success or RPC_S_* on error
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
TRANS_INFO *TransInfo;
|
|||
|
RPC_STATUS RpcStatus;
|
|||
|
|
|||
|
if (IsGarbageCollectionAvailable())
|
|||
|
return RPC_S_OK;
|
|||
|
|
|||
|
RpcStatus = LoadableTransportInfo(L"rpcrt4.dll",
|
|||
|
L"ncacn_ip_tcp",
|
|||
|
&TransInfo);
|
|||
|
|
|||
|
if (RpcStatus != RPC_S_OK)
|
|||
|
return RpcStatus;
|
|||
|
|
|||
|
RpcStatus = TransInfo->CreateThread();
|
|||
|
|
|||
|
return RpcStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
EnableIdleConnectionCleanup (
|
|||
|
void
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
We need to enable idle connection cleanup.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
RPC_S_OK - This value will always be returned.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
fEnableIdleConnectionCleanup = 1;
|
|||
|
|
|||
|
return(RPC_S_OK);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
EnableIdleLrpcSContextsCleanup (
|
|||
|
void
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
We need to enable idle LRPC SContexts cleanup.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
RPC_S_OK - This value will always be returned.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
// this is a global variable - prevent sloshing
|
|||
|
if (fEnableIdleLrpcSContextsCleanup == 0)
|
|||
|
fEnableIdleLrpcSContextsCleanup = 1;
|
|||
|
|
|||
|
return(RPC_S_OK);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
long GarbageCollectingInProgress = 0;
|
|||
|
DWORD LastCleanupTime = 0;
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
PerformGarbageCollection (
|
|||
|
void
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine should be called periodically so that each protocol
|
|||
|
module can perform garbage collection of resources as necessary.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD LocalTickCount;
|
|||
|
DWORD Diff;
|
|||
|
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: trying to garbage collect\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|||
|
#endif
|
|||
|
if (InterlockedIncrement(&GarbageCollectingInProgress) > 1)
|
|||
|
{
|
|||
|
//
|
|||
|
// Don't need more than one thread garbage collecting
|
|||
|
//
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: beaten to GC - returning\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|||
|
#endif
|
|||
|
InterlockedDecrement(&GarbageCollectingInProgress);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ((fEnableIdleConnectionCleanup || fEnableIdleLrpcSContextsCleanup) && PeriodicGarbageCollectItems)
|
|||
|
{
|
|||
|
LocalTickCount = GetTickCount();
|
|||
|
// make sure we don't cleanup too often - this is unnecessary
|
|||
|
if (LocalTickCount - LastCleanupTime > WaitToGarbageCollectDelay)
|
|||
|
{
|
|||
|
LastCleanupTime = LocalTickCount;
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Doing periodic garbage collection\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|||
|
#endif
|
|||
|
|
|||
|
// the periodic cleanup
|
|||
|
if (fEnableIdleLrpcSContextsCleanup)
|
|||
|
{
|
|||
|
GlobalRpcServer->EnumerateAndCallEachAddress(RPC_SERVER::actCleanupIdleSContext,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
|
|||
|
if (fEnableIdleConnectionCleanup)
|
|||
|
{
|
|||
|
OSF_CCONNECTION::OsfDeleteIdleConnections();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Too soon for periodic gc - skipping (%d, %d)\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), LocalTickCount,
|
|||
|
LastCleanupTime);
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (GarbageCollectionRequested)
|
|||
|
{
|
|||
|
LocalTickCount = GetTickCount();
|
|||
|
|
|||
|
Diff = LocalTickCount - NextOneTimeCleanup;
|
|||
|
if ((int)Diff >= 0)
|
|||
|
{
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Doing one time gc\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|||
|
#endif
|
|||
|
// assume the garbage collection will succeed. If it doesn't, the
|
|||
|
// functions called below have the responsibility to re-raise the flag
|
|||
|
// Note that there is a race condition where they may fail, but when
|
|||
|
// the flag was down, a thread went on a long wait. This again is ok,
|
|||
|
// because the current thread will figure out there is more garbage
|
|||
|
// collection to be done, because the flag is raised, and will do
|
|||
|
// a short wait. In worst case, the gc may be delayed because this
|
|||
|
// thread will pick a work item, and won't spawn another thread,
|
|||
|
// because there is already a thread in the IOCP, which is doing a
|
|||
|
// long wait. This may delay the gc from short to long wait. This is
|
|||
|
// Ok as it is in accordance with our design goals.
|
|||
|
GarbageCollectionRequested = 0;
|
|||
|
|
|||
|
OSF_CASSOCIATION::OsfDeleteLingeringAssociations();
|
|||
|
LRPC_CASSOCIATION::LrpcDeleteLingeringAssociations();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Too soon for one time gc - skipping (%d)\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), (int)Diff);
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
GarbageCollectingInProgress = 0;
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
CheckIfGCShouldBeTurnedOn (
|
|||
|
IN ULONG DestroyedAssociations,
|
|||
|
IN const ULONG NumberOfDestroyedAssociationsToSample,
|
|||
|
IN const long DestroyedAssociationBatchThreshold,
|
|||
|
IN OUT ULARGE_INTEGER *LastDestroyedAssociationsBatchTimestamp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Checks if it makes sense to turn on garbage collection
|
|||
|
for this process just for the pruposes of having
|
|||
|
association lingering available.
|
|||
|
|
|||
|
Parameters:
|
|||
|
DestroyedAssociations - the number of associations destroyed
|
|||
|
for this process so far (Osf and Lrpc may keep a separate
|
|||
|
count)
|
|||
|
NumberOfDestroyedAssociationsToReach - how many associations
|
|||
|
it takes to destroy for gc to be turned on
|
|||
|
DestroyedAssociationBatchThreshold - the time interval for which
|
|||
|
we have to destroy NumberOfDestroyedAssociationsToReach in
|
|||
|
order for gc to kick in
|
|||
|
LastDestroyedAssociationsBatchTimestamp - the timestamp when
|
|||
|
we made the last check
|
|||
|
|
|||
|
Return Value:
|
|||
|
non-zero - GC should be turned on
|
|||
|
FALSE - GC is either already on, or should not be turned on
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
FILETIME CurrentSystemTimeAsFileTime;
|
|||
|
ULARGE_INTEGER CurrentSystemTime;
|
|||
|
BOOL fEnableGarbageCollection;
|
|||
|
|
|||
|
if (IsGarbageCollectionAvailable()
|
|||
|
|| ((DestroyedAssociations % NumberOfDestroyedAssociationsToSample) != 0))
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
fEnableGarbageCollection = FALSE;
|
|||
|
|
|||
|
GetSystemTimeAsFileTime(&CurrentSystemTimeAsFileTime);
|
|||
|
CurrentSystemTime.LowPart = CurrentSystemTimeAsFileTime.dwLowDateTime;
|
|||
|
CurrentSystemTime.HighPart = CurrentSystemTimeAsFileTime.dwHighDateTime;
|
|||
|
if (LastDestroyedAssociationsBatchTimestamp->QuadPart != 0)
|
|||
|
{
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
ULARGE_INTEGER Temp;
|
|||
|
Temp.QuadPart = CurrentSystemTime.QuadPart - LastDestroyedAssociationsBatchTimestamp->QuadPart;
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LRPC time stamp diff: %X %X\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId(), Temp.HighPart, Temp.LowPart);
|
|||
|
#endif
|
|||
|
if (CurrentSystemTime.QuadPart - LastDestroyedAssociationsBatchTimestamp->QuadPart <=
|
|||
|
DestroyedAssociationBatchThreshold)
|
|||
|
{
|
|||
|
// we have destroyed plenty (NumberOfDestroyedAssociationsToSample) of
|
|||
|
// associations for less than DestroyedAssociationBatchThreshold
|
|||
|
// this process will probably benefit from garbage collection turned on as it
|
|||
|
// does a lot of binds. Return so to the caller
|
|||
|
fEnableGarbageCollection = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
#if defined (RPC_GC_AUDIT)
|
|||
|
else
|
|||
|
{
|
|||
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Time stamp is 0 - set it\n",
|
|||
|
GetCurrentProcessId(), GetCurrentProcessId());
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
LastDestroyedAssociationsBatchTimestamp->QuadPart = CurrentSystemTime.QuadPart;
|
|||
|
|
|||
|
return fEnableGarbageCollection;
|
|||
|
}
|