From 750493c447187457c0eb6b0cd99bb97bfbf698d2 Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Mon, 30 May 2011 17:33:59 -0700 Subject: [PATCH] Abstract away cache context This is needed to support proper behavior of cache context when executing tasks in parallel. --HG-- branch : 1.x --- src/Orchard.Tests/Stubs/StubCacheManager.cs | 2 +- src/Orchard/Caching/Cache.cs | 24 +++----- .../Caching/DefaultAcquireContextContext.cs | 56 +++++++++++++++++++ src/Orchard/Caching/DefaultCacheHolder.cs | 7 ++- src/Orchard/Caching/IAcquireContextContext.cs | 5 ++ src/Orchard/Environment/OrchardStarter.cs | 1 + src/Orchard/Orchard.Framework.csproj | 3 + 7 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 src/Orchard/Caching/DefaultAcquireContextContext.cs create mode 100644 src/Orchard/Caching/IAcquireContextContext.cs diff --git a/src/Orchard.Tests/Stubs/StubCacheManager.cs b/src/Orchard.Tests/Stubs/StubCacheManager.cs index a25790d54..868ac6a44 100644 --- a/src/Orchard.Tests/Stubs/StubCacheManager.cs +++ b/src/Orchard.Tests/Stubs/StubCacheManager.cs @@ -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 key, Func, TResult> acquire) { return _defaultCacheManager.Get(key, acquire); diff --git a/src/Orchard/Caching/Cache.cs b/src/Orchard/Caching/Cache.cs index ac8c449e2..7c9f6d496 100644 --- a/src/Orchard/Caching/Cache.cs +++ b/src/Orchard/Caching/Cache.cs @@ -5,9 +5,11 @@ using System.Linq; namespace Orchard.Caching { public class Cache : ICache { + private readonly IAcquireContextContext _acquireContextContext; private readonly ConcurrentDictionary _entries; - public Cache() { + public Cache(IAcquireContextContext acquireContextContext) { + _acquireContextContext = acquireContextContext; _entries = new ConcurrentDictionary(); } @@ -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, TResult> acquire) { + private CacheEntry CreateEntry(TKey k, Func, TResult> acquire) { var entry = new CacheEntry(); var context = new AcquireContext(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 { } } } - - /// - /// Keep track of nested caches contexts on a given thread - /// - internal static class CacheAquireContext { - [ThreadStatic] - public static IAcquireContext ThreadInstance; - } } diff --git a/src/Orchard/Caching/DefaultAcquireContextContext.cs b/src/Orchard/Caching/DefaultAcquireContextContext.cs new file mode 100644 index 000000000..e6dd87f41 --- /dev/null +++ b/src/Orchard/Caching/DefaultAcquireContextContext.cs @@ -0,0 +1,56 @@ +using System.Security.Principal; +using System.Threading; + +namespace Orchard.Caching { + /// + /// Keep track of nested caches contexts on a given thread + /// + 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; } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/DefaultCacheHolder.cs b/src/Orchard/Caching/DefaultCacheHolder.cs index d30d886e9..c44122968 100644 --- a/src/Orchard/Caching/DefaultCacheHolder.cs +++ b/src/Orchard/Caching/DefaultCacheHolder.cs @@ -7,8 +7,13 @@ namespace Orchard.Caching { /// The cache holder is responsible for actually storing the references to cached entities. /// public class DefaultCacheHolder : ICacheHolder { + private readonly IAcquireContextContext _acquireContextContext; private readonly ConcurrentDictionary _caches = new ConcurrentDictionary(); + public DefaultCacheHolder(IAcquireContextContext acquireContextContext) { + _acquireContextContext = acquireContextContext; + } + class CacheKey : Tuple { public CacheKey(Type component, Type key, Type result) : base(component, key, result) { @@ -24,7 +29,7 @@ namespace Orchard.Caching { /// An entry from the cache, or a new, empty one, if none is found. public ICache GetCache(Type component) { var cacheKey = new CacheKey(component, typeof(TKey), typeof(TResult)); - var result = _caches.GetOrAdd(cacheKey, k => new Cache()); + var result = _caches.GetOrAdd(cacheKey, k => new Cache(_acquireContextContext)); return (Cache)result; } } diff --git a/src/Orchard/Caching/IAcquireContextContext.cs b/src/Orchard/Caching/IAcquireContextContext.cs new file mode 100644 index 000000000..acf4953e3 --- /dev/null +++ b/src/Orchard/Caching/IAcquireContextContext.cs @@ -0,0 +1,5 @@ +namespace Orchard.Caching { + public interface IAcquireContextContext { + IAcquireContext Instance { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index 4e7533c3d..46b6d7ab1 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -39,6 +39,7 @@ namespace Orchard.Environment { // a single default host implementation is needed for bootstrapping a web app domain builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index bedebd48f..32aed5d6b 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -148,6 +148,8 @@ + + @@ -178,6 +180,7 @@ +