From 7122000ae0eaa8ef0b7ad69590a1d11b056fd25c Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Mon, 30 May 2011 22:31:27 -0700 Subject: [PATCH] Refactor cache context to support correct parallel execution We need a new service, IParallelCacheContext, which allows tracking the current cache context across threads and tasks running in the thread pool. --HG-- branch : 1.x --- .../Providers/CommonPartProviderTests.cs | 1 + .../Metadata/ContentDefinitionManagerTests.cs | 1 + .../Commands/CodeGenerationCommandsTests.cs | 1 + .../Migrations/SchemaCommandGeneratorTests.cs | 1 + .../Services/FileBasedProjectSystemTests.cs | 1 + .../Services/PackageInstallerTests.cs | 1 + .../RecipeHandlers/ModuleRecipeHandlerTest.cs | 1 + .../RecipeHandlers/ThemeRecipeHandlerTest.cs | 1 + .../Recipes/Services/RecipeManagerTests.cs | 1 + .../Controllers/AccountControllerTests.cs | 1 + src/Orchard.Tests/Caching/CacheTests.cs | 2 +- .../Caching/ClockCachingTests.cs | 3 +- .../DataMigration/DataMigrationTests.cs | 1 + .../Descriptors/PlacementFileParserTests.cs | 1 + .../ExtensionLoaderCoordinatorTests.cs | 18 ++-- .../Extensions/ExtensionManagerTests.cs | 27 +++-- .../Features/FeatureManagerTests.cs | 1 + .../Dependencies/DependenciesFolderTests.cs | 1 + src/Orchard.Tests/Stubs/StubCacheManager.cs | 13 ++- src/Orchard/Caching/AcquireContext.cs | 15 +++ src/Orchard/Caching/Cache.cs | 16 +-- .../Caching/DefaultAcquireContextContext.cs | 56 ---------- .../Caching/DefaultCacheContextAccessor.cs | 20 ++++ src/Orchard/Caching/DefaultCacheHolder.cs | 8 +- .../Caching/DefaultParallelCacheContext.cs | 102 ++++++++++++++++++ src/Orchard/Caching/IAcquireContextContext.cs | 5 - src/Orchard/Caching/ICacheContextAccessor.cs | 5 + src/Orchard/Caching/IParallelCacheContext.cs | 36 +++++++ .../Extensions/ExtensionManager.cs | 17 +-- src/Orchard/Environment/OrchardStarter.cs | 3 +- src/Orchard/Orchard.Framework.csproj | 6 +- 31 files changed, 264 insertions(+), 102 deletions(-) delete mode 100644 src/Orchard/Caching/DefaultAcquireContextContext.cs create mode 100644 src/Orchard/Caching/DefaultCacheContextAccessor.cs create mode 100644 src/Orchard/Caching/DefaultParallelCacheContext.cs delete mode 100644 src/Orchard/Caching/IAcquireContextContext.cs create mode 100644 src/Orchard/Caching/ICacheContextAccessor.cs create mode 100644 src/Orchard/Caching/IParallelCacheContext.cs diff --git a/src/Orchard.Core.Tests/Common/Providers/CommonPartProviderTests.cs b/src/Orchard.Core.Tests/Common/Providers/CommonPartProviderTests.cs index b8a19e0e9..b4cfbda26 100644 --- a/src/Orchard.Core.Tests/Common/Providers/CommonPartProviderTests.cs +++ b/src/Orchard.Core.Tests/Common/Providers/CommonPartProviderTests.cs @@ -55,6 +55,7 @@ namespace Orchard.Core.Tests.Common.Providers { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterInstance(new Mock().Object); builder.RegisterInstance(new Mock().Object); builder.RegisterInstance(new Mock().Object); diff --git a/src/Orchard.Core.Tests/Settings/Metadata/ContentDefinitionManagerTests.cs b/src/Orchard.Core.Tests/Settings/Metadata/ContentDefinitionManagerTests.cs index 6bafface8..f1fa72a95 100644 --- a/src/Orchard.Core.Tests/Settings/Metadata/ContentDefinitionManagerTests.cs +++ b/src/Orchard.Core.Tests/Settings/Metadata/ContentDefinitionManagerTests.cs @@ -50,6 +50,7 @@ namespace Orchard.Core.Tests.Settings.Metadata { .As(typeof(IMapper)); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); _container = builder.Build(); diff --git a/src/Orchard.Tests.Modules/CodeGeneration/Commands/CodeGenerationCommandsTests.cs b/src/Orchard.Tests.Modules/CodeGeneration/Commands/CodeGenerationCommandsTests.cs index 165364532..b4fcb32d7 100644 --- a/src/Orchard.Tests.Modules/CodeGeneration/Commands/CodeGenerationCommandsTests.cs +++ b/src/Orchard.Tests.Modules/CodeGeneration/Commands/CodeGenerationCommandsTests.cs @@ -50,6 +50,7 @@ namespace Orchard.Tests.Modules.CodeGeneration.Commands { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs b/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs index 551fffe5b..5e62bac6f 100644 --- a/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs +++ b/src/Orchard.Tests.Modules/Migrations/SchemaCommandGeneratorTests.cs @@ -88,6 +88,7 @@ namespace Orchard.Tests.Modules.Migrations { builder.RegisterType().As(); builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests.Modules/Packaging/Services/FileBasedProjectSystemTests.cs b/src/Orchard.Tests.Modules/Packaging/Services/FileBasedProjectSystemTests.cs index 5f1c54830..a372cc629 100644 --- a/src/Orchard.Tests.Modules/Packaging/Services/FileBasedProjectSystemTests.cs +++ b/src/Orchard.Tests.Modules/Packaging/Services/FileBasedProjectSystemTests.cs @@ -31,6 +31,7 @@ namespace Orchard.Tests.Modules.Packaging.Services { builder.RegisterType().As(); builder.RegisterInstance(new Mock().Object); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); _mockedVirtualPathProvider = new Mock(); diff --git a/src/Orchard.Tests.Modules/Packaging/Services/PackageInstallerTests.cs b/src/Orchard.Tests.Modules/Packaging/Services/PackageInstallerTests.cs index 626477065..9285d92d3 100644 --- a/src/Orchard.Tests.Modules/Packaging/Services/PackageInstallerTests.cs +++ b/src/Orchard.Tests.Modules/Packaging/Services/PackageInstallerTests.cs @@ -31,6 +31,7 @@ namespace Orchard.Tests.Modules.Packaging.Services { builder.RegisterType().As(); builder.RegisterInstance(new Mock().Object); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); _mockedVirtualPathProvider = new Mock(); diff --git a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleRecipeHandlerTest.cs b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleRecipeHandlerTest.cs index e1af609e9..3739ca6b8 100644 --- a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleRecipeHandlerTest.cs +++ b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleRecipeHandlerTest.cs @@ -50,6 +50,7 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeRecipeHandlerTest.cs b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeRecipeHandlerTest.cs index 740e1aee9..1df9a5b16 100644 --- a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeRecipeHandlerTest.cs +++ b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeRecipeHandlerTest.cs @@ -53,6 +53,7 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests.Modules/Recipes/Services/RecipeManagerTests.cs b/src/Orchard.Tests.Modules/Recipes/Services/RecipeManagerTests.cs index 372cba562..67478b692 100644 --- a/src/Orchard.Tests.Modules/Recipes/Services/RecipeManagerTests.cs +++ b/src/Orchard.Tests.Modules/Recipes/Services/RecipeManagerTests.cs @@ -74,6 +74,7 @@ namespace Orchard.Tests.Modules.Recipes.Services { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterInstance(_folders).As(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs index b5d4cff1a..b11119ca6 100644 --- a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs +++ b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs @@ -77,6 +77,7 @@ namespace Orchard.Tests.Modules.Users.Controllers { builder.RegisterInstance(new Mock().Object); builder.RegisterInstance(new Mock().Object); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests/Caching/CacheTests.cs b/src/Orchard.Tests/Caching/CacheTests.cs index 09bdf353a..13af703fb 100644 --- a/src/Orchard.Tests/Caching/CacheTests.cs +++ b/src/Orchard.Tests/Caching/CacheTests.cs @@ -15,7 +15,7 @@ namespace Orchard.Tests.Caching { builder.RegisterModule(new CacheModule()); builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As(); + builder.RegisterType().As(); _container = builder.Build(); _cacheManager = _container.Resolve(new TypedParameter(typeof(Type), GetType())); } diff --git a/src/Orchard.Tests/Caching/ClockCachingTests.cs b/src/Orchard.Tests/Caching/ClockCachingTests.cs index ac6618406..8464182bf 100644 --- a/src/Orchard.Tests/Caching/ClockCachingTests.cs +++ b/src/Orchard.Tests/Caching/ClockCachingTests.cs @@ -18,9 +18,8 @@ namespace Orchard.Tests.Caching { builder.RegisterModule(new CacheModule()); builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterInstance(_clock = new StubClock()); - builder.RegisterType().As(); _container = builder.Build(); _cacheManager = _container.Resolve(new TypedParameter(typeof(Type), GetType())); } diff --git a/src/Orchard.Tests/DataMigration/DataMigrationTests.cs b/src/Orchard.Tests/DataMigration/DataMigrationTests.cs index 9697aa0aa..c1de4c80f 100644 --- a/src/Orchard.Tests/DataMigration/DataMigrationTests.cs +++ b/src/Orchard.Tests/DataMigration/DataMigrationTests.cs @@ -68,6 +68,7 @@ namespace Orchard.Tests.DataMigration { builder.RegisterType().As(); builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); _session = _sessionFactory.OpenSession(); builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As(); diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs index a9b621afc..187a355f5 100644 --- a/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs +++ b/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs @@ -15,6 +15,7 @@ namespace Orchard.Tests.DisplayManagement.Descriptors { protected override void Register(Autofac.ContainerBuilder builder) { builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As() .As().InstancePerLifetimeScope(); } diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs index c9b62564a..071e8faa8 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs @@ -27,6 +27,7 @@ namespace Orchard.Tests.Environment.Extensions { builder.RegisterInstance(_folders).As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); _container = builder.Build(); @@ -116,6 +117,9 @@ namespace Orchard.Tests.Environment.Extensions { #endregion } + private ExtensionManager CreateExtensionManager(StubFolders extensionFolder, StubLoaders extensionLoader) { + return new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubParallelCacheContext(), new StubAsyncTokenProvider()); + } [Test] public void AvailableExtensionsShouldFollowCatalogLocations() { @@ -361,7 +365,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features); @@ -387,7 +391,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features); @@ -416,7 +420,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Id == "TestFeature"); @@ -446,7 +450,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Id == "TestFeature"); @@ -474,7 +478,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testModule = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Id == "TestModule"); @@ -498,7 +502,7 @@ Version: 1.0.3 OrchardVersion: 1 "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Id == "Minimalistic"); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); @@ -517,7 +521,7 @@ Version: 1.0.3 OrchardVersion: 1 "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Id == "Minimalistic"); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs index 0b62c0c47..de340b474 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs @@ -28,6 +28,7 @@ namespace Orchard.Tests.Environment.Extensions { builder.RegisterInstance(_folders).As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); _container = builder.Build(); @@ -284,7 +285,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features); @@ -310,7 +311,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features); @@ -323,6 +324,14 @@ Features: } } + private static ExtensionManager CreateExtensionManager(StubFolders extensionFolder, StubLoaders extensionLoader) { + return CreateExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + } + + private static ExtensionManager CreateExtensionManager(IEnumerable extensionFolder, IEnumerable extensionLoader) { + return new ExtensionManager(extensionFolder, extensionLoader, new StubCacheManager(), new StubParallelCacheContext(), new StubAsyncTokenProvider()); + } + [Test] public void ExtensionManagerShouldReturnEmptyFeatureIfFeatureDoesNotExist() { var featureDescriptor = new FeatureDescriptor { Id = "NoSuchFeature", Extension = new ExtensionDescriptor { Id = "NoSuchFeature" } }; @@ -347,7 +356,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Id == "TestFeature"); @@ -377,7 +386,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Id == "TestFeature"); @@ -405,7 +414,7 @@ Features: Description: Contains the Phi type. "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var testModule = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Id == "TestModule"); @@ -429,7 +438,7 @@ Version: 1.0.3 OrchardVersion: 1 "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Id == "Minimalistic"); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); @@ -465,7 +474,7 @@ Features: Dependencies: Beta "); - IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); + IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ; var features = extensionManager.AvailableFeatures(); Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo(" folders, StubLoaders loader, string expectedOrder) { - var extensionManager = new ExtensionManager(folders, new[] { loader }, new StubCacheManager(), new StubAsyncTokenProvider()); + var extensionManager = CreateExtensionManager(folders, new[] { loader }); var features = extensionManager.AvailableFeatures(); Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo(expectedOrder)); } diff --git a/src/Orchard.Tests/Environment/Features/FeatureManagerTests.cs b/src/Orchard.Tests/Environment/Features/FeatureManagerTests.cs index fd622317c..d348b1db3 100644 --- a/src/Orchard.Tests/Environment/Features/FeatureManagerTests.cs +++ b/src/Orchard.Tests/Environment/Features/FeatureManagerTests.cs @@ -39,6 +39,7 @@ namespace Orchard.Tests.Environment.Features { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); diff --git a/src/Orchard.Tests/FileSystems/Dependencies/DependenciesFolderTests.cs b/src/Orchard.Tests/FileSystems/Dependencies/DependenciesFolderTests.cs index e01023967..5d2749c74 100644 --- a/src/Orchard.Tests/FileSystems/Dependencies/DependenciesFolderTests.cs +++ b/src/Orchard.Tests/FileSystems/Dependencies/DependenciesFolderTests.cs @@ -17,6 +17,7 @@ namespace Orchard.Tests.FileSystems.Dependencies { builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); builder.RegisterType().As(); return builder.Build(); } diff --git a/src/Orchard.Tests/Stubs/StubCacheManager.cs b/src/Orchard.Tests/Stubs/StubCacheManager.cs index 868ac6a44..308e48da1 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(new DefaultAcquireContextContext())); + _defaultCacheManager = new DefaultCacheManager(this.GetType(), new DefaultCacheHolder(new DefaultCacheContextAccessor())); } public TResult Get(TKey key, Func, TResult> acquire) { return _defaultCacheManager.Get(key, acquire); @@ -19,6 +19,17 @@ namespace Orchard.Tests.Stubs { } } + public class StubParallelCacheContext : IParallelCacheContext { + public ITask CreateContextAwareTask(Func function) { + throw new NotImplementedException(); + } + + public IEnumerable RunInParallel(IEnumerable source, Func selector) { + return source.Select(selector); + } + } + + public class StubAsyncTokenProvider : IAsyncTokenProvider { public IVolatileToken GetToken(Action> task) { var tokens = new List(); diff --git a/src/Orchard/Caching/AcquireContext.cs b/src/Orchard/Caching/AcquireContext.cs index fd6fc66d5..560f421ca 100644 --- a/src/Orchard/Caching/AcquireContext.cs +++ b/src/Orchard/Caching/AcquireContext.cs @@ -14,4 +14,19 @@ namespace Orchard.Caching { public TKey Key { get; private set; } public Action Monitor { get; private set; } } + + /// + /// Simple implementation of "IAcquireContext" given a lamdba + /// + public class SimpleAcquireContext : IAcquireContext { + private readonly Action _monitor; + + public SimpleAcquireContext(Action monitor) { + _monitor = monitor; + } + + public Action Monitor { + get { return _monitor; } + } + } } diff --git a/src/Orchard/Caching/Cache.cs b/src/Orchard/Caching/Cache.cs index 7c9f6d496..9f9dfe711 100644 --- a/src/Orchard/Caching/Cache.cs +++ b/src/Orchard/Caching/Cache.cs @@ -5,11 +5,11 @@ using System.Linq; namespace Orchard.Caching { public class Cache : ICache { - private readonly IAcquireContextContext _acquireContextContext; + private readonly ICacheContextAccessor _cacheContextAccessor; private readonly ConcurrentDictionary _entries; - public Cache(IAcquireContextContext acquireContextContext) { - _acquireContextContext = acquireContextContext; + public Cache(ICacheContextAccessor cacheContextAccessor) { + _cacheContextAccessor = cacheContextAccessor; _entries = new ConcurrentDictionary(); } @@ -21,9 +21,9 @@ 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 (_acquireContextContext.Instance != null && entry.GetTokens() != null) { + if (_cacheContextAccessor.Current != null && entry.GetTokens() != null) { foreach (var token in entry.GetTokens()) - _acquireContextContext.Instance.Monitor(token); + _cacheContextAccessor.Current.Monitor(token); } return entry.Result; @@ -37,14 +37,14 @@ namespace Orchard.Caching { IAcquireContext parentContext = null; try { // Push context - parentContext = _acquireContextContext.Instance; - _acquireContextContext.Instance = context; + parentContext = _cacheContextAccessor.Current; + _cacheContextAccessor.Current = context; entry.Result = acquire(context); } finally { // Pop context - _acquireContextContext.Instance = parentContext; + _cacheContextAccessor.Current = parentContext; } return entry; } diff --git a/src/Orchard/Caching/DefaultAcquireContextContext.cs b/src/Orchard/Caching/DefaultAcquireContextContext.cs deleted file mode 100644 index e6dd87f41..000000000 --- a/src/Orchard/Caching/DefaultAcquireContextContext.cs +++ /dev/null @@ -1,56 +0,0 @@ -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/DefaultCacheContextAccessor.cs b/src/Orchard/Caching/DefaultCacheContextAccessor.cs new file mode 100644 index 000000000..56cd3db91 --- /dev/null +++ b/src/Orchard/Caching/DefaultCacheContextAccessor.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Orchard.Caching { + public class DefaultCacheContextAccessor : ICacheContextAccessor { + [ThreadStatic] + private static IAcquireContext _threadInstance; + + public static IAcquireContext ThreadInstance { + get { return _threadInstance; } + set { _threadInstance = value; } + } + + public IAcquireContext Current { + get { return ThreadInstance; } + set { ThreadInstance = value; } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/DefaultCacheHolder.cs b/src/Orchard/Caching/DefaultCacheHolder.cs index c44122968..83b286e69 100644 --- a/src/Orchard/Caching/DefaultCacheHolder.cs +++ b/src/Orchard/Caching/DefaultCacheHolder.cs @@ -7,11 +7,11 @@ 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 ICacheContextAccessor _cacheContextAccessor; private readonly ConcurrentDictionary _caches = new ConcurrentDictionary(); - public DefaultCacheHolder(IAcquireContextContext acquireContextContext) { - _acquireContextContext = acquireContextContext; + public DefaultCacheHolder(ICacheContextAccessor cacheContextAccessor) { + _cacheContextAccessor = cacheContextAccessor; } class CacheKey : Tuple { @@ -29,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(_acquireContextContext)); + var result = _caches.GetOrAdd(cacheKey, k => new Cache(_cacheContextAccessor)); return (Cache)result; } } diff --git a/src/Orchard/Caching/DefaultParallelCacheContext.cs b/src/Orchard/Caching/DefaultParallelCacheContext.cs new file mode 100644 index 000000000..e6fad8bef --- /dev/null +++ b/src/Orchard/Caching/DefaultParallelCacheContext.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Orchard.Caching { + public class DefaultParallelCacheContext : IParallelCacheContext { + private readonly ICacheContextAccessor _cacheContextAccessor; + + public DefaultParallelCacheContext(ICacheContextAccessor cacheContextAccessor) { + _cacheContextAccessor = cacheContextAccessor; + } + + public IEnumerable RunInParallel(IEnumerable source, Func selector) { + // Create tasks that capture the current thread context + var tasks = source.Select(item => this.CreateContextAwareTask(() => selector(item))).ToList(); + + // Run tasks in parallel and combine results immediately + var result = tasks.AsParallel().Select(task => task.Execute()).ToList(); + + // Forward tokens collected by tasks to the current context + foreach(var task in tasks) { + task.Finish(); + } + return result; + } + + /// + /// Create a task that wraps some piece of code that implictly depends on the cache context. + /// The return task can be used in any execution thread (e.g. System.Threading.Tasks). + /// + public ITask CreateContextAwareTask(Func function) { + return new TaskWithAcquireContext(_cacheContextAccessor, function); + } + + public class TaskWithAcquireContext : ITask { + private readonly ICacheContextAccessor _cacheContextAccessor; + private readonly Func _function; + private IList _tokens; + + public TaskWithAcquireContext(ICacheContextAccessor cacheContextAccessor, Func function) { + _cacheContextAccessor = cacheContextAccessor; + _function = function; + } + + /// + /// Execute task and collect eventual volatile tokens + /// + public T Execute() { + IAcquireContext parentContext = _cacheContextAccessor.Current; + try { + // Push context + if (parentContext == null) { + _cacheContextAccessor.Current = new SimpleAcquireContext(AddToken); + } + + // Execute lambda + return _function(); + } + finally { + // Pop context + if (parentContext == null) { + _cacheContextAccessor.Current = parentContext; + } + } + } + + /// + /// Return tokens collected during task execution + /// + public IEnumerable Tokens { + get { + if (_tokens == null) + return Enumerable.Empty(); + return _tokens; + } + } + + public void Dispose() { + Finish(); + } + + /// + /// Forward collected tokens to current cache context + /// + public void Finish() { + var tokens = _tokens; + _tokens = null; + if (_cacheContextAccessor.Current != null && tokens != null) { + foreach (var token in tokens) { + _cacheContextAccessor.Current.Monitor(token); + } + } + } + + private void AddToken(IVolatileToken token) { + if (_tokens == null) + _tokens = new List(); + _tokens.Add(token); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/IAcquireContextContext.cs b/src/Orchard/Caching/IAcquireContextContext.cs deleted file mode 100644 index acf4953e3..000000000 --- a/src/Orchard/Caching/IAcquireContextContext.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Orchard.Caching { - public interface IAcquireContextContext { - IAcquireContext Instance { get; set; } - } -} \ No newline at end of file diff --git a/src/Orchard/Caching/ICacheContextAccessor.cs b/src/Orchard/Caching/ICacheContextAccessor.cs new file mode 100644 index 000000000..c4b3e4bf3 --- /dev/null +++ b/src/Orchard/Caching/ICacheContextAccessor.cs @@ -0,0 +1,5 @@ +namespace Orchard.Caching { + public interface ICacheContextAccessor { + IAcquireContext Current { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/IParallelCacheContext.cs b/src/Orchard/Caching/IParallelCacheContext.cs new file mode 100644 index 000000000..b7e76b664 --- /dev/null +++ b/src/Orchard/Caching/IParallelCacheContext.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Orchard.Caching { + /// + /// Provides services to enable parallel tasks aware of the current cache context. + /// + public interface IParallelCacheContext { + /// + /// Create a task that wraps some piece of code that implictly depends on the cache context. + /// The return task can be used in any execution thread (e.g. System.Threading.Tasks). + /// + ITask CreateContextAwareTask(Func function); + + IEnumerable RunInParallel(IEnumerable source, Func selector); + } + + public interface ITask : IDisposable { + /// + /// Execute task and collect eventual volatile tokens + /// + T Execute(); + + /// + /// Return tokens collected during task execution. May be empty if nothing collected, + /// or if the task was executed in the same context as the current + /// ICacheContextAccessor.Current. + /// + IEnumerable Tokens { get; } + + /// + /// Forward collected tokens to current cache context + /// + void Finish(); + } +} diff --git a/src/Orchard/Environment/Extensions/ExtensionManager.cs b/src/Orchard/Environment/Extensions/ExtensionManager.cs index 28b8b60af..2c18ca734 100644 --- a/src/Orchard/Environment/Extensions/ExtensionManager.cs +++ b/src/Orchard/Environment/Extensions/ExtensionManager.cs @@ -15,6 +15,7 @@ namespace Orchard.Environment.Extensions { private readonly IEnumerable _folders; private readonly IAsyncTokenProvider _asyncTokenProvider; private readonly ICacheManager _cacheManager; + private readonly IParallelCacheContext _parallelCacheContext; private readonly IEnumerable _loaders; public Localizer T { get; set; } @@ -24,11 +25,13 @@ namespace Orchard.Environment.Extensions { IEnumerable folders, IEnumerable loaders, ICacheManager cacheManager, + IParallelCacheContext parallelCacheContext, IAsyncTokenProvider asyncTokenProvider) { _folders = folders; _asyncTokenProvider = asyncTokenProvider; _cacheManager = cacheManager; + _parallelCacheContext = parallelCacheContext; _loaders = loaders.OrderBy(x => x.Order).ToArray(); T = NullLocalizer.Instance; Logger = NullLogger.Instance; @@ -42,16 +45,18 @@ namespace Orchard.Environment.Extensions { public IEnumerable AvailableExtensions() { return _cacheManager.Get("AvailableExtensions", ctx => - _folders - .AsParallel() // Execute in parallel for each folder - .SelectMany(folder => folder.AvailableExtensions()) - .ToList() // Force execution inside the cache entry - ); + _parallelCacheContext + .RunInParallel(_folders, folder => folder.AvailableExtensions()) + .SelectMany(descriptors => descriptors) + .ToReadOnlyCollection()); } public IEnumerable AvailableFeatures() { return _cacheManager.Get("AvailableFeatures", ctx => - AvailableExtensions().SelectMany(ext => ext.Features).OrderByDependenciesAndPriorities(HasDependency, GetPriority).ToReadOnlyCollection()); + AvailableExtensions() + .SelectMany(ext => ext.Features) + .OrderByDependenciesAndPriorities(HasDependency, GetPriority) + .ToReadOnlyCollection()); } internal static int GetPriority(FeatureDescriptor featureDescriptor) { diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index 12e3ec101..66654d908 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -39,7 +39,8 @@ 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(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 32aed5d6b..4c317f8a5 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -148,8 +148,10 @@ - - + + + +