492 lines
12 KiB
C++
492 lines
12 KiB
C++
/*++
|
||
|
||
Copyright (c) 1995-1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Worker.cxx
|
||
|
||
Abstract:
|
||
|
||
Backgroup activies releated to running down and cleaning up OR and pinging
|
||
remote OR's are handled here.
|
||
|
||
Author:
|
||
|
||
Mario Goertzel [MarioGo]
|
||
|
||
Revision History:
|
||
|
||
MarioGo 03-02-95 Bits 'n pieces
|
||
MarioGo 01-18-96 Locally unique IDs
|
||
|
||
--*/
|
||
|
||
#include <or.hxx>
|
||
|
||
static CInterlockedInteger cTaskThreads(0);
|
||
|
||
#if DBG_DETAIL
|
||
extern "C" void printf(char *, ...);
|
||
#endif
|
||
|
||
#define TASKTHREAD_STACK_PAGES 3
|
||
|
||
#define SECS_BETWEEN_FLUSHES 30
|
||
|
||
typedef enum { PWR_SUSPENDED, PWR_RUNNING } PWR_STATE;
|
||
|
||
PWR_STATE gPowerState = PWR_RUNNING;
|
||
|
||
void OLESCMBindingHandleFlush();
|
||
|
||
#if _MSC_FULL_VER >= 13008827
|
||
#pragma warning(push)
|
||
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
|
||
#endif
|
||
|
||
DWORD WINAPI
|
||
ObjectExporterWorkerThread(LPVOID /* ignored */)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Main background thread for the object resolver. This thread
|
||
manages a number of background tasks:
|
||
Cleaning up the client oxid cache.
|
||
Running down un-pinged sets.
|
||
Starting task threads to rundown server OIDs and ping sets.
|
||
|
||
This thread must not block for a long time. Task threads
|
||
should be used for possibly blocking operations like remote
|
||
pinging and rundown of OIDs.
|
||
|
||
Arguments:
|
||
|
||
Ignored
|
||
|
||
Return Value:
|
||
|
||
None - should never return.
|
||
|
||
--*/
|
||
{
|
||
ORSTATUS status;
|
||
CTime now(0);
|
||
CTime timeout(0);
|
||
CTime delay(0);
|
||
CTime start(0);
|
||
CTime lastflush(0);
|
||
BOOL fCreateThread;
|
||
SYSTEM_INFO si;
|
||
|
||
// Retrieve system info so we can start task threads
|
||
// with an appropriate initial stack size
|
||
GetSystemInfo(&si);
|
||
|
||
lastflush.SetNow();
|
||
|
||
for(;;)
|
||
{
|
||
now.SetNow();
|
||
delay = now;
|
||
delay += BasePingInterval;
|
||
|
||
// Cleanup old sets.
|
||
//
|
||
// Sets are usually cleaned up during processing of pings. (As one set is
|
||
// pinged, the next set will be checked to see if it needs to be rundown.)
|
||
//
|
||
// If there's exactly one set in the table, then it won't be run down except
|
||
// by this thread.
|
||
//
|
||
// NOTE: Similar code in _SimplePing().
|
||
|
||
gpServerLock->LockShared();
|
||
|
||
ID setid = gpServerSetTable->CheckForRundowns();
|
||
if (setid)
|
||
{
|
||
gpServerLock->ConvertToExclusive();
|
||
|
||
if (gpServerSetTable->RundownSetIfNeeded(setid))
|
||
{
|
||
delay.SetNow();
|
||
}
|
||
|
||
gpServerLock->UnlockExclusive();
|
||
}
|
||
else
|
||
{
|
||
gpServerLock->UnlockShared();
|
||
}
|
||
|
||
//
|
||
// Cleanup old Client OXIDs
|
||
//
|
||
if (gpClientOxidPList->PeekMin(timeout))
|
||
{
|
||
if (timeout < now)
|
||
{
|
||
CClientOxid *pOxid;
|
||
CListElement *ple;
|
||
|
||
gpClientLock->LockExclusive();
|
||
|
||
while (ple = gpClientOxidPList->MaybeRemoveMin(now))
|
||
{
|
||
pOxid = CClientOxid::ContainingRecord(ple);
|
||
delete pOxid;
|
||
}
|
||
gpClientLock->UnlockExclusive();
|
||
|
||
delay.SetNow();
|
||
}
|
||
else
|
||
{
|
||
if (delay > timeout)
|
||
{
|
||
delay = timeout;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure pinging and rundowns are proceding
|
||
//
|
||
fCreateThread = FALSE;
|
||
|
||
// We want to create an extra task thread if we've fallen
|
||
// behind on pings. As more threads are created the
|
||
// requirements for "behind" become harder to meet.
|
||
if (gpClientSetPList->PeekMin(timeout))
|
||
{
|
||
start = now;
|
||
start += (BasePingInterval + 10*cTaskThreads);
|
||
|
||
if (cTaskThreads == 0 || start < timeout)
|
||
{
|
||
fCreateThread = TRUE;
|
||
}
|
||
else
|
||
{
|
||
if (delay > start)
|
||
{
|
||
delay = start;
|
||
}
|
||
}
|
||
}
|
||
|
||
// We want to create an extra task thread if we've fallen
|
||
// behind in running down local objects. As more threads are
|
||
// created the requirements for "behind" become harder to meet.
|
||
if (gpServerOidPList->PeekMin(timeout))
|
||
{
|
||
start = now;
|
||
start -= 10*cTaskThreads;
|
||
if (timeout < start)
|
||
{
|
||
fCreateThread = TRUE;
|
||
}
|
||
else
|
||
{
|
||
start = timeout;
|
||
start += 2*10*cTaskThreads;
|
||
if (delay > start)
|
||
{
|
||
delay = start;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (fCreateThread)
|
||
{
|
||
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
DPFLTR_INFO_LEVEL,
|
||
"OR: Creating additional task thread, we're behind..\n"));
|
||
|
||
cTaskThreads++;
|
||
|
||
DWORD tid;
|
||
HANDLE hThread = CreateThread(0,
|
||
(si.dwPageSize * TASKTHREAD_STACK_PAGES),
|
||
ObjectExporterTaskThread,
|
||
0,
|
||
0,
|
||
&tid
|
||
);
|
||
if (0 != hThread)
|
||
{
|
||
CloseHandle(hThread);
|
||
}
|
||
else
|
||
{
|
||
cTaskThreads--;
|
||
}
|
||
}
|
||
|
||
#if DBG_DETAIL
|
||
printf("================================================================\n"
|
||
"ServerOxids: %d, ServerOids: %d, ServerSets: %d\n"
|
||
"ClientOxids: %d, ClientOids: %d, ClientSets: %d\n"
|
||
"Mids: %d, Processes %d, worker threads: %d\n"
|
||
"Sleeping for %d seconds...\n",
|
||
gpServerOxidTable->Size(),
|
||
gpServerOidTable->Size(),
|
||
gpServerSetTable->Size(),
|
||
gpClientOxidTable->Size(),
|
||
gpClientOidTable->Size(),
|
||
gpClientSetTable->Size(),
|
||
gpMidTable->Size(),
|
||
gpProcessList->Size(),
|
||
cTaskThreads,
|
||
delay - now + 1
|
||
);
|
||
#endif
|
||
|
||
delay += 1;
|
||
|
||
if (delay - lastflush > SECS_BETWEEN_FLUSHES)
|
||
{
|
||
// Give olescm a chance to do other work
|
||
OLESCMBindingHandleFlush();
|
||
|
||
lastflush.SetNow();
|
||
}
|
||
|
||
delay.Sleep();
|
||
}
|
||
|
||
return(0);
|
||
}
|
||
|
||
#if _MSC_FULL_VER >= 13008827
|
||
#pragma warning(pop)
|
||
#endif
|
||
|
||
|
||
void
|
||
NotifyCOMOnSuspend()
|
||
/*++
|
||
|
||
Routine Desciption
|
||
|
||
Resets state and takes appropriate action when the machine
|
||
goes into a standby (or, hibernate) state.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
void
|
||
|
||
--*/
|
||
{
|
||
ASSERT(gPowerState == PWR_RUNNING);
|
||
|
||
// Just set the flag. This will block any further
|
||
// rundowns from happening from now until we get a
|
||
// resume notification.
|
||
gPowerState = PWR_SUSPENDED;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
void
|
||
NotifyCOMOnResume()
|
||
/*++
|
||
|
||
Routine Desciption
|
||
|
||
Resets state and takes appropriate action when the machine
|
||
starts running again after being in the standby/hibernate
|
||
state.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
void
|
||
|
||
--*/
|
||
{
|
||
// We cannot assert here that we're suspended, since the machine
|
||
// can suspend in critical situations w/o notifying us
|
||
|
||
//
|
||
// First reset all of the server pingset timers.
|
||
//
|
||
gpServerLock->LockExclusive();
|
||
|
||
// After a resume, all ping sets get reset to zero
|
||
gpServerSetTable->PingAllSets();
|
||
|
||
gpServerLock->UnlockExclusive();
|
||
|
||
//
|
||
// Second and last, reset the power state so that future
|
||
// rundowns will not be blocked.
|
||
//
|
||
gPowerState = PWR_RUNNING;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
DWORD WINAPI
|
||
ObjectExporterTaskThread(LPVOID /* ignored */)
|
||
{
|
||
CTime now(0);
|
||
CTime delay(0);
|
||
CTime timeout(0);
|
||
ORSTATUS status;
|
||
CListElement *ple;
|
||
CClientSet *pSet;
|
||
CServerOid *pOid;
|
||
|
||
enum {
|
||
Idle, // No work to do at all.
|
||
Waiting, // No work to do yet.
|
||
Busy // Did work this iteration.
|
||
} eState;
|
||
|
||
for(;;)
|
||
{
|
||
now.SetNow();
|
||
delay = now;
|
||
delay += BasePingInterval;
|
||
eState = Idle;
|
||
|
||
// Ping remote sets.
|
||
|
||
if (gpClientSetPList->PeekMin(timeout))
|
||
{
|
||
eState = Waiting;
|
||
|
||
if (now >= timeout)
|
||
{
|
||
eState = Busy;
|
||
|
||
ple = gpClientSetPList->MaybeRemoveMin(now);
|
||
|
||
if (ple)
|
||
{
|
||
// Actually ping the set
|
||
|
||
pSet = CClientSet::ContainingRecord(ple);
|
||
|
||
pSet->PingServer();
|
||
|
||
// Set maybe invalid now.
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Not ready to ping yet.
|
||
delay = timeout;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Process server OID rundowns. Only do this
|
||
// if there's something to rundown, and the machine
|
||
// is not in the suspended state.
|
||
//
|
||
if (gPowerState == PWR_RUNNING &&
|
||
gpServerOidPList->PeekMin(timeout))
|
||
{
|
||
if (eState == Idle)
|
||
eState = Waiting;
|
||
|
||
if (now >= timeout)
|
||
{
|
||
eState = Busy;
|
||
|
||
gpServerLock->LockExclusive();
|
||
|
||
CServerOid* apOid[MAX_OID_RUNDOWNS_PER_CALL];
|
||
ULONG cOids;
|
||
|
||
ple = gpServerOidPList->MaybeRemoveMin(now);
|
||
pOid = CServerOid::ContainingRecord(ple);
|
||
|
||
if (ple && pOid->IsRunningDown() == FALSE)
|
||
{
|
||
apOid[0] = pOid;
|
||
cOids = 1;
|
||
ASSERT(pOid->IsFreed() == FALSE);
|
||
ASSERT(pOid->IsPinned() == FALSE);
|
||
pOid->SetRundown(TRUE);
|
||
|
||
while(cOids < MAX_OID_RUNDOWNS_PER_CALL && pOid)
|
||
{
|
||
pOid = gpServerOidPList->MaybeRemoveMatchingOxid(now, apOid[0]);
|
||
|
||
if (pOid && pOid->IsRunningDown() == FALSE)
|
||
{
|
||
ASSERT(pOid->IsFreed() == FALSE);
|
||
ASSERT(pOid->IsPinned() == FALSE);
|
||
pOid->SetRundown(TRUE);
|
||
apOid[cOids] = pOid;
|
||
cOids++;
|
||
}
|
||
}
|
||
|
||
ASSERT(cOids < MAX_OID_RUNDOWNS_PER_CALL+1 && cOids >= 1);
|
||
ASSERT(apOid[0]->GetOxid() == apOid[cOids - 1]->GetOxid());
|
||
|
||
// Note: This call will unlock the server lock. While
|
||
// this happens the oids maybe added, deleted, added
|
||
// and deleted, added and rundown from one or more sets.
|
||
|
||
CServerOxid* pOxid = apOid[0]->GetOxid();
|
||
pOxid->RundownOids(cOids, apOid);
|
||
|
||
// Assert RundownOids released the lock
|
||
ASSERT(!gpServerLock->HeldExclusive());
|
||
}
|
||
else
|
||
{
|
||
gpServerLock->UnlockExclusive();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Not ready to rundown yet.
|
||
if (delay > timeout)
|
||
{
|
||
delay = timeout;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Decide if this task thread should exit or sleep and loop.
|
||
ASSERT(eState == Idle || eState == Busy || eState == Waiting);
|
||
|
||
if ((eState == Idle) || (eState == Waiting && cTaskThreads > 2))
|
||
{
|
||
// No work or we're all caught up and have extra threads.
|
||
cTaskThreads--;
|
||
return(0);
|
||
}
|
||
else
|
||
{
|
||
if (eState == Waiting)
|
||
{
|
||
// Sleep until just after the next work item is ready.
|
||
delay += 1;
|
||
delay.Sleep();
|
||
}
|
||
}
|
||
}
|
||
|
||
return(0);
|
||
}
|
||
|