Abstract away cache context

This is needed to support proper behavior of cache context
when executing tasks in parallel.

--HG--
branch : 1.x
This commit is contained in:
Renaud Paquay
2011-05-30 17:33:59 -07:00
parent 3ca3234bbf
commit 750493c447
7 changed files with 81 additions and 17 deletions

View File

@@ -8,7 +8,7 @@ namespace Orchard.Tests.Stubs {
private readonly ICacheManager _defaultCacheManager;
public StubCacheManager() {
_defaultCacheManager = new DefaultCacheManager(this.GetType(), new DefaultCacheHolder());
_defaultCacheManager = new DefaultCacheManager(this.GetType(), new DefaultCacheHolder(new DefaultAcquireContextContext()));
}
public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire) {
return _defaultCacheManager.Get(key, acquire);

View File

@@ -5,9 +5,11 @@ using System.Linq;
namespace Orchard.Caching {
public class Cache<TKey, TResult> : ICache<TKey, TResult> {
private readonly IAcquireContextContext _acquireContextContext;
private readonly ConcurrentDictionary<TKey, CacheEntry> _entries;
public Cache() {
public Cache(IAcquireContextContext acquireContextContext) {
_acquireContextContext = acquireContextContext;
_entries = new ConcurrentDictionary<TKey, CacheEntry>();
}
@@ -19,30 +21,30 @@ namespace Orchard.Caching {
(k, currentEntry) => (currentEntry.GetTokens() != null && currentEntry.GetTokens().Any(t => !t.IsCurrent) ? CreateEntry(k, acquire) : currentEntry));
// Bubble up volatile tokens to parent context
if (CacheAquireContext.ThreadInstance != null && entry.GetTokens() != null) {
if (_acquireContextContext.Instance != null && entry.GetTokens() != null) {
foreach (var token in entry.GetTokens())
CacheAquireContext.ThreadInstance.Monitor(token);
_acquireContextContext.Instance.Monitor(token);
}
return entry.Result;
}
private static CacheEntry CreateEntry(TKey k, Func<AcquireContext<TKey>, TResult> acquire) {
private CacheEntry CreateEntry(TKey k, Func<AcquireContext<TKey>, TResult> acquire) {
var entry = new CacheEntry();
var context = new AcquireContext<TKey>(k, entry.AddToken);
IAcquireContext parentContext = null;
try {
// Push context
parentContext = CacheAquireContext.ThreadInstance;
CacheAquireContext.ThreadInstance = context;
parentContext = _acquireContextContext.Instance;
_acquireContextContext.Instance = context;
entry.Result = acquire(context);
}
finally {
// Pop context
CacheAquireContext.ThreadInstance = parentContext;
_acquireContextContext.Instance = parentContext;
}
return entry;
}
@@ -64,12 +66,4 @@ namespace Orchard.Caching {
}
}
}
/// <summary>
/// Keep track of nested caches contexts on a given thread
/// </summary>
internal static class CacheAquireContext {
[ThreadStatic]
public static IAcquireContext ThreadInstance;
}
}

View File

@@ -0,0 +1,56 @@
using System.Security.Principal;
using System.Threading;
namespace Orchard.Caching {
/// <summary>
/// Keep track of nested caches contexts on a given thread
/// </summary>
public class DefaultAcquireContextContext : IAcquireContextContext {
public IAcquireContext Instance {
get {
var principal = Thread.CurrentPrincipal as SurrogatePrincipal;
if (principal == null)
return null;
return principal.AcquireContext;
}
set {
var surrogatePrincipal = Thread.CurrentPrincipal as SurrogatePrincipal;
if (value == null) {
if (surrogatePrincipal != null) {
surrogatePrincipal.AcquireContext = null;
Thread.CurrentPrincipal = surrogatePrincipal.ActualPrincipal;
}
}
else {
if (surrogatePrincipal == null) {
surrogatePrincipal = new SurrogatePrincipal(Thread.CurrentPrincipal);
Thread.CurrentPrincipal = surrogatePrincipal;
}
surrogatePrincipal.AcquireContext = value;
}
}
}
public class SurrogatePrincipal : IPrincipal {
private readonly IPrincipal _principal;
public SurrogatePrincipal(IPrincipal principal) {
_principal = principal;
}
public bool IsInRole(string role) {
return _principal.IsInRole(role);
}
public IIdentity Identity {
get { return _principal.Identity; }
}
public IPrincipal ActualPrincipal {
get { return _principal; }
}
public IAcquireContext AcquireContext { get; set; }
}
}
}

View File

@@ -7,8 +7,13 @@ namespace Orchard.Caching {
/// The cache holder is responsible for actually storing the references to cached entities.
/// </summary>
public class DefaultCacheHolder : ICacheHolder {
private readonly IAcquireContextContext _acquireContextContext;
private readonly ConcurrentDictionary<CacheKey, object> _caches = new ConcurrentDictionary<CacheKey, object>();
public DefaultCacheHolder(IAcquireContextContext acquireContextContext) {
_acquireContextContext = acquireContextContext;
}
class CacheKey : Tuple<Type, Type, Type> {
public CacheKey(Type component, Type key, Type result)
: base(component, key, result) {
@@ -24,7 +29,7 @@ namespace Orchard.Caching {
/// <returns>An entry from the cache, or a new, empty one, if none is found.</returns>
public ICache<TKey, TResult> GetCache<TKey, TResult>(Type component) {
var cacheKey = new CacheKey(component, typeof(TKey), typeof(TResult));
var result = _caches.GetOrAdd(cacheKey, k => new Cache<TKey, TResult>());
var result = _caches.GetOrAdd(cacheKey, k => new Cache<TKey, TResult>(_acquireContextContext));
return (Cache<TKey, TResult>)result;
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Caching {
public interface IAcquireContextContext {
IAcquireContext Instance { get; set; }
}
}

View File

@@ -39,6 +39,7 @@ namespace Orchard.Environment {
// a single default host implementation is needed for bootstrapping a web app domain
builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance();
builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
builder.RegisterType<DefaultAcquireContextContext>().As<IAcquireContextContext>().SingleInstance();
builder.RegisterType<DefaultAsyncTokenProvider>().As<IAsyncTokenProvider>().SingleInstance();
builder.RegisterType<DefaultHostEnvironment>().As<IHostEnvironment>().SingleInstance();
builder.RegisterType<DefaultHostLocalRestart>().As<IHostLocalRestart>().SingleInstance();

View File

@@ -148,6 +148,8 @@
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Caching\DefaultAcquireContextContext.cs" />
<Compile Include="Caching\IAcquireContextContext.cs" />
<Compile Include="ContentManagement\ContentIdentity.cs" />
<Compile Include="ContentManagement\ContentItemBehavior.cs" />
<Compile Include="ContentManagement\ContentPartBehavior.cs" />
@@ -178,6 +180,7 @@
<Compile Include="Caching\DefaultAsyncTokenProvider.cs" />
<Compile Include="Environment\Extensions\ExtensionMonitoringCoordinator.cs" />
<Compile Include="Caching\IAsyncTokenProvider.cs" />
<Compile Include="Environment\Extensions\Folders\CoreModuleFolders.cs" />
<Compile Include="Environment\Extensions\Folders\IExtensionHarvester.cs" />
<Compile Include="Environment\Extensions\IExtensionMonitoringCoordinator.cs" />
<Compile Include="Environment\Extensions\OrchardSuppressDependencyAttribute.cs" />