Bound the pooled queues by the number of items they contained - #1443
Merged
Merged
Conversation
QueuePooledObjectPolicy<T>.Return discarded a queue when its Count was greater than MaximumRetainedCount, but the consumers dequeue all the items before returning the queue to the pool, so Count was 0 whatever the number of items the queue contained. As Queue<T>.Clear does not release the backing array, the pool retained the array of a queue that grew a lot, such as the one used by UseLangwordInXmlCommentAnalyzer to walk an unusually wide documentation comment. The capacity of the queue cannot be used to detect these queues: the assembly loaded by Roslyn is the netstandard2.0 one, and Queue<T>.Capacity is only available from .NET 9, so the check would only apply to the build used by the tests. Introduce PooledQueue<T>, which keeps track of the maximum number of items it contained since it was last cleared, and use it as the bound in the policy so the behavior is the same for all the target frameworks.
This was referenced Sep 8, 2026
Closed
Closed
Closed
Closed
Bump Meziantou.Analyzer from 3.0.139 to 3.0.231
Analogy-LogViewer/Analogy.LogViewer.NLog.Targets#566
Closed
This was referenced Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
QueuePooledObjectPolicy<T>.Returndiscarded a queue when itsCountwas greater thanMaximumRetainedCount, so it never discarded anything: the consumers dequeue all the items before returning the queue to the pool, soCountis0whatever the number of items the queue contained. AsQueue<T>.Cleardoes not release the backing array, the static pool retained the array of a queue that grew a lot, such as the oneUseLangwordInXmlCommentAnalyzeruses to walk an unusually wide documentation comment. The items themselves were released byClear, so what was retained was the array storage, not the syntax nodes.PooledQueue<T>now wraps theQueue<T>and keeps track of the maximum number of items it contained since it was last cleared, and the policy uses that high-water mark as its bound.MaximumRetainedCountkeeps its default of1024, and is now an approximation of the capacity of the backing array, which is within a factor of about 2 of the peak count.Why not the capacity of the queue
The assembly loaded by Roslyn is the
netstandard2.0one, whereQueue<T>.Capacitydoes not exist: it was added in .NET 9. A check guarded by#if NET9_0_OR_GREATERwould only have bounded thenet10.0build used by the tests, and not the analyzer that ships. Tracking the high-water mark behaves the same way for all the target frameworks and needs no reflection.Notes for the reviewer
UseLangwordInXmlCommentAnalyzeronly changes the type of its pool field; the traversal itself is untouched.PooledQueue<T>exposes only what the consumers and the policy need (Count,MaximumCount,Enqueue,TryDequeue,Clear). It is not enumerable, so one existing assertion moved fromAssert.EmptytoAssert.Equal(0, queue.Count).Tests
Two tests cover the grow, drain and return path the previous bound missed, one on the policy and one on the pool. Reverting only the
MaximumCountcomparison back toCountmakes exactly those two fail.dotnet build: succeeded for the five Roslyn versions, 0 warnings.dotnet test --filter "FullyQualifiedName~ObjectPoolTests|FullyQualifiedName~UseLangwordInXmlComment": 160 passed, 0 failed (32 tests for each of the 5 Roslyn versions).dotnet run --project src/DocumentationGenerator: exit code 0, no markdown change.