using Microsoft.Extensions.Caching.Memory;
|
using System;
|
using System.Threading;
|
|
namespace IStation.ChEr
|
{
|
internal sealed partial class SharedMemoryCache
|
{
|
/// <summary>
|
/// Set a cache item by key, function and optional eviction
|
/// </summary>
|
/// <param name="key">A unique identifier for the cache entry to insert</param>
|
/// <param name="valueFunction">A function to execute to get the data for the cache entry</param>
|
/// <param name="policy">(Optional) An object that contains eviction details for the cache entry</param>
|
public void Set(string key, Func<object> valueFunction, int? seconds)
|
{
|
if (key == null || valueFunction == null) { return; }
|
|
Exception ex_func = null;
|
|
this._cacheKeysBeingHandled.TryAdd(key, new CacheKeyBeingHandled());
|
|
lock (this._cacheKeysBeingHandled[key].SetOperation.CounterLock)
|
{
|
this._cacheKeysBeingHandled[key].SetOperation.Counter++;
|
}
|
|
lock (this._cacheKeysBeingHandled[key].SetOperation.Lock)
|
{
|
if (this._cacheKeysBeingHandled[key].SetOperation.Thread != null)
|
{
|
try
|
{
|
this._cacheKeysBeingHandled[key].SetOperation.Thread.Interrupt();
|
}
|
finally
|
{
|
this._cacheKeysBeingHandled[key].SetOperation.Thread = null;
|
}
|
}
|
}
|
|
lock (_cacheKeysBeingHandled[key].SetOperation.CounterLock)
|
{
|
if (this._cacheKeysBeingHandled[key].SetOperation.Counter > 1)
|
{
|
this._cacheKeysBeingHandled[key].SetOperation.Counter--;
|
}
|
}
|
|
if (this._cacheKeysBeingHandled[key].SetOperation.Counter > 1)
|
{
|
return; // it's not the most recent thread, so ignore it
|
}
|
|
lock (_cacheKeysBeingHandled[key].SetOperation.Lock)
|
{
|
if (this._cacheKeysBeingHandled[key].SetOperation.Thread == null)
|
{
|
this._cacheKeysBeingHandled[key].SetOperation.Thread = new Thread(() =>
|
{
|
try
|
{
|
object value = valueFunction();
|
|
((IMemoryCacheDirect)this).Set(key, value, seconds);
|
}
|
catch (ThreadAbortException)
|
{
|
}
|
catch (Exception ex)
|
{
|
ex_func = ex;
|
}
|
finally
|
{
|
}
|
});
|
|
this._cacheKeysBeingHandled[key].SetOperation.Thread.Start();
|
this._cacheKeysBeingHandled[key].SetOperation.Thread.Join();
|
}
|
}
|
if (ex_func != null)
|
throw ex_func;
|
}
|
|
/// <summary>
|
/// Set a cache item by key, value and optional eviction
|
/// </summary>
|
/// <param name="key">A unique identifier for the cache entry to insert</param>
|
/// <param name="value">The data for the cache entry</param>
|
/// <param name="policy">(Optional) An object that contains eviction details for the cache item</param>
|
public void Set(string key, object value, int? seconds)
|
{
|
if (key == null) { return; }
|
|
if (value != null)
|
{
|
((IMemoryCacheDirect)this).Set(key, value, seconds);
|
|
// if there's currently a function opterating on this key
|
if (this._cacheKeysBeingHandled.TryGetValue(key, out CacheKeyBeingHandled cacheKeyBeingHandled))
|
{
|
cacheKeyBeingHandled.SetOperation.Thread?.Interrupt();
|
|
// if it wants the same type, then cancel it and give it this value
|
if (cacheKeyBeingHandled.GetSetOperation.Type == value.GetType())
|
{
|
cacheKeyBeingHandled.GetSetOperation.Value = value;
|
cacheKeyBeingHandled.GetSetOperation.Thread.Interrupt();
|
}
|
}
|
}
|
else
|
{
|
if (this._isWiping) { return; }
|
|
this.Remove(key);
|
}
|
}
|
|
/// <summary>
|
/// The core method that directly sets a value in the wrapped memory cache
|
/// </summary>
|
/// <param name="key">key of cache item to set</param>
|
/// <param name="value">The data for the cache entry</param>
|
/// <param name="policy">(Optional) An object that contains eviction details for the cache entry</param>
|
void IMemoryCacheDirect.Set(string key, object value, int? seconds)
|
{
|
var set = new Action(() =>
|
{
|
|
_isSetting = true;
|
|
try
|
{
|
if (seconds != null)
|
{
|
_memoryCache.Set(key, value, TimeSpan.FromSeconds(seconds.Value));
|
}
|
else
|
{
|
_memoryCache.Set(key, value);
|
}
|
}
|
finally
|
{
|
_isSetting = false;
|
}
|
|
});
|
|
if (!_isWiping)
|
{
|
set();
|
}
|
else // hold the set until wiping is complete
|
{
|
lock (_setLock)
|
{
|
SpinWait.SpinUntil(() => !_isWiping); // one thread watching until wipe complete
|
|
set();
|
}
|
}
|
}
|
}
|
}
|