859 lines
20 KiB
C++
859 lines
20 KiB
C++
/*++
|
|
|
|
Copyright (c) 1996-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
oxid.cxx
|
|
|
|
Abstract:
|
|
|
|
Object resolver class implementations. COxid, COid, and other auxiliary
|
|
classes are implemented here.
|
|
|
|
Author:
|
|
|
|
Satish Thatte [SatishT] 03-27-96
|
|
|
|
--*/
|
|
|
|
#include<or.hxx>
|
|
|
|
|
|
//
|
|
// The CPID class is used for storing process IDs enumerated using the
|
|
// snapshot generated by CreateToolhelp32Snapshot for use in
|
|
// the function CheckForCrashedProcessesIfNecessary.
|
|
// The purpose is to avoid confusing the debug kernel by
|
|
// calling OpenProcess with a nonexistent process ID
|
|
//
|
|
|
|
#include <tlhelp32.h>
|
|
|
|
class CPID : public CTableElement
|
|
{
|
|
private:
|
|
|
|
// declare the members needed for a page static allocator
|
|
DECL_PAGE_ALLOCATOR
|
|
|
|
public :
|
|
|
|
CIdKey _pid;
|
|
|
|
CPID( DWORD pid ) : _pid(pid) {}
|
|
|
|
virtual DWORD // dummy method in this class
|
|
Hash()
|
|
{
|
|
return _pid.Hash();
|
|
}
|
|
|
|
virtual operator ISearchKey&()
|
|
{
|
|
return _pid;
|
|
}
|
|
|
|
DWORD GetPID()
|
|
{
|
|
ID id = _pid.Id();
|
|
DWORD* pdw = (DWORD*) &id;
|
|
return *(++pdw);
|
|
}
|
|
};
|
|
|
|
|
|
DEFINE_TABLE(CPID)
|
|
|
|
|
|
|
|
//
|
|
// COid methods and static members
|
|
//
|
|
|
|
// define the static members for page-based allocation
|
|
DEFINE_PAGE_ALLOCATOR(COid)
|
|
|
|
void
|
|
COid::Rundown()
|
|
{
|
|
if (!_pOxid->IsLocal())
|
|
{
|
|
// We don't want to remove it from the gpOidTable at this point
|
|
// because some other client may come along and want to use
|
|
// this before it gets dropped from the ping set if the ping fails
|
|
_pOxid->_pMid->DropClientOid(this);
|
|
}
|
|
else
|
|
{
|
|
COid *pRemoved = gpOidTable->Remove(*this);
|
|
ASSERT(pRemoved==this || pRemoved==NULL);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
COid::OkToRundown()
|
|
{
|
|
DWORD dwRefs = References();
|
|
ASSERT(dwRefs >= 2);
|
|
|
|
BOOL fLocal = _pOxid->IsLocal();
|
|
|
|
DWORD dwBaseRefs = fLocal ? 2 : 3; // extra pingset ref
|
|
|
|
if (dwRefs > dwBaseRefs)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// the resolver on the server machine will take care of the delay
|
|
// in rundown if this is a non-local OID
|
|
|
|
// Check if the time since creation or last release is less
|
|
// than timeout
|
|
// Warning: removing the check will fail the middle-man cases
|
|
// because the middle-man's access time will not be counted.
|
|
//
|
|
if (fLocal && ((CTime() - *this) < BaseTimeoutInterval))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// COxidInfo methods
|
|
//
|
|
|
|
|
|
ORSTATUS // BUGBUG: perhaps this should have OXID_INFO as the parameter type
|
|
COxidInfo::Assign(
|
|
const OXID_INFO& Info
|
|
)
|
|
/*++
|
|
|
|
Routine Desciption
|
|
|
|
Makes a copy of the incoming info, including the DUALSTRINGARRAY.
|
|
|
|
Arguments:
|
|
|
|
Info - COxidInfo object to be cpied
|
|
|
|
Return Values:
|
|
|
|
OR_OK
|
|
OR_NOMEM
|
|
|
|
--*/
|
|
{
|
|
_oxidInfo = Info; // all except bindings taken care of
|
|
return _dsaBindings.Assign(Info.psa,TRUE); // already compressed
|
|
}
|
|
|
|
|
|
//
|
|
// COxid methods and static members.
|
|
//
|
|
|
|
// define the static members for page-based allocation
|
|
DEFINE_PAGE_ALLOCATOR(COxid)
|
|
|
|
|
|
COxid::COxid(
|
|
OXID Oxid, // constructor for remote OXIDs
|
|
CMid *pMid,
|
|
USHORT wProtseq,
|
|
OXID_INFO &OxidInfo
|
|
) :
|
|
_Key(Oxid, pMid->GetMID()),
|
|
_pProcess(gpPingProcess),
|
|
_protseq(wProtseq),
|
|
_fApartment(FALSE),
|
|
_fRunning(TRUE),
|
|
_pMid(pMid),
|
|
_fLocal(FALSE),
|
|
_info(OxidInfo)
|
|
{
|
|
_pMid->Reference();
|
|
_optional._remote._dwTimeStamp = 0;
|
|
}
|
|
|
|
|
|
COxid::COxid( // constructor for local OXIDs
|
|
CProcess *pProcess,
|
|
OXID_INFO &OxidInfo,
|
|
BOOL fApartment
|
|
) :
|
|
_Key(AllocateId(), gLocalMID),
|
|
_pProcess(pProcess),
|
|
_pMid(gpLocalMid),
|
|
_protseq(0),
|
|
_fApartment(fApartment),
|
|
_fRunning(TRUE),
|
|
_fLocal(TRUE),
|
|
_info(OxidInfo)
|
|
{
|
|
_optional._local._fRundownThreadStarted = FALSE;
|
|
_optional._local._hRundownThread = NULL;
|
|
_optional._local._pfRundownThreadKeepRunning = NULL;
|
|
_pProcess->Reference();
|
|
}
|
|
|
|
|
|
COxid::~COxid()
|
|
{
|
|
// this works even if executed by nonowner thread
|
|
StopRundownThreadIfNecessary();
|
|
StopRunning();
|
|
|
|
DUALSTRINGARRAY *pdsaBindings = _info._dsaBindings;
|
|
OrMemFree(pdsaBindings);
|
|
|
|
if (!IsLocal()) // Don't release the local CMid!
|
|
{
|
|
_pMid->Release();
|
|
}
|
|
else // Don't release the PingProcess!
|
|
{
|
|
_pProcess->Release();
|
|
}
|
|
}
|
|
|
|
|
|
ORSTATUS
|
|
COxid::OwnOid(COid *pOid)
|
|
{
|
|
if (!IsLocal())
|
|
{
|
|
_optional._remote._dwTimeStamp = NULL;
|
|
}
|
|
|
|
ORSTATUS status = _MyOids.Add(pOid); // acquires a reference
|
|
|
|
if (status == OR_I_DUPLICATE)
|
|
{
|
|
status = OR_OK;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
COxid::HasExpired()
|
|
{
|
|
BOOL result = FALSE;
|
|
|
|
ASSERT(!IsLocal());
|
|
|
|
if (_MyOids.IsEmpty())
|
|
{
|
|
// if we are not running, we are gone
|
|
if (!_fRunning)
|
|
{
|
|
result = TRUE;
|
|
}
|
|
else if (_optional._remote._dwTimeStamp == 0)
|
|
{
|
|
// Maybe we haven't acquired our first Oid yet
|
|
// Or someone has been asking for our bindings
|
|
result = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// have we been idle long enough?
|
|
result = (CTime() - CTime(_optional._remote._dwTimeStamp))
|
|
>= (BaseTimeoutInterval);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
COid *
|
|
COxid::DisownOid(COid *pOid)
|
|
{
|
|
// Rundown is idempotent
|
|
pOid->Rundown();
|
|
|
|
COid *pMyOid = _MyOids.Remove(*pOid);// releases our reference
|
|
|
|
// The Oid may have been run down and removed already
|
|
ASSERT(pMyOid == pOid || pMyOid == NULL);
|
|
|
|
// If this is a non-local Oxid and it has no Oids registered
|
|
// then it becomes a candidate for being eliminated
|
|
if (!IsLocal() && _MyOids.IsEmpty())
|
|
{
|
|
_optional._remote._dwTimeStamp = GetTickCount();
|
|
}
|
|
|
|
return pMyOid;
|
|
}
|
|
|
|
|
|
void
|
|
COxid::StopRunning()
|
|
{
|
|
ReleaseAllOids();
|
|
_fRunning = FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
COxid::ReleaseAllOids()
|
|
{
|
|
if (_MyOids.Size())
|
|
{
|
|
COidTableIterator Oids(_MyOids);
|
|
COid *pOid;
|
|
|
|
while(pOid = Oids.Next())
|
|
{
|
|
DisownOid(pOid);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ORSTATUS
|
|
COxid::GetInfo(
|
|
OUT OXID_INFO *pInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the OXID_INFO structure for this oxid for local.
|
|
|
|
Arguments:
|
|
|
|
pInfo - Will contain the standard info, a single _expanded_
|
|
string binding and complete security bindings.
|
|
|
|
Return Value:
|
|
|
|
OR_NOMEM - Unable to allocate a parameter.
|
|
|
|
OR_OK - Normally.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT protseq;
|
|
PWSTR pwstrT;
|
|
CDSA dsaBindings;
|
|
|
|
if (!IsRunning())
|
|
{
|
|
return OR_NOSERVER;
|
|
}
|
|
|
|
if (_fLocal)
|
|
{
|
|
dsaBindings.Assign(_pProcess->GetLocalBindings());
|
|
|
|
protseq = ID_WMSG;
|
|
}
|
|
else
|
|
{
|
|
protseq = _protseq; // use the one we set when this was created
|
|
DUALSTRINGARRAY *pdsa = _info._dsaBindings; // auto conversion
|
|
dsaBindings.Assign(pdsa); // noncopying assignment
|
|
}
|
|
|
|
if (_pMid->IsLocal())
|
|
{
|
|
pwstrT = FindMatchingProtseq(protseq, dsaBindings->aStringArray);
|
|
}
|
|
else
|
|
{
|
|
protseq = _pMid->ProtseqOfServer();
|
|
PWSTR pwstrAddress = _pMid->AddressOfServer();
|
|
|
|
if (protseq==0) // there is no working binding
|
|
{
|
|
return OR_NOSERVER;
|
|
}
|
|
|
|
if (pwstrAddress==NULL) // we failed to allocate the string
|
|
{
|
|
return OR_NOMEM;
|
|
}
|
|
|
|
pwstrT = FindMatchingProtseqAndAddr(
|
|
protseq,
|
|
pwstrAddress,
|
|
dsaBindings->aStringArray
|
|
);
|
|
|
|
RpcStringFree(&pwstrAddress);
|
|
}
|
|
|
|
ASSERT(pwstrT != NULL && "OR: Didn't find a matching binding for oxid");
|
|
|
|
// the memcpy gets everything except the bindings
|
|
memcpy(pInfo,&_info._oxidInfo,sizeof(_info._oxidInfo));
|
|
|
|
if (pwstrT)
|
|
{
|
|
pInfo->psa =
|
|
GetStringBinding(
|
|
pwstrT,
|
|
dsaBindings->aStringArray + dsaBindings->wSecurityOffset
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// BUGBUG - ronans - can be due to null pwstrT - in this
|
|
// case OR_NOMEM is not the correct return value.
|
|
|
|
pInfo->psa = NULL;
|
|
return OR_NOSERVER;
|
|
}
|
|
|
|
if (0 == pInfo->psa)
|
|
{
|
|
return OR_NOMEM;
|
|
}
|
|
|
|
return(OR_OK);
|
|
}
|
|
|
|
|
|
|
|
ORSTATUS
|
|
COxid::GetRemoteInfo(
|
|
IN USHORT cClientProtseqs,
|
|
IN USHORT *aClientProtseqs,
|
|
IN USHORT cInstalledProtseqs,
|
|
IN USHORT *aInstalledProtseqs,
|
|
OUT OXID_INFO *pInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the OXID_INFO structure for this oxid for remote clients.
|
|
|
|
Arguments:
|
|
|
|
pInfo - Will contain the standard info, a single _expanded_
|
|
string binding and complete security bindings.
|
|
|
|
Return Value:
|
|
|
|
OR_NOMEM - Unable to allocate a parameter.
|
|
|
|
OR_OK - Normally.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR pwstrT;
|
|
ORSTATUS status = OR_OK;
|
|
|
|
if (!IsRunning())
|
|
{
|
|
return OR_NOSERVER;
|
|
}
|
|
|
|
ASSERT(_fLocal); // Do not resolve remote servers for remote clients!
|
|
|
|
DUALSTRINGARRAY * pdsaBindings = _pProcess->GetLocalBindings();
|
|
|
|
pwstrT = FindMatchingProtseq(
|
|
cClientProtseqs,
|
|
aClientProtseqs,
|
|
pdsaBindings->aStringArray
|
|
);
|
|
|
|
if ( pwstrT == NULL ) // try lazy use of protseq(s)
|
|
{
|
|
status = _pProcess->UseProtseqIfNeeded(
|
|
cClientProtseqs,
|
|
aClientProtseqs,
|
|
cInstalledProtseqs,
|
|
aInstalledProtseqs,
|
|
_info._oxidInfo.dwTid // so we know where to call over WMSG
|
|
);
|
|
|
|
pdsaBindings = _pProcess->GetLocalBindings();
|
|
}
|
|
|
|
// the memcpy gets everything except the bindings
|
|
memcpy(pInfo,&_info._oxidInfo,sizeof(_info._oxidInfo));
|
|
|
|
pInfo->psa = GetMatchingDSA(
|
|
cClientProtseqs,
|
|
aClientProtseqs,
|
|
pdsaBindings
|
|
);
|
|
|
|
if (NULL == pInfo->psa)
|
|
{
|
|
return OR_NOMEM;
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
|
|
ORSTATUS
|
|
COxid::StartRundownThreadIfNecessary()
|
|
{
|
|
DWORD dwThrdId;
|
|
|
|
if (_fApartment || !IsLocal()) // rundown timer is attached to window
|
|
// or rundown handled by ping server
|
|
{
|
|
return OR_OK;
|
|
}
|
|
|
|
if (_optional._local._fRundownThreadStarted)
|
|
{
|
|
ASSERT(_optional._local._hRundownThread);
|
|
ASSERT(*_optional._local._pfRundownThreadKeepRunning == TRUE);
|
|
return OR_OK;
|
|
}
|
|
|
|
SRundownThreadInfo *pInfo = (SRundownThreadInfo*)
|
|
PrivMemAlloc(sizeof(SRundownThreadInfo));
|
|
|
|
pInfo->pSelf = this;
|
|
pInfo->fKeepRunning = TRUE;
|
|
_optional._local._pfRundownThreadKeepRunning = &(pInfo->fKeepRunning);
|
|
|
|
_optional._local._hRundownThread =
|
|
CreateThread(
|
|
NULL, 0,
|
|
RundownThread,
|
|
pInfo, 0, &dwThrdId);
|
|
|
|
if (_optional._local._hRundownThread)
|
|
{
|
|
_optional._local._fRundownThreadStarted = TRUE;
|
|
return OR_OK;
|
|
}
|
|
else
|
|
{
|
|
return GetLastError();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
ORSTATUS
|
|
COxid::StopRundownThreadIfNecessary()
|
|
{
|
|
if (!IsLocal() || _pProcess->IsCurrentProcess() != TRUE)
|
|
{
|
|
return OR_OK;
|
|
}
|
|
|
|
if (_optional._local._fRundownThreadStarted)
|
|
{
|
|
ASSERT(_optional._local._hRundownThread);
|
|
|
|
CloseHandle(_optional._local._hRundownThread);
|
|
_optional._local._hRundownThread = NULL;
|
|
_optional._local._fRundownThreadStarted = FALSE;
|
|
// signal the rundown thread to terminate itself
|
|
*_optional._local._pfRundownThreadKeepRunning = FALSE;
|
|
return OR_OK;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!_optional._local._hRundownThread);
|
|
return OR_OK;
|
|
}
|
|
}
|
|
|
|
|
|
ORSTATUS
|
|
COxid::StopTimerIfNecessary() // must be called by owner thread
|
|
{
|
|
ORSTATUS status = OR_OK;
|
|
|
|
if (_fApartment)
|
|
{
|
|
// find the HWND for this thread
|
|
|
|
COleTls tls;
|
|
OXIDEntry *pOXIDEntry = (OXIDEntry *)tls->pOXIDEntry;
|
|
HWND hWindow = (HWND) pOXIDEntry->hServerSTA;
|
|
|
|
if (!KillTimer(hWindow,pOXIDEntry->uiTimer))
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
CheckForCrashedProcessesIfNecessary()
|
|
{
|
|
CTime CurrentTime;
|
|
|
|
// don't check too often -- note that the subtraction returns a result
|
|
// denominated in seconds, not in ticks
|
|
|
|
DWORD dwTimeSinceLastCheckInSeconds =
|
|
CurrentTime - CTime(*gpdwLastCrashedProcessCheckTime);
|
|
|
|
if (dwTimeSinceLastCheckInSeconds < BasePingInterval)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// timestamp the check -- this time in ticks
|
|
*gpdwLastCrashedProcessCheckTime = CurrentTime;
|
|
|
|
#if DBG
|
|
{
|
|
OutputDebugString("Checked For Crashed Processes at:");
|
|
CHAR buffer[20];
|
|
wsprintfA(buffer, " %d\n", (*gpdwLastCrashedProcessCheckTime)/1000);
|
|
OutputDebugString(buffer);
|
|
OutputDebugString("Time Since Last Check:");
|
|
wsprintfA(buffer, " %d\n", dwTimeSinceLastCheckInSeconds);
|
|
OutputDebugString(buffer);
|
|
}
|
|
#endif
|
|
|
|
// now get together the table of currebntly running processes
|
|
|
|
CPIDTable PidTable;
|
|
|
|
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
|
|
|
|
if ((LONG)hSnap == -1)
|
|
{
|
|
ComDebOut((DEB_OXID,"CreateToolhelp32Snapshot failed with error %x",
|
|
GetLastError()));
|
|
|
|
return;
|
|
}
|
|
|
|
PROCESSENTRY32 processEntry;
|
|
processEntry.dwSize = sizeof(processEntry);
|
|
|
|
BOOL fContinue = FALSE;
|
|
|
|
for (
|
|
fContinue = Process32First(hSnap,&processEntry);
|
|
fContinue;
|
|
fContinue = Process32Next(hSnap,&processEntry)
|
|
)
|
|
{
|
|
// These objects are ref counted and will be destroyed
|
|
// when the table is destroyed at the end of the scope
|
|
CPID *pPid = new CPID(processEntry.th32ProcessID);
|
|
|
|
if (pPid==NULL)
|
|
{
|
|
CloseHandle(hSnap);
|
|
return; // not much else we can do here
|
|
}
|
|
|
|
PidTable.Add(pPid);
|
|
|
|
#if DBG
|
|
OutputDebugString("Snapshot contains ");
|
|
CHAR buffer[20];
|
|
wsprintfA(buffer, "%x ", processEntry.th32ProcessID);
|
|
OutputDebugString(buffer);
|
|
OutputDebugString(processEntry.szExeFile);
|
|
OutputDebugString("\n");
|
|
#endif
|
|
}
|
|
|
|
CloseHandle(hSnap);
|
|
|
|
// now iterate through our table of registered processes to
|
|
// see if any of them are gone
|
|
|
|
CProcessTableIterator procIter(*gpProcessTable);
|
|
|
|
CProcess *pNextProcess;
|
|
|
|
while (pNextProcess = procIter.Next())
|
|
{
|
|
CPID Pid(pNextProcess->GetProcessID());
|
|
|
|
if (PidTable.Lookup(Pid) == NULL) // not running
|
|
{
|
|
ComDebOut((DEB_OXID,"Process PID = %x has crashed\n",
|
|
pNextProcess->GetProcessID()));
|
|
|
|
// run the process down before removing it -- removal may cause
|
|
// its ref count to drop to zero leading to premature deletion
|
|
pNextProcess->Rundown();
|
|
gpProcessTable->Remove(*pNextProcess);
|
|
}
|
|
else
|
|
{
|
|
ComDebOut((DEB_OXID,"Process PID = %d is running\n",
|
|
pNextProcess->_processID));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define RUNDOWN_BATCH_SIZE 100
|
|
|
|
// This helper assumes that the gpMutex is held,
|
|
// and releases it for the duration of the function call
|
|
// The algorithm: Look through all candidate Oids to be
|
|
// run down and try to run them down -- put the ones that
|
|
// the Rundown interface OKs on the aRundownOids array.
|
|
inline void
|
|
RundownHelper(
|
|
IRundown *pRemUnk,
|
|
COid * aRundownOidCandidates[],
|
|
DWORD cRundownOidCandidates,
|
|
COid * aRundownOids[],
|
|
DWORD &cRundownOids
|
|
)
|
|
{
|
|
unsigned char afOkToRundown[RUNDOWN_BATCH_SIZE];
|
|
OID aOIDs[RUNDOWN_BATCH_SIZE];
|
|
USHORT i;
|
|
|
|
for (i =0; i < cRundownOidCandidates; i++)
|
|
{
|
|
aOIDs[i] = aRundownOidCandidates[i]->GetOID();
|
|
}
|
|
|
|
{
|
|
// Release shared memory so we can try real rundown without deadlock
|
|
CTempReleaseSharedMemory temp;
|
|
|
|
HRESULT hr = pRemUnk->RundownOid(
|
|
cRundownOidCandidates,
|
|
aOIDs,
|
|
afOkToRundown
|
|
);
|
|
|
|
ASSERT(hr == S_OK);
|
|
}
|
|
|
|
cRundownOids = 0;
|
|
|
|
for (i = 0; i < cRundownOidCandidates; i++)
|
|
{
|
|
// the optimizer will take the hr == S_OK out of the loop, right?
|
|
if (afOkToRundown[i])
|
|
{
|
|
aRundownOids[cRundownOids++] = aRundownOidCandidates[i];
|
|
}
|
|
else
|
|
{
|
|
// We don't care what happens to this any more
|
|
// even though the shared mutex is not held right now
|
|
aRundownOidCandidates[i]->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
COxid::RundownOidsIfNecessary(
|
|
IRundown *pRemUnk
|
|
)
|
|
{
|
|
::CheckForCrashedProcessesIfNecessary();
|
|
|
|
if (_MyOids.Size() == 0) return;
|
|
|
|
COidTableIterator Oids(_MyOids);
|
|
COid *pOid;
|
|
|
|
// Hold a reference to self temorarily so we are not destroyed
|
|
// during this call -- in spite of giving up the shared mutex
|
|
|
|
CTempHoldRef tempRef(this);
|
|
|
|
COid* aRundownOidCandidates[RUNDOWN_BATCH_SIZE];
|
|
DWORD cRundownOidCandidates;
|
|
BOOL fCheckedAllOids = FALSE;
|
|
|
|
// Find Oids that look ready to run down
|
|
do
|
|
{
|
|
cRundownOidCandidates = 0;
|
|
|
|
while (cRundownOidCandidates < RUNDOWN_BATCH_SIZE)
|
|
{
|
|
if ((pOid = Oids.Next()) == NULL) // We've been through all OIDs
|
|
{
|
|
fCheckedAllOids = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (pOid->OkToRundown())
|
|
{
|
|
aRundownOidCandidates[cRundownOidCandidates++] = pOid;
|
|
|
|
// We don't want to lose these, so hold a reference
|
|
pOid->Reference();
|
|
}
|
|
}
|
|
|
|
if (cRundownOidCandidates == 0)
|
|
{
|
|
return; // No more work, just return
|
|
}
|
|
|
|
COid* bufRundownOids[RUNDOWN_BATCH_SIZE];
|
|
COid** aRundownOids;
|
|
DWORD cRundownOids;
|
|
|
|
if (IsLocal())
|
|
{
|
|
aRundownOids = bufRundownOids;
|
|
|
|
RundownHelper(
|
|
pRemUnk,
|
|
aRundownOidCandidates,
|
|
cRundownOidCandidates,
|
|
aRundownOids,
|
|
cRundownOids
|
|
);
|
|
}
|
|
else
|
|
{
|
|
aRundownOids = aRundownOidCandidates;
|
|
cRundownOids = cRundownOidCandidates;
|
|
}
|
|
|
|
|
|
// If while the lock was released, we stopped running, just return
|
|
// The refs we are holding temporarily will be released by destructors
|
|
if (!_fRunning)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (USHORT i = 0; i < cRundownOids; i++)
|
|
{
|
|
pOid = aRundownOids[i];
|
|
DisownOid(pOid);
|
|
pOid->Release(); // release our private reference
|
|
}
|
|
|
|
} while (! fCheckedAllOids);
|
|
}
|