@@ -131,93 +131,99 @@ private bool GetOrDiscard(I item, out V value)
131
131
///<inheritdoc/>
132
132
public V GetOrAdd ( K key , Func < K , V > valueFactory )
133
133
{
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
+ }
138
140
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 ) ) ;
142
144
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
+ }
150
152
151
- if ( newItem . Value is IDisposable d )
152
- {
153
- d . Dispose ( ) ;
153
+ if ( newItem . Value is IDisposable d )
154
+ {
155
+ d . Dispose ( ) ;
156
+ }
154
157
}
155
-
156
- return this . GetOrAdd ( key , valueFactory ) ;
157
158
}
158
159
159
160
///<inheritdoc/>
160
161
public async Task < V > GetOrAddAsync ( K key , Func < K , Task < V > > valueFactory )
161
162
{
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
+ }
166
169
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 ) ) ;
170
173
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
+ }
178
181
179
- if ( newItem . Value is IDisposable d )
180
- {
181
- d . Dispose ( ) ;
182
+ if ( newItem . Value is IDisposable d )
183
+ {
184
+ d . Dispose ( ) ;
185
+ }
182
186
}
183
-
184
- return await this . GetOrAddAsync ( key , valueFactory ) . ConfigureAwait ( false ) ;
185
187
}
186
188
187
189
///<inheritdoc/>
188
190
public bool TryRemove ( K key )
189
191
{
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 ) )
197
195
{
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 ) )
206
201
{
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 )
208
210
{
209
- d . Dispose ( ) ;
211
+ if ( existing . Value is IDisposable d )
212
+ {
213
+ d . Dispose ( ) ;
214
+ }
210
215
}
216
+
217
+ return true ;
211
218
}
212
219
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 ;
214
225
}
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 ) ;
218
226
}
219
-
220
- return false ;
221
227
}
222
228
223
229
///<inheritdoc/>
@@ -250,25 +256,27 @@ public bool TryUpdate(K key, V value)
250
256
///<remarks>Note: Updates to existing items do not affect LRU order. Added items are at the top of the LRU.</remarks>
251
257
public void AddOrUpdate ( K key , V value )
252
258
{
253
- // first, try to update
254
- if ( this . TryUpdate ( key , value ) )
259
+ while ( true )
255
260
{
256
- return ;
257
- }
261
+ // first, try to update
262
+ if ( this . TryUpdate ( key , value ) )
263
+ {
264
+ return ;
265
+ }
258
266
259
- // then try add
260
- var newItem = this . policy . CreateItem ( key , value ) ;
267
+ // then try add
268
+ var newItem = this . policy . CreateItem ( key , value ) ;
261
269
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
+ }
269
277
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
+ }
272
280
}
273
281
274
282
///<inheritdoc/>
0 commit comments