Avoid freezing ConcurrentQueue segments in Count (#18035)
authorStephen Toub <stoub@microsoft.com>
Sat, 19 May 2018 02:07:02 +0000 (22:07 -0400)
committerGitHub <noreply@github.com>
Sat, 19 May 2018 02:07:02 +0000 (22:07 -0400)
commit6e8232059e563d4e3d877f7020db17ee14e285f0
tree3ae299b64f5be330ab0e5c4cd3eab4e743ff4c8e
parent3c28c423053f30adedc034c5e2c912bd2adaad10
Avoid freezing ConcurrentQueue segments in Count (#18035)

* Avoid freezing ConcurrentQueue segments in Count

In .NET Core 2.0, we changed the implementation of ConcurrentQueue to allow segment reuse, making enqueues ammoritzed allocation-free, whereas previously every enqueue had some allocation cost (even if it was batched as part of allocating a segment).  However, some operations mark segments as being frozen, which then prevents subsequent enqueues into those segments; this is necessary for enumeration, but Count also caused this.  Ideally Count isn't used on hot paths, but if it is, this can end up in degenerate situations where, for example, if Count is called after every enqueue, we'll end up creating a new segment for every enqueued item, which is incredibly inefficient both for the enqueues and for Count, which is O(N) in the number of segments.

It turns out, though, that we were overly cautious in implementing Count, and we don't actually need it to freeze the segments.  Instead, when there are more than two segments (the case where we previously froze), we can take the cross-segment lock, which is the same lock that's held any time we update the head and tail segment pointers.  Once that lock is held, we know that the internal segments won't change, because code can only enqueue/dequeue from the head and tail segments, so any segment that's not head and tail while the lock is held is effectively immutable.  That means that we can simply walk those segments and add up their counts, safe in knowing they won't change while we're enumerating.

* Remove stale comment
src/System.Private.CoreLib/shared/System/Collections/Concurrent/ConcurrentQueue.cs