using System;
using Microsoft.Extensions.Caching.Memory;
using System.Threading;
namespace IStation
{
internal sealed partial class SharedMemoryCache
{
///
/// Attempts to get cache value of type T, otherwise sets a cache item by key, function and optional eviction
///
///
/// A unique identifier for the cache entry to insert
/// A function to execute to get the value for the cache entry
/// (Optional) An object that contains eviction details for the cache entry
/// An object of type T, or default T
public T GetSet(string key, Func valueFunction, int? seconds)
{
bool found;
T value = this.Get(key, out found);
Exception ex_func = null;
if (!found)
{
this._cacheKeysBeingHandled.TryAdd(key, new CacheKeyBeingHandled());
lock (this._cacheKeysBeingHandled[key].GetSetOperation.Lock)
{
// re-check to see if another thread beat us to setting this value
value = this.Get(key, out found);
if (!found)
{
// set, so function can be cancelled if a direct set of the same type occurs
this._cacheKeysBeingHandled[key].GetSetOperation.Type = typeof(T);
// put the function into it's own thread (so it can be cancelled)
this._cacheKeysBeingHandled[key].GetSetOperation.Thread = new Thread(() =>
{
var aborted = false;
var success = false;
try
{
value = valueFunction();
success = true;
}
catch (ThreadAbortException)
{
aborted = true;
}
#region 用于处理valueFunction引发异常
catch (Exception ex)
{
aborted = true;
ex_func = ex;
}
#endregion
finally
{
if (aborted)
{
value = (T)this._cacheKeysBeingHandled[key].GetSetOperation.Value; // cast should always be valid as this method is the only thing that sets it
}
else if (success)
{
if (value == null)
{
if (!this._isWiping)
{
((IMemoryCacheDirect)this).Remove(key);
}
}
else
{
((IMemoryCacheDirect)this).Set(key, value, seconds);
}
}
}
});
this._cacheKeysBeingHandled[key].GetSetOperation.Thread.Start();
this._cacheKeysBeingHandled[key].GetSetOperation.Thread.Join();
}
}
this._cacheKeysBeingHandled.TryRemove(key, out CacheKeyBeingHandled cacheKeyBeingHandled);
}
if (ex_func != null)
throw ex_func;
return value;
}
///
/// Attempts to get cache value of type T, otherwise sets a cache item by key, value and optional eviction
///
///
/// A unique identifier for the cache entry to insert
/// The data for the cache entry
/// (Optional) An object that contains eviction details for the cache item
/// An object of type T, or default T
public T GetSet(string key, T value, int? seconds)
{
bool found;
var cachedValue = this.Get(key, out found);
if (!found)
{
Set(key, value, seconds);
return value;
}
return cachedValue;
}
}
}