diff --git a/R5.Internals/R5.Internals.Caching/Caches/AsyncLazyCache.cs b/R5.Internals/R5.Internals.Caching/Caches/AsyncLazyCache.cs index 747101a..c1fb70d 100644 --- a/R5.Internals/R5.Internals.Caching/Caches/AsyncLazyCache.cs +++ b/R5.Internals/R5.Internals.Caching/Caches/AsyncLazyCache.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Caching.Memory; +using AsyncKeyedLock; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using System; @@ -19,7 +20,11 @@ public interface IAsyncLazyCache : IDisposable public class AsyncLazyCache : IAsyncLazyCache { private readonly IMemoryCache _cache; - private static readonly KeyedLocks _locks = new KeyedLocks(); + private static readonly AsyncKeyedLocker _locks = new AsyncKeyedLocker(o => + { + o.PoolSize = 20; + o.PoolInitialFill = 1; + }); public AsyncLazyCache(IMemoryCache cache) { @@ -42,7 +47,7 @@ public async Task GetOrCreateAsync(string key, Func> factoryTask) return value; } - using (await _locks.LockAsync(key)) + using (await _locks.LockAsync(key).ConfigureAwait(false)) { if (!_cache.TryGetValue(key, out _)) { @@ -102,98 +107,6 @@ public void Dispose() { _cache?.Dispose(); } - - // internal abstraction to manage the creating/releasing of keyed semaphores - private sealed class KeyedLocks - { - private static readonly Dictionary _locks - = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public IDisposable Lock(string key) - { - AcquireLock(key).Wait(); - return new LockReleaser(key); - } - - public async Task LockAsync(string key) - { - await AcquireLock(key).WaitAsync().ConfigureAwait(false); - return new LockReleaser(key); - } - - // Returns the semaphore associated to the key. Either creates - // a new one and adds to the map, or returns the already existing - // one (additionally incrementing its referenced count) - private SemaphoreSlim AcquireLock(string key) - { - CountedLock exclusiveLock; - lock (_locks) - { - if (_locks.TryGetValue(key, out exclusiveLock)) - { - exclusiveLock.Increment(); - } - else - { - exclusiveLock = new CountedLock(); - _locks[key] = exclusiveLock; - } - } - - return exclusiveLock.Lock; - } - - // Wrapper around a semaphore - keeps track of the count the key was referenced - private sealed class CountedLock - { - public SemaphoreSlim Lock { get; } = new SemaphoreSlim(1, 1); - private int _count { get; set; } = 1; - - public void Increment() - { - _count++; - } - - public void Decrement() - { - _count--; - } - - public bool NotReferenced() - { - return _count == 0; - } - } - - // IDisposable type returned after acquiring a lock. It will handle removing the - // semaphore from the map if releasing results in no more references to the key. - private sealed class LockReleaser : IDisposable - { - private string _key { get; } - - public LockReleaser(string key) - { - _key = key; - } - - public void Dispose() - { - CountedLock exclusiveLock; - lock (_locks) - { - exclusiveLock = _locks[_key]; - - exclusiveLock.Decrement(); - if (!exclusiveLock.NotReferenced()) - { - _locks.Remove(_key); - } - } - - exclusiveLock.Lock.Release(); - } - } - } } public static class ServiceCollectionExtensions diff --git a/R5.Internals/R5.Internals.Caching/R5.Internals.Caching.csproj b/R5.Internals/R5.Internals.Caching/R5.Internals.Caching.csproj index b0042e5..e2afdff 100644 --- a/R5.Internals/R5.Internals.Caching/R5.Internals.Caching.csproj +++ b/R5.Internals/R5.Internals.Caching/R5.Internals.Caching.csproj @@ -6,6 +6,7 @@ +