Created
February 27, 2015 19:45
-
-
Save bbarry/3301a9277742c6a76236 to your computer and use it in GitHub Desktop.
Thread safe generic cache
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
public static class Cache<T> { | |
static readonly ConcurrentDictionary<string, T> Dictionary = new ConcurrentDictionary<string, T>(); | |
//by making this a tuple with the generic constraint, there will be one per cache type; the first parameter will always be the default value | |
static readonly ConcurrentDictionary<string, Tuple<T, DateTime, TimeSpan>> Removals = new ConcurrentDictionary<string, Tuple<T, DateTime, TimeSpan>>(); | |
/// <summary> | |
/// Gets an item out of the cache or adds it if it does not already exist. | |
/// </summary> | |
/// <param name="key">the cache item key</param> | |
/// <param name="creator">creation function, provides key as parameter</param> | |
/// <returns>the item from the cache</returns> | |
/// <remarks>Will not modify existing expiration details.</remarks> | |
public static T GetOrAdd(string key, Func<string, T> creator) { return GetOrAdd(key, creator, null, null); } | |
/// <summary> | |
/// Gets an item out of the cache or adds it if it does not already exist. | |
/// </summary> | |
/// <param name="key">the cache item key</param> | |
/// <param name="creator">creation function, provides key as parameter</param> | |
/// <param name="absoluteExpiration">Sets an absolute time when the item will expire. Retrieving the item without providing a new expiration will not modify this time.</param> | |
/// <returns>the item from the cache</returns> | |
public static T GetOrAdd(string key, Func<string, T> creator, DateTime absoluteExpiration) { return GetOrAdd(key, creator, absoluteExpiration, null); } | |
/// <summary> | |
/// Gets an item out of the cache or adds it if it does not already exist. | |
/// </summary> | |
/// <param name="key">the cache item key</param> | |
/// <param name="creator">creation function, provides key as parameter</param> | |
/// <param name="slidingExpiration">Sets a sliding window for when an item will be removed from the cache. Retrieving the item will reset the timespan here</param> | |
/// <returns>the item from the cache</returns> | |
public static T GetOrAdd(string key, Func<string, T> creator, TimeSpan slidingExpiration) { return GetOrAdd(key, creator, null, slidingExpiration); } | |
/// <summary> | |
/// Attempts to retrieve the item from the cache. | |
/// </summary> | |
/// <param name="key">the cache item key</param> | |
/// <param name="value">the value</param> | |
/// <returns><c>true</c> if the item exists; <c>false</c> otherwise</returns> | |
/// <remarks>Will update sliding expirations.</remarks> | |
public static bool TryGetValue(string key, out T value) { | |
Tuple<T, DateTime, TimeSpan> when; | |
if (Removals.TryGetValue(key, out when) && when.Item3 != TimeSpan.Zero) { | |
Remove(key, Tuple.Create(default(T), DateTime.Now.Add(when.Item3), when.Item3)); | |
} | |
return Dictionary.TryGetValue(key, out value); | |
} | |
/// <summary> | |
/// Will remove the item from the cache and return it if it was still there. | |
/// </summary> | |
/// <param name="key">the cache item key</param> | |
/// <param name="value">the value</param> | |
/// <returns><c>true</c> if the item was removed; <c>false</c> otherwise</returns> | |
public static bool Expire(string key, out T value) { return Dictionary.TryRemove(key, out value); } | |
/// <summary> | |
/// Will remove the item from the cache. | |
/// </summary> | |
/// <param name="key">the cache item key</param> | |
public static void Expire(string key) { | |
T value; | |
Dictionary.TryRemove(key, out value); | |
} | |
static T GetOrAdd(string key, Func<string, T> creator, DateTime? absoluteExpiration, TimeSpan? slidingExpiration) { | |
if (key == null) { | |
throw new ArgumentNullException("key"); | |
} | |
Tuple<T, DateTime, TimeSpan> when; | |
var updateRemoval = Removals.TryGetValue(key, out when) && when.Item3 != TimeSpan.Zero; | |
var v = Dictionary.GetOrAdd(key, creator); | |
if (absoluteExpiration == null && slidingExpiration == null && !updateRemoval) { | |
return v; | |
} | |
if (absoluteExpiration != null || slidingExpiration != null) { | |
var expiration = (TimeSpan)(slidingExpiration ?? (absoluteExpiration - DateTime.Now)); | |
when = Tuple.Create(default(T), DateTime.Now.Add(expiration), expiration); | |
} else { | |
when = Tuple.Create(default(T), DateTime.Now.Add(when.Item3), when.Item3); | |
} | |
if (absoluteExpiration != null) { | |
Removals.TryAdd(key, Tuple.Create(default(T), (DateTime)absoluteExpiration, TimeSpan.Zero)); | |
} else { | |
Removals.AddOrUpdate(key, when, (a, b) => when); | |
} | |
Remove(key, when); | |
return v; | |
} | |
static void Remove(string key, Tuple<T, DateTime, TimeSpan> then) { | |
System.Threading.Tasks.Task.Delay(then.Item3).ContinueWith(task => { | |
Tuple<T, DateTime, TimeSpan> when; | |
if (!Removals.TryGetValue(key, out when) || when.Item2 >= DateTime.Now) | |
return; | |
T v; | |
Dictionary.TryRemove(key, out v); | |
Removals.TryRemove(key, out when); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment