/*++ 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 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); }