2021-09-27 12:42:46 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
namespace Wabbajack.Common;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Represents a cache where values are created on-the-fly when they are found missing.
|
|
|
|
/// Creating a value locks the cache entry so that each key/value pair is only created once.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TKey"></typeparam>
|
|
|
|
/// <typeparam name="TVal"></typeparam>
|
|
|
|
public class LazyCache<TKey, TArg, TVal>
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
private readonly ConcurrentDictionary<TKey, AsyncLazy<TVal>> _data;
|
|
|
|
private readonly Func<TArg, TKey> _selector;
|
|
|
|
private readonly Func<TArg, Task<TVal>> _valueFactory;
|
|
|
|
|
|
|
|
public LazyCache(Func<TArg, TKey> selector, Func<TArg, Task<TVal>> valueFactory)
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
_selector = selector;
|
|
|
|
_valueFactory = valueFactory;
|
|
|
|
_data = new ConcurrentDictionary<TKey, AsyncLazy<TVal>>();
|
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
public async ValueTask<TVal> Get(TArg lookup)
|
|
|
|
{
|
|
|
|
var key = _selector(lookup);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
while (true)
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
if (_data.TryGetValue(key, out var found))
|
|
|
|
return await found.Value;
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
var value = new AsyncLazy<TVal>(() => _valueFactory(lookup));
|
|
|
|
if (_data.TryAdd(key, value))
|
|
|
|
return await value.Value;
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|