Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the locking per key logic in the CachingService #187

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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");
}
}
}
}