@@ -59,20 +59,32 @@ private void Release(TKey key)
5959 {
6060 while ( true )
6161 {
62- var oldRefCount = this . cache [ key ] ;
63- var newRefCount = oldRefCount . DecrementCopy ( ) ;
64- if ( this . cache . TryUpdate ( key , newRefCount , oldRefCount ) )
62+ if ( ! this . cache . TryGetValue ( key , out var oldRefCount ) )
6563 {
66- if ( newRefCount . Count == 0 )
64+ // already released, exit
65+ break ;
66+ }
67+
68+ // if count is 1, the caller's decrement makes refcount 0: it is unreferenced and eligible to remove
69+ if ( oldRefCount . Count == 1 )
70+ {
71+ var kvp = new KeyValuePair < TKey , ReferenceCount < TValue > > ( key , oldRefCount ) ;
72+
73+ // hidden atomic remove
74+ // https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/
75+ if ( ( ( ICollection < KeyValuePair < TKey , ReferenceCount < TValue > > > ) this . cache ) . Remove ( kvp ) )
6776 {
68- if ( ( ( IDictionary < TKey , ReferenceCount < TValue > > ) this . cache ) . Remove ( new KeyValuePair < TKey , ReferenceCount < TValue > > ( key , newRefCount ) ) )
77+ // no longer in cache, safe to dispose and exit
78+ if ( oldRefCount . Value is IDisposable d )
6979 {
70- if ( newRefCount . Value is IDisposable d )
71- {
72- d . Dispose ( ) ;
73- }
80+ d . Dispose ( ) ;
7481 }
82+ break ;
7583 }
84+ }
85+ else if ( this . cache . TryUpdate ( key , oldRefCount . DecrementCopy ( ) , oldRefCount ) )
86+ {
87+ // replaced with decremented copy, exit
7688 break ;
7789 }
7890 }
0 commit comments