Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 12 additions & 35 deletions LazyCache/CachingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class CachingService : IAppCache
{
private readonly Lazy<ICacheProvider> cacheProvider;

private readonly int[] keyLocks;
private readonly object[] keyLocks;

public CachingService() : this(DefaultCacheProvider)
{
Expand All @@ -22,16 +22,17 @@ public CachingService(Lazy<ICacheProvider> cacheProvider)
{
this.cacheProvider = cacheProvider ?? throw new ArgumentNullException(nameof(cacheProvider));
var lockCount = Math.Max(Environment.ProcessorCount * 8, 32);
keyLocks = new int[lockCount];
keyLocks = new object[lockCount];
for (i = 0; i < keyLocks.Length; i++) keyLocks[i] = new object();
}

public CachingService(Func<ICacheProvider> cacheProviderFactory)
{
if (cacheProviderFactory == null) throw new ArgumentNullException(nameof(cacheProviderFactory));
cacheProvider = new Lazy<ICacheProvider>(cacheProviderFactory);
var lockCount = Math.Max(Environment.ProcessorCount * 8, 32);
keyLocks = new int[lockCount];

keyLocks = new object[lockCount];
for (i = 0; i < keyLocks.Length; i++) keyLocks[i] = new object();
}

public CachingService(ICacheProvider cache) : this(() => cache)
Expand Down Expand Up @@ -117,16 +118,10 @@ object CacheFactory(ICacheEntry entry) =>

// acquire lock per key
uint hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
while (Interlocked.CompareExchange(ref keyLocks[hash], 1, 0) == 1) { Thread.Yield(); }

try
lock (keyLocks[hash])
{
cacheItem = CacheProvider.GetOrCreate<object>(key, policy, CacheFactory);
}
finally
{
keyLocks[hash] = 0;
}

try
{
Expand All @@ -139,16 +134,10 @@ object CacheFactory(ICacheEntry entry) =>

// acquire lock again
hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
while (Interlocked.CompareExchange(ref keyLocks[hash], 1, 0) == 1) { Thread.Yield(); }

try
lock (keyLocks[hash])
{
cacheItem = CacheProvider.GetOrCreate<object>(key, CacheFactory);
}
finally
{
keyLocks[hash] = 0;
}
result = GetValueFromLazy<T>(cacheItem, out _ /* we just evicted so type change cannot happen this time */);
}

Expand Down Expand Up @@ -195,10 +184,6 @@ public virtual async Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task
// below, and guarded using the async lazy. Here we just ensure only one thread can place
// the AsyncLazy into the cache at one time

// acquire lock
uint hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
while (Interlocked.CompareExchange(ref keyLocks[hash], 1, 0) == 1) { Thread.Yield(); }

object CacheFactory(ICacheEntry entry) =>
new AsyncLazy<T>(async () =>
{
Expand All @@ -208,14 +193,12 @@ object CacheFactory(ICacheEntry entry) =>
return result;
});

try
// acquire lock
uint hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
lock (keyLocks[hash])
{
cacheItem = CacheProvider.GetOrCreate<object>(key, policy, CacheFactory);
}
finally
{
keyLocks[hash] = 0;
}

try
{
Expand All @@ -228,16 +211,10 @@ object CacheFactory(ICacheEntry entry) =>

// acquire lock
hash = (uint)key.GetHashCode() % (uint)keyLocks.Length;
while (Interlocked.CompareExchange(ref keyLocks[hash], 1, 0) == 1) { Thread.Yield(); }

try
lock (keyLocks[hash])
{
cacheItem = CacheProvider.GetOrCreate<object>(key, CacheFactory);
}
finally
{
keyLocks[hash] = 0;
}
result = GetValueFromAsyncLazy<T>(cacheItem, out _ /* we just evicted so type change cannot happen this time */);
}

Expand Down Expand Up @@ -341,4 +318,4 @@ protected virtual void ValidateKey(string key)
throw new ArgumentOutOfRangeException(nameof(key), "Cache keys cannot be empty or whitespace");
}
}
}
}