@@ -131,93 +131,99 @@ private bool GetOrDiscard(I item, out V value)
131131 ///<inheritdoc/>
132132 public V GetOrAdd ( K key , Func < K , V > valueFactory )
133133 {
134- if ( this . TryGet ( key , out var value ) )
135- {
136- return value ;
137- }
134+ while ( true )
135+ {
136+ if ( this . TryGet ( key , out var value ) )
137+ {
138+ return value ;
139+ }
138140
139- // The value factory may be called concurrently for the same key, but the first write to the dictionary wins.
140- // This is identical logic in ConcurrentDictionary.GetOrAdd method.
141- var newItem = this . policy . CreateItem ( key , valueFactory ( key ) ) ;
141+ // The value factory may be called concurrently for the same key, but the first write to the dictionary wins.
142+ // This is identical logic in ConcurrentDictionary.GetOrAdd method.
143+ var newItem = this . policy . CreateItem ( key , valueFactory ( key ) ) ;
142144
143- if ( this . dictionary . TryAdd ( key , newItem ) )
144- {
145- this . hotQueue . Enqueue ( newItem ) ;
146- Interlocked . Increment ( ref hotCount ) ;
147- Cycle ( ) ;
148- return newItem . Value ;
149- }
145+ if ( this . dictionary . TryAdd ( key , newItem ) )
146+ {
147+ this . hotQueue . Enqueue ( newItem ) ;
148+ Interlocked . Increment ( ref hotCount ) ;
149+ Cycle ( ) ;
150+ return newItem . Value ;
151+ }
150152
151- if ( newItem . Value is IDisposable d )
152- {
153- d . Dispose ( ) ;
153+ if ( newItem . Value is IDisposable d )
154+ {
155+ d . Dispose ( ) ;
156+ }
154157 }
155-
156- return this . GetOrAdd ( key , valueFactory ) ;
157158 }
158159
159160 ///<inheritdoc/>
160161 public async Task < V > GetOrAddAsync ( K key , Func < K , Task < V > > valueFactory )
161162 {
162- if ( this . TryGet ( key , out var value ) )
163- {
164- return value ;
165- }
163+ while ( true )
164+ {
165+ if ( this . TryGet ( key , out var value ) )
166+ {
167+ return value ;
168+ }
166169
167- // The value factory may be called concurrently for the same key, but the first write to the dictionary wins.
168- // This is identical logic in ConcurrentDictionary.GetOrAdd method.
169- var newItem = this . policy . CreateItem ( key , await valueFactory ( key ) . ConfigureAwait ( false ) ) ;
170+ // The value factory may be called concurrently for the same key, but the first write to the dictionary wins.
171+ // This is identical logic in ConcurrentDictionary.GetOrAdd method.
172+ var newItem = this . policy . CreateItem ( key , await valueFactory ( key ) . ConfigureAwait ( false ) ) ;
170173
171- if ( this . dictionary . TryAdd ( key , newItem ) )
172- {
173- this . hotQueue . Enqueue ( newItem ) ;
174- Interlocked . Increment ( ref hotCount ) ;
175- Cycle ( ) ;
176- return newItem . Value ;
177- }
174+ if ( this . dictionary . TryAdd ( key , newItem ) )
175+ {
176+ this . hotQueue . Enqueue ( newItem ) ;
177+ Interlocked . Increment ( ref hotCount ) ;
178+ Cycle ( ) ;
179+ return newItem . Value ;
180+ }
178181
179- if ( newItem . Value is IDisposable d )
180- {
181- d . Dispose ( ) ;
182+ if ( newItem . Value is IDisposable d )
183+ {
184+ d . Dispose ( ) ;
185+ }
182186 }
183-
184- return await this . GetOrAddAsync ( key , valueFactory ) . ConfigureAwait ( false ) ;
185187 }
186188
187189 ///<inheritdoc/>
188190 public bool TryRemove ( K key )
189191 {
190- if ( this . dictionary . TryGetValue ( key , out var existing ) )
191- {
192- var kvp = new KeyValuePair < K , I > ( key , existing ) ;
193-
194- // hidden atomic remove
195- // https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/
196- if ( ( ( ICollection < KeyValuePair < K , I > > ) this . dictionary ) . Remove ( kvp ) )
192+ while ( true )
193+ {
194+ if ( this . dictionary . TryGetValue ( key , out var existing ) )
197195 {
198- // Mark as not accessed, it will later be cycled out of the queues because it can never be fetched
199- // from the dictionary. Note: Hot/Warm/Cold count will reflect the removed item until it is cycled
200- // from the queue.
201- existing . WasAccessed = false ;
202- existing . WasRemoved = true ;
203-
204- // serialize dispose (common case dispose not thread safe)
205- lock ( existing )
196+ var kvp = new KeyValuePair < K , I > ( key , existing ) ;
197+
198+ // hidden atomic remove
199+ // https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/
200+ if ( ( ( ICollection < KeyValuePair < K , I > > ) this . dictionary ) . Remove ( kvp ) )
206201 {
207- if ( existing . Value is IDisposable d )
202+ // Mark as not accessed, it will later be cycled out of the queues because it can never be fetched
203+ // from the dictionary. Note: Hot/Warm/Cold count will reflect the removed item until it is cycled
204+ // from the queue.
205+ existing . WasAccessed = false ;
206+ existing . WasRemoved = true ;
207+
208+ // serialize dispose (common case dispose not thread safe)
209+ lock ( existing )
208210 {
209- d . Dispose ( ) ;
211+ if ( existing . Value is IDisposable d )
212+ {
213+ d . Dispose ( ) ;
214+ }
210215 }
216+
217+ return true ;
211218 }
212219
213- return true ;
220+ // it existed, but we couldn't remove - this means value was replaced afer the TryGetValue (a race), try again
221+ }
222+ else
223+ {
224+ return false ;
214225 }
215-
216- // it existed, but we couldn't remove - this means value was replaced afer the TryGetValue (a race), try again
217- return TryRemove ( key ) ;
218226 }
219-
220- return false ;
221227 }
222228
223229 ///<inheritdoc/>
@@ -250,25 +256,27 @@ public bool TryUpdate(K key, V value)
250256 ///<remarks>Note: Updates to existing items do not affect LRU order. Added items are at the top of the LRU.</remarks>
251257 public void AddOrUpdate ( K key , V value )
252258 {
253- // first, try to update
254- if ( this . TryUpdate ( key , value ) )
259+ while ( true )
255260 {
256- return ;
257- }
261+ // first, try to update
262+ if ( this . TryUpdate ( key , value ) )
263+ {
264+ return ;
265+ }
258266
259- // then try add
260- var newItem = this . policy . CreateItem ( key , value ) ;
267+ // then try add
268+ var newItem = this . policy . CreateItem ( key , value ) ;
261269
262- if ( this . dictionary . TryAdd ( key , newItem ) )
263- {
264- this . hotQueue . Enqueue ( newItem ) ;
265- Interlocked . Increment ( ref hotCount ) ;
266- Cycle ( ) ;
267- return ;
268- }
270+ if ( this . dictionary . TryAdd ( key , newItem ) )
271+ {
272+ this . hotQueue . Enqueue ( newItem ) ;
273+ Interlocked . Increment ( ref hotCount ) ;
274+ Cycle ( ) ;
275+ return ;
276+ }
269277
270- // if both update and add failed there was a race, try again
271- AddOrUpdate ( key , value ) ;
278+ // if both update and add failed there was a race, try again
279+ }
272280 }
273281
274282 ///<inheritdoc/>
0 commit comments