Last active
April 29, 2019 01:46
-
-
Save jelical/c424003efe008435b49fd54a3d1bd5b0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace Microsoft.Extensions.Caching.Memory | |
{ | |
/// <summary> | |
/// Thread safe version of GetOrCreate(Async) using Interlocked | |
/// </summary> | |
public static class InterlockedCacheExtensions | |
{ | |
private class Lock | |
{ | |
public long Value; | |
public Exception Exception; | |
} | |
private static readonly ConcurrentDictionary<ValueTuple<object, IMemoryCache>, Lock> Interlocks = | |
new ConcurrentDictionary<ValueTuple<object, IMemoryCache>, Lock>(); | |
public static TItem GetOrCreateInterlocked<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factoryMethod) | |
{ | |
return cache.GetOrCreateInterlockedAsync(key, (a) => Task.FromResult(factoryMethod(a))).Result; | |
} | |
public static async Task<TItem> GetOrCreateInterlockedAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factoryMethod) | |
{ | |
if (!cache.TryGetValue(key, out object result)) | |
{ | |
var interlock = Interlocks.GetOrAdd((key, cache), (_) => new Lock()); | |
if (Interlocked.CompareExchange(ref interlock.Value, 0x1, 0x0) == 0x0) | |
{ | |
try | |
{ | |
if (!cache.TryGetValue(key, out result)) | |
{ | |
var entry = cache.CreateEntry(key); | |
entry.SetValue(result = await factoryMethod(entry)).Dispose(); | |
} | |
} | |
catch (Exception e) | |
{ | |
interlock.Exception = e; | |
} | |
finally | |
{ | |
Interlocks.TryRemove((key, cache), out Lock oldAsync); | |
Interlocked.Exchange(ref interlock.Value, 0x0); | |
} | |
} | |
else | |
{ | |
SpinWait.SpinUntil(() => cache.TryGetValue(key, out result) || Interlocked.Read(ref interlock.Value) != 0x1); | |
if (interlock.Exception != null) | |
throw interlock.Exception; | |
} | |
} | |
return (TItem)result; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment