@@ -113,15 +113,9 @@ public bool TryGet(K key, out V value)
113113 return false ;
114114 }
115115
116- ///<inheritdoc/>
117- public V GetOrAdd ( K key , Func < K , V > valueFactory )
116+ private bool TryAdd ( K key , V value )
118117 {
119- if ( this . TryGet ( key , out var value ) )
120- {
121- return value ;
122- }
123-
124- var node = new LinkedListNode < LruItem > ( new LruItem ( key , valueFactory ( key ) ) ) ;
118+ var node = new LinkedListNode < LruItem > ( new LruItem ( key , value ) ) ;
125119
126120 if ( this . dictionary . TryAdd ( key , node ) )
127121 {
@@ -152,57 +146,101 @@ public V GetOrAdd(K key, Func<K, V> valueFactory)
152146 Disposer < V > . Dispose ( removed . Value . Value ) ;
153147 }
154148
155- return node . Value . Value ;
149+ return true ;
156150 }
157151
158- return this . GetOrAdd ( key , valueFactory ) ;
152+ return false ;
159153 }
160154
161155 ///<inheritdoc/>
162- public async ValueTask < V > GetOrAddAsync ( K key , Func < K , Task < V > > valueFactory )
156+ public V GetOrAdd ( K key , Func < K , V > valueFactory )
163157 {
164158 if ( this . TryGet ( key , out var value ) )
165159 {
166160 return value ;
167161 }
168162
169- var node = new LinkedListNode < LruItem > ( new LruItem ( key , await valueFactory ( key ) ) ) ;
163+ value = valueFactory ( key ) ;
170164
171- if ( this . dictionary . TryAdd ( key , node ) )
165+ if ( TryAdd ( key , value ) )
172166 {
173- LinkedListNode < LruItem > first = null ;
167+ return value ;
168+ }
174169
175- lock ( this . linkedList )
176- {
177- if ( linkedList . Count >= capacity )
178- {
179- first = linkedList . First ;
180- linkedList . RemoveFirst ( ) ;
181- }
170+ return this . GetOrAdd ( key , valueFactory ) ;
171+ }
182172
183- linkedList . AddLast ( node ) ;
184- }
173+ /// <summary>
174+ /// Adds a key/value pair to the cache if the key does not already exist. Returns the new value, or the
175+ /// existing value if the key already exists.
176+ /// </summary>
177+ /// <typeparam name="TArg">The type of an argument to pass into valueFactory.</typeparam>
178+ /// <param name="key">The key of the element to add.</param>
179+ /// <param name="valueFactory">The factory function used to generate a value for the key.</param>
180+ /// <param name="factoryArgument">An argument value to pass into valueFactory.</param>
181+ /// <returns>The value for the key. This will be either the existing value for the key if the key is already
182+ /// in the cache, or the new value if the key was not in the cache.</returns>
183+ public V GetOrAdd < TArg > ( K key , Func < K , TArg , V > valueFactory , TArg factoryArgument )
184+ {
185+ if ( this . TryGet ( key , out var value ) )
186+ {
187+ return value ;
188+ }
185189
186- // Remove from the dictionary outside the lock. This means that the dictionary at this moment
187- // contains an item that is not in the linked list. If another thread fetches this item,
188- // LockAndMoveToEnd will ignore it, since it is detached. This means we potentially 'lose' an
189- // item just as it was about to move to the back of the LRU list and be preserved. The next request
190- // for the same key will be a miss. Dictionary and list are eventually consistent.
191- // However, all operations inside the lock are extremely fast, so contention is minimized.
192- if ( first != null )
193- {
194- dictionary . TryRemove ( first . Value . Key , out var removed ) ;
190+ value = valueFactory ( key , factoryArgument ) ;
195191
196- Interlocked . Increment ( ref this . metrics . evictedCount ) ;
197- Disposer < V > . Dispose ( removed . Value . Value ) ;
198- }
192+ if ( TryAdd ( key , value ) )
193+ {
194+ return value ;
195+ }
196+
197+ return this . GetOrAdd ( key , valueFactory , factoryArgument ) ;
198+ }
199+
200+ ///<inheritdoc/>
201+ public async ValueTask < V > GetOrAddAsync ( K key , Func < K , Task < V > > valueFactory )
202+ {
203+ if ( this . TryGet ( key , out var value ) )
204+ {
205+ return value ;
206+ }
207+
208+ value = await valueFactory ( key ) ;
199209
200- return node . Value . Value ;
210+ if ( TryAdd ( key , value ) )
211+ {
212+ return value ;
201213 }
202214
203215 return await this . GetOrAddAsync ( key , valueFactory ) ;
204216 }
205217
218+ /// <summary>
219+ /// Adds a key/value pair to the cache if the key does not already exist. Returns the new value, or the
220+ /// existing value if the key already exists.
221+ /// </summary>
222+ /// <typeparam name="TArg">The type of an argument to pass into valueFactory.</typeparam>
223+ /// <param name="key">The key of the element to add.</param>
224+ /// <param name="valueFactory">The factory function used to asynchronously generate a value for the key.</param>
225+ /// <param name="factoryArgument">An argument value to pass into valueFactory.</param>
226+ /// <returns>A task that represents the asynchronous GetOrAdd operation.</returns>
227+ public async ValueTask < V > GetOrAddAsync < TArg > ( K key , Func < K , TArg , Task < V > > valueFactory , TArg factoryArgument )
228+ {
229+ if ( this . TryGet ( key , out var value ) )
230+ {
231+ return value ;
232+ }
233+
234+ value = await valueFactory ( key , factoryArgument ) ;
235+
236+ if ( TryAdd ( key , value ) )
237+ {
238+ return value ;
239+ }
240+
241+ return await this . GetOrAddAsync ( key , valueFactory , factoryArgument ) ;
242+ }
243+
206244 ///<inheritdoc/>
207245 public bool TryRemove ( K key )
208246 {
0 commit comments