using System;
using System.Threading.Tasks;
namespace System.Collections.Concurrent
{
public static class ConcurrentDictionaryExtensions
{
///
/// Provides an alternative to that disposes values that implement .
///
///
///
///
///
///
///
public static TValue GetOrAddWithDispose(
this ConcurrentDictionary dictionary,
TKey key,
Func valueFactory) where TValue : IDisposable
{
while (true)
{
if (dictionary.TryGetValue(key, out var value))
{
// Try to get the value
return value;
}
/// Try to add the value
value = valueFactory(key);
if (dictionary.TryAdd(key, value))
{
// Won the race, so return the instance
return value;
}
// Lost the race, dispose the created object
value.Dispose();
}
}
///
/// Provides an alternative to specifically for asynchronous values. The factory method will only run once.
///
///
///
///
///
///
///
public static async Task GetOrAddAsync(
this ConcurrentDictionary> dictionary,
TKey key,
Func> valueFactory)
{
while (true)
{
if (dictionary.TryGetValue(key, out var task))
{
return await task;
}
// This is the task that we'll return to all waiters. We'll complete it when the factory is complete
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
if (dictionary.TryAdd(key, tcs.Task))
{
try
{
var value = await valueFactory(key);
tcs.TrySetResult(value);
return await tcs.Task;
}
catch (Exception ex)
{
// Make sure all waiters see the exception
tcs.SetException(ex);
// We remove the entry if the factory failed so it's not a permanent failure
// and future gets can retry (this could be a pluggable policy)
dictionary.TryRemove(key, out _);
throw;
}
}
}
}
}
}