Source code of Windows XP (NT5)
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.

491 lines
12 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. typedef enum { PWR_SUSPENDED, PWR_RUNNING } PWR_STATE;
  22. PWR_STATE gPowerState = PWR_RUNNING;
  23. void OLESCMBindingHandleFlush();
  24. #if _MSC_FULL_VER >= 13008827
  25. #pragma warning(push)
  26. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  27. #endif
  28. DWORD WINAPI
  29. ObjectExporterWorkerThread(LPVOID /* ignored */)
  30. /*++
  31. Routine Description:
  32. Main background thread for the object resolver. This thread
  33. manages a number of background tasks:
  34. Cleaning up the client oxid cache.
  35. Running down un-pinged sets.
  36. Starting task threads to rundown server OIDs and ping sets.
  37. This thread must not block for a long time. Task threads
  38. should be used for possibly blocking operations like remote
  39. pinging and rundown of OIDs.
  40. Arguments:
  41. Ignored
  42. Return Value:
  43. None - should never return.
  44. --*/
  45. {
  46. ORSTATUS status;
  47. CTime now(0);
  48. CTime timeout(0);
  49. CTime delay(0);
  50. CTime start(0);
  51. CTime lastflush(0);
  52. BOOL fCreateThread;
  53. SYSTEM_INFO si;
  54. // Retrieve system info so we can start task threads
  55. // with an appropriate initial stack size
  56. GetSystemInfo(&si);
  57. lastflush.SetNow();
  58. for(;;)
  59. {
  60. now.SetNow();
  61. delay = now;
  62. delay += BasePingInterval;
  63. // Cleanup old sets.
  64. //
  65. // Sets are usually cleaned up during processing of pings. (As one set is
  66. // pinged, the next set will be checked to see if it needs to be rundown.)
  67. //
  68. // If there's exactly one set in the table, then it won't be run down except
  69. // by this thread.
  70. //
  71. // NOTE: Similar code in _SimplePing().
  72. gpServerLock->LockShared();
  73. ID setid = gpServerSetTable->CheckForRundowns();
  74. if (setid)
  75. {
  76. gpServerLock->ConvertToExclusive();
  77. if (gpServerSetTable->RundownSetIfNeeded(setid))
  78. {
  79. delay.SetNow();
  80. }
  81. gpServerLock->UnlockExclusive();
  82. }
  83. else
  84. {
  85. gpServerLock->UnlockShared();
  86. }
  87. //
  88. // Cleanup old Client OXIDs
  89. //
  90. if (gpClientOxidPList->PeekMin(timeout))
  91. {
  92. if (timeout < now)
  93. {
  94. CClientOxid *pOxid;
  95. CListElement *ple;
  96. gpClientLock->LockExclusive();
  97. while (ple = gpClientOxidPList->MaybeRemoveMin(now))
  98. {
  99. pOxid = CClientOxid::ContainingRecord(ple);
  100. delete pOxid;
  101. }
  102. gpClientLock->UnlockExclusive();
  103. delay.SetNow();
  104. }
  105. else
  106. {
  107. if (delay > timeout)
  108. {
  109. delay = timeout;
  110. }
  111. }
  112. }
  113. //
  114. // Make sure pinging and rundowns are proceding
  115. //
  116. fCreateThread = FALSE;
  117. // We want to create an extra task thread if we've fallen
  118. // behind on pings. As more threads are created the
  119. // requirements for "behind" become harder to meet.
  120. if (gpClientSetPList->PeekMin(timeout))
  121. {
  122. start = now;
  123. start += (BasePingInterval + 10*cTaskThreads);
  124. if (cTaskThreads == 0 || start < timeout)
  125. {
  126. fCreateThread = TRUE;
  127. }
  128. else
  129. {
  130. if (delay > start)
  131. {
  132. delay = start;
  133. }
  134. }
  135. }
  136. // We want to create an extra task thread if we've fallen
  137. // behind in running down local objects. As more threads are
  138. // created the requirements for "behind" become harder to meet.
  139. if (gpServerOidPList->PeekMin(timeout))
  140. {
  141. start = now;
  142. start -= 10*cTaskThreads;
  143. if (timeout < start)
  144. {
  145. fCreateThread = TRUE;
  146. }
  147. else
  148. {
  149. start = timeout;
  150. start += 2*10*cTaskThreads;
  151. if (delay > start)
  152. {
  153. delay = start;
  154. }
  155. }
  156. }
  157. if (fCreateThread)
  158. {
  159. KdPrintEx((DPFLTR_DCOMSS_ID,
  160. DPFLTR_INFO_LEVEL,
  161. "OR: Creating additional task thread, we're behind..\n"));
  162. cTaskThreads++;
  163. DWORD tid;
  164. HANDLE hThread = CreateThread(0,
  165. (si.dwPageSize * TASKTHREAD_STACK_PAGES),
  166. ObjectExporterTaskThread,
  167. 0,
  168. 0,
  169. &tid
  170. );
  171. if (0 != hThread)
  172. {
  173. CloseHandle(hThread);
  174. }
  175. else
  176. {
  177. cTaskThreads--;
  178. }
  179. }
  180. #if DBG_DETAIL
  181. printf("================================================================\n"
  182. "ServerOxids: %d, ServerOids: %d, ServerSets: %d\n"
  183. "ClientOxids: %d, ClientOids: %d, ClientSets: %d\n"
  184. "Mids: %d, Processes %d, worker threads: %d\n"
  185. "Sleeping for %d seconds...\n",
  186. gpServerOxidTable->Size(),
  187. gpServerOidTable->Size(),
  188. gpServerSetTable->Size(),
  189. gpClientOxidTable->Size(),
  190. gpClientOidTable->Size(),
  191. gpClientSetTable->Size(),
  192. gpMidTable->Size(),
  193. gpProcessList->Size(),
  194. cTaskThreads,
  195. delay - now + 1
  196. );
  197. #endif
  198. delay += 1;
  199. if (delay - lastflush > SECS_BETWEEN_FLUSHES)
  200. {
  201. // Give olescm a chance to do other work
  202. OLESCMBindingHandleFlush();
  203. lastflush.SetNow();
  204. }
  205. delay.Sleep();
  206. }
  207. return(0);
  208. }
  209. #if _MSC_FULL_VER >= 13008827
  210. #pragma warning(pop)
  211. #endif
  212. void
  213. NotifyCOMOnSuspend()
  214. /*++
  215. Routine Desciption
  216. Resets state and takes appropriate action when the machine
  217. goes into a standby (or, hibernate) state.
  218. Arguments:
  219. none
  220. Return Value:
  221. void
  222. --*/
  223. {
  224. ASSERT(gPowerState == PWR_RUNNING);
  225. // Just set the flag. This will block any further
  226. // rundowns from happening from now until we get a
  227. // resume notification.
  228. gPowerState = PWR_SUSPENDED;
  229. return;
  230. }
  231. void
  232. NotifyCOMOnResume()
  233. /*++
  234. Routine Desciption
  235. Resets state and takes appropriate action when the machine
  236. starts running again after being in the standby/hibernate
  237. state.
  238. Arguments:
  239. none
  240. Return Value:
  241. void
  242. --*/
  243. {
  244. // We cannot assert here that we're suspended, since the machine
  245. // can suspend in critical situations w/o notifying us
  246. //
  247. // First reset all of the server pingset timers.
  248. //
  249. gpServerLock->LockExclusive();
  250. // After a resume, all ping sets get reset to zero
  251. gpServerSetTable->PingAllSets();
  252. gpServerLock->UnlockExclusive();
  253. //
  254. // Second and last, reset the power state so that future
  255. // rundowns will not be blocked.
  256. //
  257. gPowerState = PWR_RUNNING;
  258. return;
  259. }
  260. DWORD WINAPI
  261. ObjectExporterTaskThread(LPVOID /* ignored */)
  262. {
  263. CTime now(0);
  264. CTime delay(0);
  265. CTime timeout(0);
  266. ORSTATUS status;
  267. CListElement *ple;
  268. CClientSet *pSet;
  269. CServerOid *pOid;
  270. enum {
  271. Idle, // No work to do at all.
  272. Waiting, // No work to do yet.
  273. Busy // Did work this iteration.
  274. } eState;
  275. for(;;)
  276. {
  277. now.SetNow();
  278. delay = now;
  279. delay += BasePingInterval;
  280. eState = Idle;
  281. // Ping remote sets.
  282. if (gpClientSetPList->PeekMin(timeout))
  283. {
  284. eState = Waiting;
  285. if (now >= timeout)
  286. {
  287. eState = Busy;
  288. ple = gpClientSetPList->MaybeRemoveMin(now);
  289. if (ple)
  290. {
  291. // Actually ping the set
  292. pSet = CClientSet::ContainingRecord(ple);
  293. pSet->PingServer();
  294. // Set maybe invalid now.
  295. }
  296. }
  297. else
  298. {
  299. // Not ready to ping yet.
  300. delay = timeout;
  301. }
  302. }
  303. //
  304. // Process server OID rundowns. Only do this
  305. // if there's something to rundown, and the machine
  306. // is not in the suspended state.
  307. //
  308. if (gPowerState == PWR_RUNNING &&
  309. gpServerOidPList->PeekMin(timeout))
  310. {
  311. if (eState == Idle)
  312. eState = Waiting;
  313. if (now >= timeout)
  314. {
  315. eState = Busy;
  316. gpServerLock->LockExclusive();
  317. CServerOid* apOid[MAX_OID_RUNDOWNS_PER_CALL];
  318. ULONG cOids;
  319. ple = gpServerOidPList->MaybeRemoveMin(now);
  320. pOid = CServerOid::ContainingRecord(ple);
  321. if (ple && pOid->IsRunningDown() == FALSE)
  322. {
  323. apOid[0] = pOid;
  324. cOids = 1;
  325. ASSERT(pOid->IsFreed() == FALSE);
  326. ASSERT(pOid->IsPinned() == FALSE);
  327. pOid->SetRundown(TRUE);
  328. while(cOids < MAX_OID_RUNDOWNS_PER_CALL && pOid)
  329. {
  330. pOid = gpServerOidPList->MaybeRemoveMatchingOxid(now, apOid[0]);
  331. if (pOid && pOid->IsRunningDown() == FALSE)
  332. {
  333. ASSERT(pOid->IsFreed() == FALSE);
  334. ASSERT(pOid->IsPinned() == FALSE);
  335. pOid->SetRundown(TRUE);
  336. apOid[cOids] = pOid;
  337. cOids++;
  338. }
  339. }
  340. ASSERT(cOids < MAX_OID_RUNDOWNS_PER_CALL+1 && cOids >= 1);
  341. ASSERT(apOid[0]->GetOxid() == apOid[cOids - 1]->GetOxid());
  342. // Note: This call will unlock the server lock. While
  343. // this happens the oids maybe added, deleted, added
  344. // and deleted, added and rundown from one or more sets.
  345. CServerOxid* pOxid = apOid[0]->GetOxid();
  346. pOxid->RundownOids(cOids, apOid);
  347. // Assert RundownOids released the lock
  348. ASSERT(!gpServerLock->HeldExclusive());
  349. }
  350. else
  351. {
  352. gpServerLock->UnlockExclusive();
  353. }
  354. }
  355. else
  356. {
  357. // Not ready to rundown yet.
  358. if (delay > timeout)
  359. {
  360. delay = timeout;
  361. }
  362. }
  363. }
  364. // Decide if this task thread should exit or sleep and loop.
  365. ASSERT(eState == Idle || eState == Busy || eState == Waiting);
  366. if ((eState == Idle) || (eState == Waiting && cTaskThreads > 2))
  367. {
  368. // No work or we're all caught up and have extra threads.
  369. cTaskThreads--;
  370. return(0);
  371. }
  372. else
  373. {
  374. if (eState == Waiting)
  375. {
  376. // Sleep until just after the next work item is ready.
  377. delay += 1;
  378. delay.Sleep();
  379. }
  380. }
  381. }
  382. return(0);
  383. }