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.

601 lines
22 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. GC.cxx
  5. Abstract:
  6. The garbage collection mechanism common code. See the comment
  7. below for more details.
  8. Author:
  9. Kamen Moutafov (kamenm) Apr 2000
  10. Garbage Collection Mechanism:
  11. This comment describes how the garbage collection mechanism works.
  12. The code itself is spread in variety in places.
  13. Purpose:
  14. There are two types of garbage collection we perform - periodic
  15. and one-time. Periodic may be needed by the Osf idle connection
  16. cleanup mechanism, which tries to cleanup unused osf connections
  17. if the app explicitly asked for it via RpcMgmtEnableIdleCleanup.
  18. The one-time cleanup is used by the lingering associations. If
  19. an association is lingered, it will request cleanup to be performed
  20. after a certain period of time. The garbage collection needs to
  21. support both of those mechanisms.
  22. Design Goals:
  23. Have minimal memory and CPU consumption requirements
  24. Don't cause periodic background activity if there is no
  25. garbage collection to be performed.
  26. Guarantee that garbage collection will be performed in
  27. a reasonable amount of time after its request time (i.e. 10 minutes
  28. to an hour at worst case)
  29. Implementation:
  30. We use the worker threads in the thread pools to perform garbage
  31. collection. There are several thread pools - the Ioc thread pool
  32. (remote threads) as well as one thread pool for each LRPC address.
  33. Within each pool, from a gc perspective, we differentiate between
  34. two types of threads - threads on a short wait and threads on a
  35. long wait. Threads on a short wait are either threads waiting for
  36. something to happen with a timeout of gThreadTimeout or less, or
  37. threads performing a work item (threads doing both are also
  38. considered to be on a short wait). Threads on a long wait are
  39. threads waiting for more than that. As part of our thread management
  40. we will keep count of how many threads are on a short wait and how
  41. many are on a long wait.
  42. All threads in all thread pools will attempt to do garbage collection
  43. when they timeout waiting for something to happen. Since all thread pools
  44. need at least one listening thread, all thread pools are guaranteed to
  45. have a thread timing out once every so often. The garbage collection attempt
  46. will be cut very short if there is nothing to garbage collect, so the
  47. attempt is not performance expensive in the common case. The function
  48. to attempt garbage collection is PerformGarbageCollection
  49. If a thread times out on the completion port/LPC port, it will
  50. do garbage collection, and then will check whether there are
  51. more items to garbage collect (either one-time or periodic) and how
  52. many threads from this thread pool are on a short wait. If there is
  53. garbage collection to be done, and there are no other threads on short
  54. wait, this thread will not go on a long wait, but it will repeat its
  55. short wait. This ensures timely garbage collection. If all the threads
  56. have gone on a long wait, and a piece of code needs garbage collection,
  57. it will request the garbage collection and it will tickle a worker thread.
  58. The tickling consist of posting an empty message to the completion port
  59. or LPC port. All the synchronization b/n worker threads and threads
  60. requesting garbage collection is done using interlocks, to avoid perf
  61. hit. This introduces a couple of benign races through the code, which may
  62. prevent a thread from going on a long wait once, but that's ok.
  63. In order to ensure that we do gc only when needed, in most cases we refcount
  64. the number of items that need garbage collection.
  65. --*/
  66. #include <precomp.hxx>
  67. #include <hndlsvr.hxx>
  68. #include <lpcpack.hxx>
  69. #include <lpcsvr.hxx>
  70. #include <osfpcket.hxx>
  71. #include <bitset.hxx>
  72. #include <queue.hxx>
  73. #include <ProtBind.hxx>
  74. #include <osfclnt.hxx>
  75. #include <rpcqos.h>
  76. #include <lpcclnt.hxx>
  77. // used by periodic cleanup only - the period
  78. // on which to do cleanup. This is in seconds
  79. unsigned long WaitToGarbageCollectDelay = 0;
  80. // The number of items on which garbage collection
  81. // is needed. If 0, no periodic garbage collection
  82. // is necessary. Each item that needs garbage collection
  83. // will InterlockIncrement this when it is created,
  84. // and will InterlockDecrement this when it is destroyed
  85. long PeriodicGarbageCollectItems = 0;
  86. // set non-zero when we need to cleanup idle LRPC_SCONTEXTs
  87. unsigned int fEnableIdleLrpcSContextsCleanup = 0;
  88. // set to non-zero when we enable garbage collection cleanup. This either
  89. // happens when the user calls it explicitly with
  90. // RpcMgmtEnableIdleCleanup or implicitly if we gather too many
  91. // connection in an association
  92. unsigned int fEnableIdleConnectionCleanup = 0;
  93. unsigned int IocThreadStarted = 0;
  94. // used by one-time garbage collection items only!
  95. long GarbageCollectionRequested = 0;
  96. // The semantics of this variable should be
  97. // interpreted as follows - don't bother to cleanup
  98. // before this time stamp - you won't find anything.
  99. // This means that after this interval, there may be
  100. // many items to cleanup later on - it just says the
  101. // first is at this time.
  102. // The timestamp is in millseconds.
  103. DWORD NextOneTimeCleanup = 0;
  104. const int MaxPeriodsWithoutGC = 100;
  105. BOOL
  106. GarbageCollectionNeeded (
  107. IN BOOL fOneTimeCleanup,
  108. IN unsigned long GarbageCollectInterval
  109. )
  110. /*++
  111. Routine Description:
  112. A routine used by code throughout RPC to arrange
  113. for garbage collection to be performed. Currently,
  114. there are two types of garbage collecting -
  115. idle Osf connections and lingering associations.
  116. Parameters:
  117. fOneTimeCleanup - if non-zero, this is a one time
  118. cleanup and GarbageCollectInterval is interpreted as the
  119. minimum time after which we want garbage collection
  120. performed. Note that the garbage collection code can kick
  121. off earlier than that. Appropriate arrangements must be
  122. made to protect items not due for garbage collection.
  123. If 0, this is a periodic cleanup, and
  124. GarbageCollectInterval is interpreted as the period for
  125. which we wait before making the next garbage collection
  126. pass. Note that for the periodic cleanup, this is a hint
  127. that can be ignored - don't count on it. The time is in
  128. milliseconds.
  129. Return Value:
  130. non-zero - garbage collection is available and will be done
  131. FALSE - garbage collection is not available
  132. --*/
  133. {
  134. RPC_STATUS RpcStatus = RPC_S_OK;
  135. THREAD * Thread;
  136. DWORD LocalTickCount;
  137. LOADABLE_TRANSPORT *LoadableTransport;
  138. LOADABLE_TRANSPORT *FirstTransport = NULL;
  139. DictionaryCursor cursor;
  140. BOOL fRetVal = FALSE;
  141. LRPC_ADDRESS *CurrentAddress;
  142. LRPC_ADDRESS *LrpcAddressToTickle = NULL;
  143. if (fOneTimeCleanup)
  144. {
  145. LocalTickCount = GetTickCount();
  146. // N.B. There is a race here where two threads can set this -
  147. // the race is benign - the second thread will win and write
  148. // its time, which by virtue of the small race window will
  149. // be shortly after the first thread
  150. if (!GarbageCollectionRequested)
  151. {
  152. NextOneTimeCleanup = LocalTickCount + GarbageCollectInterval;
  153. GarbageCollectionRequested = 1;
  154. #if defined (RPC_GC_AUDIT)
  155. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) GC requested - tick count %d\n",
  156. GetCurrentProcessId(), GetCurrentProcessId(), LocalTickCount);
  157. #endif
  158. }
  159. }
  160. else
  161. {
  162. // WaitToGarbageCollectDelay is a global variable - avoid sloshing
  163. if (WaitToGarbageCollectDelay == 0)
  164. WaitToGarbageCollectDelay = GarbageCollectInterval;
  165. InterlockedIncrement(&PeriodicGarbageCollectItems);
  166. }
  167. // is the completion port started? If yes, we will use it as the
  168. // preferred method of garbage collection
  169. if (IocThreadStarted)
  170. {
  171. // if we use the completion port, we either need a thread on a
  172. // short wait (i.e. it will perform garbage collection soon
  173. // anyway), or we need to tickle a thread on a long wait. We know
  174. // that one of these will be true, because we always keep
  175. // listening threads on the completion port - the only
  176. // question is whether it is on a long or short wait thread that
  177. // we have.
  178. // this dictionary is guaranteed to never grow beyond the initial
  179. // dictionary size and elements from it are never deleted - therefore,
  180. // it is safe to iterate it without holding a mutex - we may miss
  181. // an element if it was just being added, but that's ok. The important
  182. // thing is that we can't fault
  183. LoadedLoadableTransports->Reset(cursor);
  184. while ((LoadableTransport
  185. = LoadedLoadableTransports->Next(cursor)) != 0)
  186. {
  187. if (LoadableTransport->GetThreadsDoingShortWait() > 0)
  188. {
  189. #if defined (RPC_GC_AUDIT)
  190. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: there are Ioc threads on short wait - don't tickle\n",
  191. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
  192. #endif
  193. // there is a transport with threads on short wait
  194. // garbage collection will be performed soon even without
  195. // our help - we can bail out
  196. FirstTransport = NULL;
  197. fRetVal = TRUE;
  198. break;
  199. }
  200. if (FirstTransport == NULL)
  201. FirstTransport = LoadableTransport;
  202. }
  203. }
  204. else if (LrpcAddressList
  205. && (((RTL_CRITICAL_SECTION *)(NtCurrentPeb()->LoaderLock))->OwningThread != NtCurrentTeb()->ClientId.UniqueThread))
  206. {
  207. LrpcMutexRequest();
  208. // else, if there are Lrpc Addresses, check whether they are doing short wait
  209. // and can gc for us
  210. CurrentAddress = LrpcAddressList;
  211. while (CurrentAddress)
  212. {
  213. // can this address gc for us?
  214. if (CurrentAddress->GetNumberOfThreadsDoingShortWait() > 0)
  215. {
  216. #if defined (RPC_GC_AUDIT)
  217. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: there are threads on short wait (%d) on address %X - don't tickle\n",
  218. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), CurrentAddress,
  219. CurrentAddress->GetNumberOfThreadsDoingShortWait());
  220. #endif
  221. LrpcAddressToTickle = NULL;
  222. fRetVal = TRUE;
  223. break;
  224. }
  225. if ((LrpcAddressToTickle == NULL) && (CurrentAddress->IsPreparedForLoopbackTickling()))
  226. {
  227. LrpcAddressToTickle = CurrentAddress;
  228. }
  229. CurrentAddress = CurrentAddress->GetNextAddress();
  230. }
  231. // N.B. It is possible that Osf associations need cleanup, but only LRPC worker
  232. // threads are available, and moreover, no LRPC associations were created, which
  233. // means none of the Lrpc addresses is prepared for loopback tickling. If this is
  234. // the case, choose the first address, and make sure it is prepared for tickling
  235. if ((LrpcAddressToTickle == NULL) && (fRetVal == FALSE))
  236. {
  237. LrpcAddressToTickle = LrpcAddressList;
  238. // prepare the selected address for tickling
  239. fRetVal = LrpcAddressToTickle->PrepareForLoopbackTicklingIfNecessary();
  240. if (fRetVal == FALSE)
  241. {
  242. // if this fails, zero out the address for tickling. This
  243. // will cause this function to return failure
  244. LrpcAddressToTickle = NULL;
  245. }
  246. }
  247. LrpcMutexClear();
  248. }
  249. else if (fEnableIdleConnectionCleanup)
  250. {
  251. // if fEnableIdleConnectionCleanup is set, we have to create a thread if there is't one yet
  252. RpcStatus = CreateGarbageCollectionThread();
  253. if (RpcStatus == RPC_S_OK)
  254. {
  255. // the thread creation was successful - tell our caller we
  256. // will be doing garbage collection
  257. fRetVal = TRUE;
  258. }
  259. }
  260. // neither Ioc nor the LRPC thread pools have threads on short wait
  261. // We have to tickle somebody - we try the Ioc thread pool first
  262. if (FirstTransport)
  263. {
  264. // we couldn't find any transport with threads on short wait -
  265. // tickle a thread from the RPC transport in order to ensure timely
  266. // cleanup
  267. #if defined (RPC_GC_AUDIT)
  268. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: No Ioc threads on short wait found - tickling one\n",
  269. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
  270. #endif
  271. RpcStatus = TickleIocThread();
  272. if (RpcStatus == RPC_S_OK)
  273. fRetVal = TRUE;
  274. }
  275. else if (LrpcAddressToTickle)
  276. {
  277. // try to tickle the LRPC address
  278. fRetVal = LrpcAddressToTickle->LoopbackTickle();
  279. }
  280. return fRetVal;
  281. }
  282. RPC_STATUS CreateGarbageCollectionThread (
  283. void
  284. )
  285. /*++
  286. Routine Description:
  287. Make a best effort to create a garbage collection thread. In this
  288. implementation we simply choose to create a completion port thread,
  289. as it has many uses.
  290. Return Value:
  291. RPC_S_OK on success or RPC_S_* on error
  292. --*/
  293. {
  294. TRANS_INFO *TransInfo;
  295. RPC_STATUS RpcStatus;
  296. if (IsGarbageCollectionAvailable())
  297. return RPC_S_OK;
  298. RpcStatus = LoadableTransportInfo(L"rpcrt4.dll",
  299. L"ncacn_ip_tcp",
  300. &TransInfo);
  301. if (RpcStatus != RPC_S_OK)
  302. return RpcStatus;
  303. RpcStatus = TransInfo->CreateThread();
  304. return RpcStatus;
  305. }
  306. RPC_STATUS
  307. EnableIdleConnectionCleanup (
  308. void
  309. )
  310. /*++
  311. Routine Description:
  312. We need to enable idle connection cleanup.
  313. Return Value:
  314. RPC_S_OK - This value will always be returned.
  315. --*/
  316. {
  317. fEnableIdleConnectionCleanup = 1;
  318. return(RPC_S_OK);
  319. }
  320. RPC_STATUS
  321. EnableIdleLrpcSContextsCleanup (
  322. void
  323. )
  324. /*++
  325. Routine Description:
  326. We need to enable idle LRPC SContexts cleanup.
  327. Return Value:
  328. RPC_S_OK - This value will always be returned.
  329. --*/
  330. {
  331. // this is a global variable - prevent sloshing
  332. if (fEnableIdleLrpcSContextsCleanup == 0)
  333. fEnableIdleLrpcSContextsCleanup = 1;
  334. return(RPC_S_OK);
  335. }
  336. long GarbageCollectingInProgress = 0;
  337. DWORD LastCleanupTime = 0;
  338. void
  339. PerformGarbageCollection (
  340. void
  341. )
  342. /*++
  343. Routine Description:
  344. This routine should be called periodically so that each protocol
  345. module can perform garbage collection of resources as necessary.
  346. --*/
  347. {
  348. DWORD LocalTickCount;
  349. DWORD Diff;
  350. #if defined (RPC_GC_AUDIT)
  351. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: trying to garbage collect\n",
  352. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
  353. #endif
  354. if (InterlockedIncrement(&GarbageCollectingInProgress) > 1)
  355. {
  356. //
  357. // Don't need more than one thread garbage collecting
  358. //
  359. #if defined (RPC_GC_AUDIT)
  360. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: beaten to GC - returning\n",
  361. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
  362. #endif
  363. InterlockedDecrement(&GarbageCollectingInProgress);
  364. return;
  365. }
  366. if ((fEnableIdleConnectionCleanup || fEnableIdleLrpcSContextsCleanup) && PeriodicGarbageCollectItems)
  367. {
  368. LocalTickCount = GetTickCount();
  369. // make sure we don't cleanup too often - this is unnecessary
  370. if (LocalTickCount - LastCleanupTime > WaitToGarbageCollectDelay)
  371. {
  372. LastCleanupTime = LocalTickCount;
  373. #if defined (RPC_GC_AUDIT)
  374. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Doing periodic garbage collection\n",
  375. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
  376. #endif
  377. // the periodic cleanup
  378. if (fEnableIdleLrpcSContextsCleanup)
  379. {
  380. GlobalRpcServer->EnumerateAndCallEachAddress(RPC_SERVER::actCleanupIdleSContext,
  381. NULL);
  382. }
  383. if (fEnableIdleConnectionCleanup)
  384. {
  385. OSF_CCONNECTION::OsfDeleteIdleConnections();
  386. }
  387. }
  388. else
  389. {
  390. #if defined (RPC_GC_AUDIT)
  391. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Too soon for periodic gc - skipping (%d, %d)\n",
  392. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), LocalTickCount,
  393. LastCleanupTime);
  394. #endif
  395. }
  396. }
  397. if (GarbageCollectionRequested)
  398. {
  399. LocalTickCount = GetTickCount();
  400. Diff = LocalTickCount - NextOneTimeCleanup;
  401. if ((int)Diff >= 0)
  402. {
  403. #if defined (RPC_GC_AUDIT)
  404. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Doing one time gc\n",
  405. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
  406. #endif
  407. // assume the garbage collection will succeed. If it doesn't, the
  408. // functions called below have the responsibility to re-raise the flag
  409. // Note that there is a race condition where they may fail, but when
  410. // the flag was down, a thread went on a long wait. This again is ok,
  411. // because the current thread will figure out there is more garbage
  412. // collection to be done, because the flag is raised, and will do
  413. // a short wait. In worst case, the gc may be delayed because this
  414. // thread will pick a work item, and won't spawn another thread,
  415. // because there is already a thread in the IOCP, which is doing a
  416. // long wait. This may delay the gc from short to long wait. This is
  417. // Ok as it is in accordance with our design goals.
  418. GarbageCollectionRequested = 0;
  419. OSF_CASSOCIATION::OsfDeleteLingeringAssociations();
  420. LRPC_CASSOCIATION::LrpcDeleteLingeringAssociations();
  421. }
  422. else
  423. {
  424. #if defined (RPC_GC_AUDIT)
  425. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Too soon for one time gc - skipping (%d)\n",
  426. GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), (int)Diff);
  427. #endif
  428. }
  429. }
  430. GarbageCollectingInProgress = 0;
  431. }
  432. BOOL
  433. CheckIfGCShouldBeTurnedOn (
  434. IN ULONG DestroyedAssociations,
  435. IN const ULONG NumberOfDestroyedAssociationsToSample,
  436. IN const long DestroyedAssociationBatchThreshold,
  437. IN OUT ULARGE_INTEGER *LastDestroyedAssociationsBatchTimestamp
  438. )
  439. /*++
  440. Routine Description:
  441. Checks if it makes sense to turn on garbage collection
  442. for this process just for the pruposes of having
  443. association lingering available.
  444. Parameters:
  445. DestroyedAssociations - the number of associations destroyed
  446. for this process so far (Osf and Lrpc may keep a separate
  447. count)
  448. NumberOfDestroyedAssociationsToReach - how many associations
  449. it takes to destroy for gc to be turned on
  450. DestroyedAssociationBatchThreshold - the time interval for which
  451. we have to destroy NumberOfDestroyedAssociationsToReach in
  452. order for gc to kick in
  453. LastDestroyedAssociationsBatchTimestamp - the timestamp when
  454. we made the last check
  455. Return Value:
  456. non-zero - GC should be turned on
  457. FALSE - GC is either already on, or should not be turned on
  458. --*/
  459. {
  460. FILETIME CurrentSystemTimeAsFileTime;
  461. ULARGE_INTEGER CurrentSystemTime;
  462. BOOL fEnableGarbageCollection;
  463. if (IsGarbageCollectionAvailable()
  464. || ((DestroyedAssociations % NumberOfDestroyedAssociationsToSample) != 0))
  465. {
  466. return FALSE;
  467. }
  468. fEnableGarbageCollection = FALSE;
  469. GetSystemTimeAsFileTime(&CurrentSystemTimeAsFileTime);
  470. CurrentSystemTime.LowPart = CurrentSystemTimeAsFileTime.dwLowDateTime;
  471. CurrentSystemTime.HighPart = CurrentSystemTimeAsFileTime.dwHighDateTime;
  472. if (LastDestroyedAssociationsBatchTimestamp->QuadPart != 0)
  473. {
  474. #if defined (RPC_GC_AUDIT)
  475. ULARGE_INTEGER Temp;
  476. Temp.QuadPart = CurrentSystemTime.QuadPart - LastDestroyedAssociationsBatchTimestamp->QuadPart;
  477. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LRPC time stamp diff: %X %X\n",
  478. GetCurrentProcessId(), GetCurrentProcessId(), Temp.HighPart, Temp.LowPart);
  479. #endif
  480. if (CurrentSystemTime.QuadPart - LastDestroyedAssociationsBatchTimestamp->QuadPart <=
  481. DestroyedAssociationBatchThreshold)
  482. {
  483. // we have destroyed plenty (NumberOfDestroyedAssociationsToSample) of
  484. // associations for less than DestroyedAssociationBatchThreshold
  485. // this process will probably benefit from garbage collection turned on as it
  486. // does a lot of binds. Return so to the caller
  487. fEnableGarbageCollection = TRUE;
  488. }
  489. }
  490. #if defined (RPC_GC_AUDIT)
  491. else
  492. {
  493. DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Time stamp is 0 - set it\n",
  494. GetCurrentProcessId(), GetCurrentProcessId());
  495. }
  496. #endif
  497. LastDestroyedAssociationsBatchTimestamp->QuadPart = CurrentSystemTime.QuadPart;
  498. return fEnableGarbageCollection;
  499. }