@@ -259,6 +259,30 @@ public void AddOrUpdate(K key, V value)
259259
260260 // if both update and add failed there was a race, try again
261261 AddOrUpdate ( key , value ) ;
262+ }
263+
264+ ///<inheritdoc/>
265+ public void Clear ( )
266+ {
267+ // take a key snapshot
268+ var keys = this . dictionary . Keys . ToList ( ) ;
269+
270+ // remove all keys in the snapshot - this correctly handles disposable values
271+ foreach ( var key in keys )
272+ {
273+ TryRemove ( key ) ;
274+ }
275+
276+ // At this point, dictionary is empty but queues still hold references to all values.
277+ // Cycle the queues to purge all refs. If any items were added during this process,
278+ // it is possible they might be removed as part of CycleCold. However, the dictionary
279+ // and queues will remain in a consistent state.
280+ for ( int i = 0 ; i < keys . Count ; i ++ )
281+ {
282+ CycleHotUnchecked ( ) ;
283+ CycleWarmUnchecked ( ) ;
284+ CycleColdUnchecked ( ) ;
285+ }
262286 }
263287
264288 private void Cycle ( )
@@ -282,72 +306,90 @@ private void CycleHot()
282306 {
283307 if ( this . hotCount > this . hotCapacity )
284308 {
285- Interlocked . Decrement ( ref this . hotCount ) ;
309+ CycleHotUnchecked ( ) ;
310+ }
311+ }
286312
287- if ( this . hotQueue . TryDequeue ( out var item ) )
288- {
289- var where = this . policy . RouteHot ( item ) ;
290- this . Move ( item , where ) ;
291- }
292- else
293- {
294- Interlocked . Increment ( ref this . hotCount ) ;
295- }
313+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
314+ private void CycleHotUnchecked ( )
315+ {
316+ Interlocked . Decrement ( ref this . hotCount ) ;
317+
318+ if ( this . hotQueue . TryDequeue ( out var item ) )
319+ {
320+ var where = this . policy . RouteHot ( item ) ;
321+ this . Move ( item , where ) ;
322+ }
323+ else
324+ {
325+ Interlocked . Increment ( ref this . hotCount ) ;
296326 }
297327 }
298328
299329 private void CycleWarm ( )
300330 {
301331 if ( this . warmCount > this . warmCapacity )
302332 {
303- Interlocked . Decrement ( ref this . warmCount ) ;
304-
305- if ( this . warmQueue . TryDequeue ( out var item ) )
306- {
307- var where = this . policy . RouteWarm ( item ) ;
333+ CycleWarmUnchecked ( ) ;
334+ }
335+ }
308336
309- // When the warm queue is full, we allow an overflow of 1 item before redirecting warm items to cold.
310- // This only happens when hit rate is high, in which case we can consider all items relatively equal in
311- // terms of which was least recently used.
312- if ( where == ItemDestination . Warm && this . warmCount <= this . warmCapacity )
313- {
314- this . Move ( item , where ) ;
315- }
316- else
317- {
318- this . Move ( item , ItemDestination . Cold ) ;
319- }
320- }
321- else
322- {
323- Interlocked . Increment ( ref this . warmCount ) ;
324- }
337+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
338+ private void CycleWarmUnchecked ( )
339+ {
340+ Interlocked . Decrement ( ref this . warmCount ) ;
341+
342+ if ( this . warmQueue . TryDequeue ( out var item ) )
343+ {
344+ var where = this . policy . RouteWarm ( item ) ;
345+
346+ // When the warm queue is full, we allow an overflow of 1 item before redirecting warm items to cold.
347+ // This only happens when hit rate is high, in which case we can consider all items relatively equal in
348+ // terms of which was least recently used.
349+ if ( where == ItemDestination . Warm && this . warmCount <= this . warmCapacity )
350+ {
351+ this . Move ( item , where ) ;
352+ }
353+ else
354+ {
355+ this . Move ( item , ItemDestination . Cold ) ;
356+ }
357+ }
358+ else
359+ {
360+ Interlocked . Increment ( ref this . warmCount ) ;
325361 }
326362 }
327363
328364 private void CycleCold ( )
329365 {
330366 if ( this . coldCount > this . coldCapacity )
331367 {
332- Interlocked . Decrement ( ref this . coldCount ) ;
333-
334- if ( this . coldQueue . TryDequeue ( out var item ) )
335- {
336- var where = this . policy . RouteCold ( item ) ;
368+ CycleColdUnchecked ( ) ;
369+ }
370+ }
337371
338- if ( where == ItemDestination . Warm && this . warmCount <= this . warmCapacity )
339- {
340- this . Move ( item , where ) ;
341- }
342- else
343- {
344- this . Move ( item , ItemDestination . Remove ) ;
345- }
346- }
347- else
348- {
349- Interlocked . Increment ( ref this . coldCount ) ;
350- }
372+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
373+ private void CycleColdUnchecked ( )
374+ {
375+ Interlocked . Decrement ( ref this . coldCount ) ;
376+
377+ if ( this . coldQueue . TryDequeue ( out var item ) )
378+ {
379+ var where = this . policy . RouteCold ( item ) ;
380+
381+ if ( where == ItemDestination . Warm && this . warmCount <= this . warmCapacity )
382+ {
383+ this . Move ( item , where ) ;
384+ }
385+ else
386+ {
387+ this . Move ( item , ItemDestination . Remove ) ;
388+ }
389+ }
390+ else
391+ {
392+ Interlocked . Increment ( ref this . coldCount ) ;
351393 }
352394 }
353395
0 commit comments