Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

571 lines
14 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name:
  4. Worker.cxx
  5. Abstract:
  6. Backgroup activies releated to running down and cleaning up OR and pinging
  7. remote OR's are handled here.
  8. Author:
  9. Mario Goertzel [MarioGo]
  10. Revision History:
  11. MarioGo 03-02-95 Bits 'n pieces
  12. MarioGo 01-18-96 Locally unique IDs
  13. --*/
  14. #include <or.hxx>
  15. static CInterlockedInteger cTaskThreads(0);
  16. #if DBG_DETAIL
  17. extern "C" void printf(char *, ...);
  18. #endif
  19. #define TASKTHREAD_STACK_PAGES 3
  20. #define SECS_BETWEEN_FLUSHES 30
  21. PWR_STATE gPowerState = PWR_RUNNING;
  22. void OLESCMBindingHandleFlush();
  23. #if _MSC_FULL_VER >= 13008827
  24. #pragma warning(push)
  25. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  26. #endif
  27. DWORD WINAPI
  28. ObjectExporterWorkerThread(LPVOID /* ignored */)
  29. /*++
  30. Routine Description:
  31. Main background thread for the object resolver. This thread
  32. manages a number of background tasks:
  33. Cleaning up the client oxid cache.
  34. Running down un-pinged sets.
  35. Starting task threads to rundown server OIDs and ping sets.
  36. This thread must not block for a long time. Task threads
  37. should be used for possibly blocking operations like remote
  38. pinging and rundown of OIDs.
  39. Arguments:
  40. Ignored
  41. Return Value:
  42. None - should never return.
  43. --*/
  44. {
  45. ORSTATUS status;
  46. CTime now(0);
  47. CTime timeout(0);
  48. CTime delay(0);
  49. CTime start(0);
  50. CTime lastflush(0);
  51. BOOL fCreateThread;
  52. SYSTEM_INFO si;
  53. // Retrieve system info so we can start task threads
  54. // with an appropriate initial stack size
  55. GetSystemInfo(&si);
  56. lastflush.SetNow();
  57. for(;;)
  58. {
  59. now.SetNow();
  60. delay = now;
  61. delay += BasePingInterval;
  62. // Cleanup old sets.
  63. //
  64. // Sets are usually cleaned up during processing of pings. (As one set is
  65. // pinged, the next set will be checked to see if it needs to be rundown.)
  66. //
  67. // If there's exactly one set in the table, then it won't be run down except
  68. // by this thread.
  69. //
  70. // NOTE: Similar code in _SimplePing().
  71. gpServerLock->LockShared();
  72. ID setid = gpServerSetTable->CheckForRundowns();
  73. if (setid)
  74. {
  75. gpServerLock->ConvertToExclusive();
  76. if (gpServerSetTable->RundownSetIfNeeded(setid))
  77. {
  78. delay.SetNow();
  79. }
  80. gpServerLock->UnlockExclusive();
  81. }
  82. else
  83. {
  84. gpServerLock->UnlockShared();
  85. }
  86. //
  87. // Cleanup old Client OXIDs
  88. //
  89. if (gpClientOxidPList->PeekMin(timeout))
  90. {
  91. if (timeout < now)
  92. {
  93. CClientOxid *pOxid;
  94. CListElement *ple;
  95. gpClientLock->LockExclusive();
  96. while (ple = gpClientOxidPList->MaybeRemoveMin(now))
  97. {
  98. pOxid = CClientOxid::ContainingRecord(ple);
  99. delete pOxid;
  100. }
  101. gpClientLock->UnlockExclusive();
  102. delay.SetNow();
  103. }
  104. else
  105. {
  106. if (delay > timeout)
  107. {
  108. delay = timeout;
  109. }
  110. }
  111. }
  112. //
  113. // Make sure pinging and rundowns are proceding
  114. //
  115. fCreateThread = FALSE;
  116. // We want to create an extra task thread if we've fallen
  117. // behind on pings. As more threads are created the
  118. // requirements for "behind" become harder to meet.
  119. if (gpClientSetPList->PeekMin(timeout))
  120. {
  121. start = now;
  122. start += (BasePingInterval + 10*cTaskThreads);
  123. if (cTaskThreads == 0 || start < timeout)
  124. {
  125. fCreateThread = TRUE;
  126. }
  127. else
  128. {
  129. if (delay > start)
  130. {
  131. delay = start;
  132. }
  133. }
  134. }
  135. // We want to create an extra task thread if we've fallen
  136. // behind in running down local objects. As more threads are
  137. // created the requirements for "behind" become harder to meet.
  138. if (gpServerOidPList->PeekMin(timeout))
  139. {
  140. start = now;
  141. start -= 10*cTaskThreads;
  142. if (timeout < start)
  143. {
  144. fCreateThread = TRUE;
  145. }
  146. else
  147. {
  148. start = timeout;
  149. start += 2*10*cTaskThreads;
  150. if (delay > start)
  151. {
  152. delay = start;
  153. }
  154. }
  155. }
  156. if (fCreateThread)
  157. {
  158. KdPrintEx((DPFLTR_DCOMSS_ID,
  159. DPFLTR_INFO_LEVEL,
  160. "OR: Creating additional task thread, we're behind..\n"));
  161. cTaskThreads++;
  162. DWORD tid;
  163. HANDLE hThread = CreateThread(0,
  164. (si.dwPageSize * TASKTHREAD_STACK_PAGES),
  165. ObjectExporterTaskThread,
  166. 0,
  167. 0,
  168. &tid
  169. );
  170. if (0 != hThread)
  171. {
  172. CloseHandle(hThread);
  173. }
  174. else
  175. {
  176. cTaskThreads--;
  177. }
  178. }
  179. #if DBG_DETAIL
  180. printf("================================================================\n"
  181. "ServerOxids: %d, ServerOids: %d, ServerSets: %d\n"
  182. "ClientOxids: %d, ClientOids: %d, ClientSets: %d\n"
  183. "Mids: %d, Processes %d, worker threads: %d\n"
  184. "Sleeping for %d seconds...\n",
  185. gpServerOxidTable->Size(),
  186. gpServerOidTable->Size(),
  187. gpServerSetTable->Size(),
  188. gpClientOxidTable->Size(),
  189. gpClientOidTable->Size(),
  190. gpClientSetTable->Size(),
  191. gpMidTable->Size(),
  192. gpProcessList->Size(),
  193. cTaskThreads,
  194. delay - now + 1
  195. );
  196. #endif
  197. delay += 1;
  198. if (delay - lastflush > SECS_BETWEEN_FLUSHES)
  199. {
  200. // Give olescm a chance to do other work
  201. OLESCMBindingHandleFlush();
  202. lastflush.SetNow();
  203. }
  204. delay.Sleep();
  205. }
  206. return(0);
  207. }
  208. #if _MSC_FULL_VER >= 13008827
  209. #pragma warning(pop)
  210. #endif
  211. void
  212. NotifyCOMOnSuspend()
  213. /*++
  214. Routine Desciption
  215. Resets state and takes appropriate action when the machine
  216. goes into a standby (or, hibernate) state.
  217. Arguments:
  218. none
  219. Return Value:
  220. void
  221. --*/
  222. {
  223. ASSERT(gPowerState == PWR_RUNNING);
  224. // Set the flag immediately. Don't need to do this
  225. // under the lock, so do it right away.
  226. gPowerState = PWR_SUSPENDED;
  227. // Take and release gpServerLock. This guarantees that no
  228. // worker threads are in an in-between state (eg, they didn't see
  229. // the gPowerState change above and are currently preempted right
  230. // before checking for eligible serversets or oids to rundown).
  231. gpServerLock->LockExclusive();
  232. gpServerLock->UnlockExclusive();
  233. return;
  234. }
  235. void
  236. NotifyCOMOnResume()
  237. /*++
  238. Routine Desciption
  239. Resets state and takes appropriate action when the machine
  240. starts running again after being in the standby/hibernate
  241. state.
  242. Arguments:
  243. none
  244. Return Value:
  245. void
  246. --*/
  247. {
  248. // We cannot assert here that we're suspended, since the machine
  249. // can suspend in critical situations w/o notifying us
  250. //
  251. // First reset all of the server pingset timers.
  252. //
  253. gpServerLock->LockExclusive();
  254. gpServerSetTable->PingAllSets();
  255. gpServerLock->UnlockExclusive();
  256. //
  257. // Second reset all of the non-referenced oid timeouts,
  258. // so they don't get erroneously rundown.
  259. //
  260. gpServerOidPList->ResetAllOidTimeouts();
  261. //
  262. // Last, reset the power state so that future
  263. // rundowns will not be blocked.
  264. //
  265. gPowerState = PWR_RUNNING;
  266. return;
  267. }
  268. BOOL
  269. RundownOidsHelper(
  270. IN CTime* pNow,
  271. IN CServerOxid* pOxidToRundown) // can be NULL
  272. /*++
  273. Routine Desciption
  274. This function exists so that all of the oid rundown processing
  275. code (well most of it) is in one place. Caller supplies his
  276. "now" and an optional oxid to try to rundown oids from.
  277. Caller must hold gpServerLock when calling. gpServerLock will
  278. be released before returning.
  279. Arguments:
  280. pNow -- caller's Now
  281. pOxidToRundown -- optional pointer to oxid from which caller
  282. wants us to try to rundown oids. If NULL, we simply
  283. use the first eligible oid from any oxid.
  284. Return Value:
  285. TRUE -- One or more oids was processed for rundown
  286. FALSE -- Zero oids were processed for rundown
  287. --*/
  288. {
  289. CServerOid* pOid = NULL;
  290. ULONG cOids = 0;
  291. CServerOid* apOid[MAX_OID_RUNDOWNS_PER_CALL];
  292. ASSERT(pNow);
  293. ASSERT(gpServerLock->HeldExclusive());
  294. // If not running (either we're about to be suspended or we
  295. // we just came out of suspension) don't run anything down.
  296. if (gPowerState != PWR_RUNNING)
  297. {
  298. gpServerLock->UnlockExclusive();
  299. return FALSE;
  300. }
  301. if (!pOxidToRundown)
  302. {
  303. pOid = gpServerOidPList->RemoveEligibleOid(*pNow);
  304. }
  305. else
  306. {
  307. pOid = gpServerOidPList->RemoveEligibleOidWithMatchingOxid(*pNow, pOxidToRundown);
  308. }
  309. if (!pOid)
  310. {
  311. // nothing to rundown, either in general or
  312. // for the specified oxid.
  313. gpServerLock->UnlockExclusive();
  314. return FALSE;
  315. }
  316. // If caller didn't supply an oxid, use the oid's oxid.
  317. if (!pOxidToRundown)
  318. {
  319. pOxidToRundown = pOid->GetOxid();
  320. }
  321. ASSERT(pOxidToRundown);
  322. ZeroMemory(apOid, sizeof(apOid));
  323. do
  324. {
  325. ASSERT(pOid);
  326. ASSERT(pOid->IsFreed() == FALSE);
  327. ASSERT(pOid->IsPinned() == FALSE);
  328. ASSERT(pOid->IsRunningDown() == FALSE);
  329. ASSERT(pOid->GetOxid() == pOxidToRundown);
  330. pOid->SetRundown(TRUE);
  331. apOid[cOids] = pOid;
  332. cOids++;
  333. if (cOids == MAX_OID_RUNDOWNS_PER_CALL)
  334. {
  335. break;
  336. }
  337. pOid = gpServerOidPList->RemoveEligibleOidWithMatchingOxid(*pNow, pOxidToRundown);
  338. } while (pOid);
  339. ASSERT((cOids > 0) && (cOids <= MAX_OID_RUNDOWNS_PER_CALL));
  340. // Note: This call will unlock the server lock. While
  341. // this happens the oids maybe added, deleted, added
  342. // and deleted, added and rundown from one or more sets.
  343. pOxidToRundown->RundownOids(cOids, apOid);
  344. // Assert RundownOids released the lock
  345. ASSERT(!gpServerLock->HeldExclusive());
  346. return TRUE;
  347. }
  348. DWORD WINAPI
  349. ObjectExporterTaskThread(LPVOID /* ignored */)
  350. {
  351. CTime now(0);
  352. CTime delay(0);
  353. CTime timeout(0);
  354. ORSTATUS status;
  355. CListElement *ple;
  356. CClientSet *pSet;
  357. CServerOid *pOid;
  358. enum {
  359. Idle, // No work to do at all.
  360. Waiting, // No work to do yet.
  361. Busy // Did work this iteration.
  362. } eState;
  363. for(;;)
  364. {
  365. now.SetNow();
  366. delay = now;
  367. delay += BasePingInterval;
  368. eState = Idle;
  369. // Ping remote sets.
  370. if (gpClientSetPList->PeekMin(timeout))
  371. {
  372. eState = Waiting;
  373. if (now >= timeout)
  374. {
  375. eState = Busy;
  376. ple = gpClientSetPList->MaybeRemoveMin(now);
  377. if (ple)
  378. {
  379. // Actually ping the set
  380. pSet = CClientSet::ContainingRecord(ple);
  381. pSet->PingServer();
  382. // Set maybe invalid now.
  383. }
  384. }
  385. else
  386. {
  387. // Not ready to ping yet.
  388. delay = timeout;
  389. }
  390. }
  391. //
  392. // Process server OID rundowns
  393. //
  394. if (gpServerOidPList->PeekMin(timeout))
  395. {
  396. if (eState == Idle)
  397. eState = Waiting;
  398. if (now >= timeout)
  399. {
  400. eState = Busy;
  401. gpServerLock->LockExclusive();
  402. BOOL fRanSomethingDown = RundownOidsHelper(&now, NULL);
  403. // Assert RundownOidsHelper released the lock
  404. ASSERT(!gpServerLock->HeldExclusive());
  405. // If we didn't actually try to run any oids down, reset
  406. // state to Waiting so we don't spin unnecessarily.
  407. if (!fRanSomethingDown)
  408. {
  409. eState = Waiting;
  410. }
  411. }
  412. else
  413. {
  414. // Not ready to rundown yet.
  415. if (delay > timeout)
  416. {
  417. delay = timeout;
  418. }
  419. }
  420. }
  421. // Decide if this task thread should exit or sleep and loop.
  422. ASSERT(eState == Idle || eState == Busy || eState == Waiting);
  423. if ((eState == Idle) || (eState == Waiting && cTaskThreads > 2))
  424. {
  425. // No work or we're all caught up and have extra threads.
  426. cTaskThreads--;
  427. return(0);
  428. }
  429. else
  430. {
  431. if (eState == Waiting)
  432. {
  433. // Sleep until just after the next work item is ready.
  434. delay += 1;
  435. delay.Sleep();
  436. }
  437. }
  438. }
  439. return(0);
  440. }