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
This commit is contained in:
Renaud Paquay
2011-05-30 22:31:27 -07:00
parent 1c16b10aa0
commit 7122000ae0
31 changed files with 264 additions and 102 deletions

View File

@@ -55,6 +55,7 @@ namespace Orchard.Core.Tests.Common.Providers {
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>(); builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>(); builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterInstance(new Mock<IThemeManager>().Object); builder.RegisterInstance(new Mock<IThemeManager>().Object);
builder.RegisterInstance(new Mock<IOrchardServices>().Object); builder.RegisterInstance(new Mock<IOrchardServices>().Object);
builder.RegisterInstance(new Mock<RequestContext>().Object); builder.RegisterInstance(new Mock<RequestContext>().Object);

View File

@@ -50,6 +50,7 @@ namespace Orchard.Core.Tests.Settings.Metadata {
.As(typeof(IMapper<SettingsDictionary, XElement>)); .As(typeof(IMapper<SettingsDictionary, XElement>));
builder.RegisterType<Signals>().As<ISignals>(); builder.RegisterType<Signals>().As<ISignals>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
_container = builder.Build(); _container = builder.Build();

View File

@@ -50,6 +50,7 @@ namespace Orchard.Tests.Modules.CodeGeneration.Commands {
builder.RegisterType<ExtensionManager>().As<IExtensionManager>(); builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<SchemaCommandGenerator>().As<ISchemaCommandGenerator>(); builder.RegisterType<SchemaCommandGenerator>().As<ISchemaCommandGenerator>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
builder.RegisterType<StubHostEnvironment>().As<IHostEnvironment>(); builder.RegisterType<StubHostEnvironment>().As<IHostEnvironment>();

View File

@@ -88,6 +88,7 @@ namespace Orchard.Tests.Modules.Migrations {
builder.RegisterType<SchemaCommandGenerator>().As<ISchemaCommandGenerator>(); builder.RegisterType<SchemaCommandGenerator>().As<ISchemaCommandGenerator>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
builder.RegisterType<StubHostEnvironment>().As<IHostEnvironment>(); builder.RegisterType<StubHostEnvironment>().As<IHostEnvironment>();

View File

@@ -31,6 +31,7 @@ namespace Orchard.Tests.Modules.Packaging.Services {
builder.RegisterType<FolderUpdater>().As<IFolderUpdater>(); builder.RegisterType<FolderUpdater>().As<IFolderUpdater>();
builder.RegisterInstance(new Mock<INotifier>().Object); builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
_mockedVirtualPathProvider = new Mock<IVirtualPathProvider>(); _mockedVirtualPathProvider = new Mock<IVirtualPathProvider>();

View File

@@ -31,6 +31,7 @@ namespace Orchard.Tests.Modules.Packaging.Services {
builder.RegisterType<FolderUpdater>().As<IFolderUpdater>(); builder.RegisterType<FolderUpdater>().As<IFolderUpdater>();
builder.RegisterInstance(new Mock<INotifier>().Object); builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
_mockedVirtualPathProvider = new Mock<IVirtualPathProvider>(); _mockedVirtualPathProvider = new Mock<IVirtualPathProvider>();

View File

@@ -50,6 +50,7 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
builder.RegisterType<ExtensionManager>().As<IExtensionManager>(); builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<FeatureManager>().As<IFeatureManager>(); builder.RegisterType<FeatureManager>().As<IFeatureManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
builder.RegisterType<ShellDescriptorManager>().As<IShellDescriptorManager>().SingleInstance(); builder.RegisterType<ShellDescriptorManager>().As<IShellDescriptorManager>().SingleInstance();
builder.RegisterType<StubDataMigrationManager>().As<IDataMigrationManager>(); builder.RegisterType<StubDataMigrationManager>().As<IDataMigrationManager>();

View File

@@ -53,6 +53,7 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
builder.RegisterType<ExtensionManager>().As<IExtensionManager>(); builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<FeatureManager>().As<IFeatureManager>(); builder.RegisterType<FeatureManager>().As<IFeatureManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
builder.RegisterType<ShellDescriptorManager>().As<IShellDescriptorManager>().SingleInstance(); builder.RegisterType<ShellDescriptorManager>().As<IShellDescriptorManager>().SingleInstance();
builder.RegisterType<ModuleRecipeHandlerTest.StubDataMigrationManager>().As<IDataMigrationManager>(); builder.RegisterType<ModuleRecipeHandlerTest.StubDataMigrationManager>().As<IDataMigrationManager>();

View File

@@ -74,6 +74,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
builder.RegisterType<StubAppDataFolder>().As<IAppDataFolder>(); builder.RegisterType<StubAppDataFolder>().As<IAppDataFolder>();
builder.RegisterType<StubClock>().As<IClock>(); builder.RegisterType<StubClock>().As<IClock>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
builder.RegisterInstance(_folders).As<IExtensionFolders>(); builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<Environment.Extensions.ExtensionManagerTests.StubLoaders>().As<IExtensionLoader>(); builder.RegisterType<Environment.Extensions.ExtensionManagerTests.StubLoaders>().As<IExtensionLoader>();

View File

@@ -77,6 +77,7 @@ namespace Orchard.Tests.Modules.Users.Controllers {
builder.RegisterInstance(new Mock<INotifier>().Object); builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterInstance(new Mock<IContentDisplay>().Object); builder.RegisterInstance(new Mock<IContentDisplay>().Object);
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<Signals>().As<ISignals>(); builder.RegisterType<Signals>().As<ISignals>();
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>(); builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();

View File

@@ -15,7 +15,7 @@ namespace Orchard.Tests.Caching {
builder.RegisterModule(new CacheModule()); builder.RegisterModule(new CacheModule());
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>(); builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance(); builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>(); builder.RegisterType<DefaultCacheContextAccessor>().As<ICacheContextAccessor>();
_container = builder.Build(); _container = builder.Build();
_cacheManager = _container.Resolve<ICacheManager>(new TypedParameter(typeof(Type), GetType())); _cacheManager = _container.Resolve<ICacheManager>(new TypedParameter(typeof(Type), GetType()));
} }

View File

@@ -18,9 +18,8 @@ namespace Orchard.Tests.Caching {
builder.RegisterModule(new CacheModule()); builder.RegisterModule(new CacheModule());
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>(); builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance(); builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>(); builder.RegisterType<DefaultCacheContextAccessor>().As<ICacheContextAccessor>();
builder.RegisterInstance<IClock>(_clock = new StubClock()); builder.RegisterInstance<IClock>(_clock = new StubClock());
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
_container = builder.Build(); _container = builder.Build();
_cacheManager = _container.Resolve<ICacheManager>(new TypedParameter(typeof(Type), GetType())); _cacheManager = _container.Resolve<ICacheManager>(new TypedParameter(typeof(Type), GetType()));
} }

View File

@@ -68,6 +68,7 @@ namespace Orchard.Tests.DataMigration {
builder.RegisterType<DataMigrationManager>().As<IDataMigrationManager>(); builder.RegisterType<DataMigrationManager>().As<IDataMigrationManager>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
_session = _sessionFactory.OpenSession(); _session = _sessionFactory.OpenSession();
builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>(); builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();

View File

@@ -15,6 +15,7 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
protected override void Register(Autofac.ContainerBuilder builder) { protected override void Register(Autofac.ContainerBuilder builder) {
builder.RegisterType<PlacementFileParser>().As<IPlacementFileParser>(); builder.RegisterType<PlacementFileParser>().As<IPlacementFileParser>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<InMemoryWebSiteFolder>().As<IWebSiteFolder>() builder.RegisterType<InMemoryWebSiteFolder>().As<IWebSiteFolder>()
.As<InMemoryWebSiteFolder>().InstancePerLifetimeScope(); .As<InMemoryWebSiteFolder>().InstancePerLifetimeScope();
} }

View File

@@ -27,6 +27,7 @@ namespace Orchard.Tests.Environment.Extensions {
builder.RegisterInstance(_folders).As<IExtensionFolders>(); builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>(); builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
_container = builder.Build(); _container = builder.Build();
@@ -116,6 +117,9 @@ namespace Orchard.Tests.Environment.Extensions {
#endregion #endregion
} }
private ExtensionManager CreateExtensionManager(StubFolders extensionFolder, StubLoaders extensionLoader) {
return new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubParallelCacheContext(), new StubAsyncTokenProvider());
}
[Test] [Test]
public void AvailableExtensionsShouldFollowCatalogLocations() { public void AvailableExtensionsShouldFollowCatalogLocations() {
@@ -361,7 +365,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features); .SelectMany(x => x.Features);
@@ -387,7 +391,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features); .SelectMany(x => x.Features);
@@ -416,7 +420,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features) .SelectMany(x => x.Features)
.Single(x => x.Id == "TestFeature"); .Single(x => x.Id == "TestFeature");
@@ -446,7 +450,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features) .SelectMany(x => x.Features)
.Single(x => x.Id == "TestFeature"); .Single(x => x.Id == "TestFeature");
@@ -474,7 +478,7 @@ Features:
Description: Contains the Phi type. 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() var testModule = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features) .SelectMany(x => x.Features)
.Single(x => x.Id == "TestModule"); .Single(x => x.Id == "TestModule");
@@ -498,7 +502,7 @@ Version: 1.0.3
OrchardVersion: 1 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"); var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Id == "Minimalistic");
Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1));
@@ -517,7 +521,7 @@ Version: 1.0.3
OrchardVersion: 1 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"); var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Id == "Minimalistic");
Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1));

View File

@@ -28,6 +28,7 @@ namespace Orchard.Tests.Environment.Extensions {
builder.RegisterInstance(_folders).As<IExtensionFolders>(); builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>(); builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
_container = builder.Build(); _container = builder.Build();
@@ -284,7 +285,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features); .SelectMany(x => x.Features);
@@ -310,7 +311,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features); .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<StubFolders> extensionFolder, IEnumerable<StubLoaders> extensionLoader) {
return new ExtensionManager(extensionFolder, extensionLoader, new StubCacheManager(), new StubParallelCacheContext(), new StubAsyncTokenProvider());
}
[Test] [Test]
public void ExtensionManagerShouldReturnEmptyFeatureIfFeatureDoesNotExist() { public void ExtensionManagerShouldReturnEmptyFeatureIfFeatureDoesNotExist() {
var featureDescriptor = new FeatureDescriptor { Id = "NoSuchFeature", Extension = new ExtensionDescriptor { Id = "NoSuchFeature" } }; var featureDescriptor = new FeatureDescriptor { Id = "NoSuchFeature", Extension = new ExtensionDescriptor { Id = "NoSuchFeature" } };
@@ -347,7 +356,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features) .SelectMany(x => x.Features)
.Single(x => x.Id == "TestFeature"); .Single(x => x.Id == "TestFeature");
@@ -377,7 +386,7 @@ Features:
Description: Contains the Phi type. 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() var testFeature = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features) .SelectMany(x => x.Features)
.Single(x => x.Id == "TestFeature"); .Single(x => x.Id == "TestFeature");
@@ -405,7 +414,7 @@ Features:
Description: Contains the Phi type. 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() var testModule = extensionManager.AvailableExtensions()
.SelectMany(x => x.Features) .SelectMany(x => x.Features)
.Single(x => x.Id == "TestModule"); .Single(x => x.Id == "TestModule");
@@ -429,7 +438,7 @@ Version: 1.0.3
OrchardVersion: 1 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"); var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Id == "Minimalistic");
Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1));
@@ -465,7 +474,7 @@ Features:
Dependencies: Beta Dependencies: Beta
"); ");
IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); IExtensionManager extensionManager = CreateExtensionManager(extensionFolder, extensionLoader); ;
var features = extensionManager.AvailableFeatures(); var features = extensionManager.AvailableFeatures();
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Gamma<Alpha<")); Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Gamma<Alpha<"));
} }
@@ -505,7 +514,7 @@ Version: 1.0.3
OrchardVersion: 1 OrchardVersion: 1
"); ");
IExtensionManager extensionManager = new ExtensionManager(new[] { moduleExtensionFolder, themeExtensionFolder }, new[] { extensionLoader }, new StubCacheManager(), new StubAsyncTokenProvider()); IExtensionManager extensionManager = CreateExtensionManager(new[] { moduleExtensionFolder, themeExtensionFolder }, new[] { extensionLoader });
var features = extensionManager.AvailableFeatures(); var features = extensionManager.AvailableFeatures();
Assert.That(features.Count(), Is.EqualTo(4)); Assert.That(features.Count(), Is.EqualTo(4));
} }
@@ -539,7 +548,7 @@ OrchardVersion: 1{1}{2}",
} }
private static void AssertFeaturesAreInOrder(IEnumerable<StubFolders> folders, StubLoaders loader, string expectedOrder) { private static void AssertFeaturesAreInOrder(IEnumerable<StubFolders> 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(); var features = extensionManager.AvailableFeatures();
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo(expectedOrder)); Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo(expectedOrder));
} }

View File

@@ -39,6 +39,7 @@ namespace Orchard.Tests.Environment.Features {
builder.RegisterType<ExtensionManager>().As<IExtensionManager>(); builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<FeatureManager>().As<IFeatureManager>(); builder.RegisterType<FeatureManager>().As<IFeatureManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>(); builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>(); builder.RegisterType<StubAsyncTokenProvider>().As<IAsyncTokenProvider>();
builder.RegisterType<ShellDescriptorManager>().As<IShellDescriptorManager>().SingleInstance(); builder.RegisterType<ShellDescriptorManager>().As<IShellDescriptorManager>().SingleInstance();
builder.RegisterType<ShellStateManager>().As<IShellStateManager>().SingleInstance(); builder.RegisterType<ShellStateManager>().As<IShellStateManager>().SingleInstance();

View File

@@ -17,6 +17,7 @@ namespace Orchard.Tests.FileSystems.Dependencies {
builder.RegisterType<StubClock>().As<IClock>().SingleInstance(); builder.RegisterType<StubClock>().As<IClock>().SingleInstance();
builder.RegisterType<StubAppDataFolder>().As<IAppDataFolder>().SingleInstance(); builder.RegisterType<StubAppDataFolder>().As<IAppDataFolder>().SingleInstance();
builder.RegisterType<StubCacheManager>().As<ICacheManager>().SingleInstance(); builder.RegisterType<StubCacheManager>().As<ICacheManager>().SingleInstance();
builder.RegisterType<StubParallelCacheContext>().As<IParallelCacheContext>();
builder.RegisterType<DefaultDependenciesFolder>().As<IDependenciesFolder>(); builder.RegisterType<DefaultDependenciesFolder>().As<IDependenciesFolder>();
return builder.Build(); return builder.Build();
} }

View File

@@ -8,7 +8,7 @@ namespace Orchard.Tests.Stubs {
private readonly ICacheManager _defaultCacheManager; private readonly ICacheManager _defaultCacheManager;
public StubCacheManager() { public StubCacheManager() {
_defaultCacheManager = new DefaultCacheManager(this.GetType(), new DefaultCacheHolder(new DefaultAcquireContextContext())); _defaultCacheManager = new DefaultCacheManager(this.GetType(), new DefaultCacheHolder(new DefaultCacheContextAccessor()));
} }
public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire) { public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire) {
return _defaultCacheManager.Get(key, acquire); return _defaultCacheManager.Get(key, acquire);
@@ -19,6 +19,17 @@ namespace Orchard.Tests.Stubs {
} }
} }
public class StubParallelCacheContext : IParallelCacheContext {
public ITask<T> CreateContextAwareTask<T>(Func<T> function) {
throw new NotImplementedException();
}
public IEnumerable<TResult> RunInParallel<T, TResult>(IEnumerable<T> source, Func<T, TResult> selector) {
return source.Select(selector);
}
}
public class StubAsyncTokenProvider : IAsyncTokenProvider { public class StubAsyncTokenProvider : IAsyncTokenProvider {
public IVolatileToken GetToken(Action<Action<IVolatileToken>> task) { public IVolatileToken GetToken(Action<Action<IVolatileToken>> task) {
var tokens = new List<IVolatileToken>(); var tokens = new List<IVolatileToken>();

View File

@@ -14,4 +14,19 @@ namespace Orchard.Caching {
public TKey Key { get; private set; } public TKey Key { get; private set; }
public Action<IVolatileToken> Monitor { get; private set; } public Action<IVolatileToken> Monitor { get; private set; }
} }
/// <summary>
/// Simple implementation of "IAcquireContext" given a lamdba
/// </summary>
public class SimpleAcquireContext : IAcquireContext {
private readonly Action<IVolatileToken> _monitor;
public SimpleAcquireContext(Action<IVolatileToken> monitor) {
_monitor = monitor;
}
public Action<IVolatileToken> Monitor {
get { return _monitor; }
}
}
} }

View File

@@ -5,11 +5,11 @@ using System.Linq;
namespace Orchard.Caching { namespace Orchard.Caching {
public class Cache<TKey, TResult> : ICache<TKey, TResult> { public class Cache<TKey, TResult> : ICache<TKey, TResult> {
private readonly IAcquireContextContext _acquireContextContext; private readonly ICacheContextAccessor _cacheContextAccessor;
private readonly ConcurrentDictionary<TKey, CacheEntry> _entries; private readonly ConcurrentDictionary<TKey, CacheEntry> _entries;
public Cache(IAcquireContextContext acquireContextContext) { public Cache(ICacheContextAccessor cacheContextAccessor) {
_acquireContextContext = acquireContextContext; _cacheContextAccessor = cacheContextAccessor;
_entries = new ConcurrentDictionary<TKey, CacheEntry>(); _entries = new ConcurrentDictionary<TKey, CacheEntry>();
} }
@@ -21,9 +21,9 @@ namespace Orchard.Caching {
(k, currentEntry) => (currentEntry.GetTokens() != null && currentEntry.GetTokens().Any(t => !t.IsCurrent) ? CreateEntry(k, acquire) : currentEntry)); (k, currentEntry) => (currentEntry.GetTokens() != null && currentEntry.GetTokens().Any(t => !t.IsCurrent) ? CreateEntry(k, acquire) : currentEntry));
// Bubble up volatile tokens to parent context // 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()) foreach (var token in entry.GetTokens())
_acquireContextContext.Instance.Monitor(token); _cacheContextAccessor.Current.Monitor(token);
} }
return entry.Result; return entry.Result;
@@ -37,14 +37,14 @@ namespace Orchard.Caching {
IAcquireContext parentContext = null; IAcquireContext parentContext = null;
try { try {
// Push context // Push context
parentContext = _acquireContextContext.Instance; parentContext = _cacheContextAccessor.Current;
_acquireContextContext.Instance = context; _cacheContextAccessor.Current = context;
entry.Result = acquire(context); entry.Result = acquire(context);
} }
finally { finally {
// Pop context // Pop context
_acquireContextContext.Instance = parentContext; _cacheContextAccessor.Current = parentContext;
} }
return entry; return entry;
} }

View File

@@ -1,56 +0,0 @@
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

@@ -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; }
}
}
}

View File

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

View File

@@ -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<TResult> RunInParallel<T, TResult>(IEnumerable<T> source, Func<T, TResult> 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;
}
/// <summary>
/// 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).
/// </summary>
public ITask<T> CreateContextAwareTask<T>(Func<T> function) {
return new TaskWithAcquireContext<T>(_cacheContextAccessor, function);
}
public class TaskWithAcquireContext<T> : ITask<T> {
private readonly ICacheContextAccessor _cacheContextAccessor;
private readonly Func<T> _function;
private IList<IVolatileToken> _tokens;
public TaskWithAcquireContext(ICacheContextAccessor cacheContextAccessor, Func<T> function) {
_cacheContextAccessor = cacheContextAccessor;
_function = function;
}
/// <summary>
/// Execute task and collect eventual volatile tokens
/// </summary>
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;
}
}
}
/// <summary>
/// Return tokens collected during task execution
/// </summary>
public IEnumerable<IVolatileToken> Tokens {
get {
if (_tokens == null)
return Enumerable.Empty<IVolatileToken>();
return _tokens;
}
}
public void Dispose() {
Finish();
}
/// <summary>
/// Forward collected tokens to current cache context
/// </summary>
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<IVolatileToken>();
_tokens.Add(token);
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace Orchard.Caching {
/// <summary>
/// Provides services to enable parallel tasks aware of the current cache context.
/// </summary>
public interface IParallelCacheContext {
/// <summary>
/// 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).
/// </summary>
ITask<T> CreateContextAwareTask<T>(Func<T> function);
IEnumerable<TResult> RunInParallel<T, TResult>(IEnumerable<T> source, Func<T, TResult> selector);
}
public interface ITask<T> : IDisposable {
/// <summary>
/// Execute task and collect eventual volatile tokens
/// </summary>
T Execute();
/// <summary>
/// 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.
/// </summary>
IEnumerable<IVolatileToken> Tokens { get; }
/// <summary>
/// Forward collected tokens to current cache context
/// </summary>
void Finish();
}
}

View File

@@ -15,6 +15,7 @@ namespace Orchard.Environment.Extensions {
private readonly IEnumerable<IExtensionFolders> _folders; private readonly IEnumerable<IExtensionFolders> _folders;
private readonly IAsyncTokenProvider _asyncTokenProvider; private readonly IAsyncTokenProvider _asyncTokenProvider;
private readonly ICacheManager _cacheManager; private readonly ICacheManager _cacheManager;
private readonly IParallelCacheContext _parallelCacheContext;
private readonly IEnumerable<IExtensionLoader> _loaders; private readonly IEnumerable<IExtensionLoader> _loaders;
public Localizer T { get; set; } public Localizer T { get; set; }
@@ -24,11 +25,13 @@ namespace Orchard.Environment.Extensions {
IEnumerable<IExtensionFolders> folders, IEnumerable<IExtensionFolders> folders,
IEnumerable<IExtensionLoader> loaders, IEnumerable<IExtensionLoader> loaders,
ICacheManager cacheManager, ICacheManager cacheManager,
IParallelCacheContext parallelCacheContext,
IAsyncTokenProvider asyncTokenProvider) { IAsyncTokenProvider asyncTokenProvider) {
_folders = folders; _folders = folders;
_asyncTokenProvider = asyncTokenProvider; _asyncTokenProvider = asyncTokenProvider;
_cacheManager = cacheManager; _cacheManager = cacheManager;
_parallelCacheContext = parallelCacheContext;
_loaders = loaders.OrderBy(x => x.Order).ToArray(); _loaders = loaders.OrderBy(x => x.Order).ToArray();
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
@@ -42,16 +45,18 @@ namespace Orchard.Environment.Extensions {
public IEnumerable<ExtensionDescriptor> AvailableExtensions() { public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
return _cacheManager.Get("AvailableExtensions", ctx => return _cacheManager.Get("AvailableExtensions", ctx =>
_folders _parallelCacheContext
.AsParallel() // Execute in parallel for each folder .RunInParallel(_folders, folder => folder.AvailableExtensions())
.SelectMany(folder => folder.AvailableExtensions()) .SelectMany(descriptors => descriptors)
.ToList() // Force execution inside the cache entry .ToReadOnlyCollection());
);
} }
public IEnumerable<FeatureDescriptor> AvailableFeatures() { public IEnumerable<FeatureDescriptor> AvailableFeatures() {
return _cacheManager.Get("AvailableFeatures", ctx => 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) { internal static int GetPriority(FeatureDescriptor featureDescriptor) {

View File

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

View File

@@ -148,8 +148,10 @@
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Caching\DefaultAcquireContextContext.cs" /> <Compile Include="Caching\DefaultCacheContextAccessor.cs" />
<Compile Include="Caching\IAcquireContextContext.cs" /> <Compile Include="Caching\DefaultParallelCacheContext.cs" />
<Compile Include="Caching\ICacheContextAccessor.cs" />
<Compile Include="Caching\IParallelCacheContext.cs" />
<Compile Include="ContentManagement\ContentIdentity.cs" /> <Compile Include="ContentManagement\ContentIdentity.cs" />
<Compile Include="ContentManagement\ContentItemBehavior.cs" /> <Compile Include="ContentManagement\ContentItemBehavior.cs" />
<Compile Include="ContentManagement\ContentPartBehavior.cs" /> <Compile Include="ContentManagement\ContentPartBehavior.cs" />