mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-04-05 21:01:35 +08:00
Concurrency fixes (#8054)
Fixes #7720 Fixes #8019 Fixes #7708 Fixes #8025 Fixes #8028 Fixes #8040 Fixes #8052
This commit is contained in:
parent
6e6c5ac1e6
commit
1f77745888
@ -11,6 +11,7 @@ using Orchard.ContentManagement.Handlers;
|
|||||||
using Orchard.ContentManagement.Records;
|
using Orchard.ContentManagement.Records;
|
||||||
using Orchard.Data;
|
using Orchard.Data;
|
||||||
using Orchard.Environment;
|
using Orchard.Environment;
|
||||||
|
using Orchard.Locking;
|
||||||
using Orchard.Security;
|
using Orchard.Security;
|
||||||
using Orchard.Tags.Handlers;
|
using Orchard.Tags.Handlers;
|
||||||
using Orchard.Tags.Models;
|
using Orchard.Tags.Models;
|
||||||
@ -40,6 +41,7 @@ namespace Orchard.Tests.Modules.Tags.Services {
|
|||||||
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
|
||||||
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
|
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
|
||||||
builder.RegisterType<Signals>().As<ISignals>();
|
builder.RegisterType<Signals>().As<ISignals>();
|
||||||
|
builder.RegisterType<LockingProvider>().As<ILockingProvider>();
|
||||||
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
|
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ namespace Orchard.Tests.Stubs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string SiteName {
|
public string SiteName {
|
||||||
get { throw new NotImplementedException(); }
|
get { return "TestSite"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SiteSalt {
|
public string SiteSalt {
|
||||||
|
@ -5,6 +5,7 @@ using Orchard.ContentManagement;
|
|||||||
using Orchard.ContentManagement.Handlers;
|
using Orchard.ContentManagement.Handlers;
|
||||||
using Orchard.Data;
|
using Orchard.Data;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
|
using Orchard.Locking;
|
||||||
using Orchard.UI.Notify;
|
using Orchard.UI.Notify;
|
||||||
|
|
||||||
namespace Orchard.Autoroute.Handlers {
|
namespace Orchard.Autoroute.Handlers {
|
||||||
@ -13,19 +14,22 @@ namespace Orchard.Autoroute.Handlers {
|
|||||||
private readonly Lazy<IAutorouteService> _autorouteService;
|
private readonly Lazy<IAutorouteService> _autorouteService;
|
||||||
private readonly IOrchardServices _orchardServices;
|
private readonly IOrchardServices _orchardServices;
|
||||||
private readonly IHomeAliasService _homeAliasService;
|
private readonly IHomeAliasService _homeAliasService;
|
||||||
|
private readonly ILockingProvider _lockingProvider;
|
||||||
|
|
||||||
public Localizer T { get; set; }
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
public AutoroutePartHandler(
|
public AutoroutePartHandler(
|
||||||
IRepository<AutoroutePartRecord> autoroutePartRepository,
|
IRepository<AutoroutePartRecord> autoroutePartRepository,
|
||||||
Lazy<IAutorouteService> autorouteService,
|
Lazy<IAutorouteService> autorouteService,
|
||||||
IOrchardServices orchardServices,
|
IOrchardServices orchardServices,
|
||||||
IHomeAliasService homeAliasService) {
|
IHomeAliasService homeAliasService,
|
||||||
|
ILockingProvider lockingProvider) {
|
||||||
|
|
||||||
Filters.Add(StorageFilter.For(autoroutePartRepository));
|
Filters.Add(StorageFilter.For(autoroutePartRepository));
|
||||||
_autorouteService = autorouteService;
|
_autorouteService = autorouteService;
|
||||||
_orchardServices = orchardServices;
|
_orchardServices = orchardServices;
|
||||||
_homeAliasService = homeAliasService;
|
_homeAliasService = homeAliasService;
|
||||||
|
_lockingProvider = lockingProvider;
|
||||||
|
|
||||||
OnUpdated<AutoroutePart>((ctx, part) => CreateAlias(part));
|
OnUpdated<AutoroutePart>((ctx, part) => CreateAlias(part));
|
||||||
|
|
||||||
@ -50,53 +54,74 @@ namespace Orchard.Autoroute.Handlers {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _lockString = "";
|
||||||
|
private string LockString {
|
||||||
|
get {
|
||||||
|
if (string.IsNullOrWhiteSpace(_lockString)) {
|
||||||
|
_lockString = string.Join(
|
||||||
|
_orchardServices.WorkContext?.CurrentSite?.BaseUrl ?? "",
|
||||||
|
_orchardServices.WorkContext?.CurrentSite?.SiteName ?? "",
|
||||||
|
"Orchard.Autoroute.Handlers.AutoroutePartHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _lockString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CreateAlias(AutoroutePart part) {
|
private void CreateAlias(AutoroutePart part) {
|
||||||
ProcessAlias(part);
|
ProcessAlias(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PublishAlias(AutoroutePart part) {
|
private void PublishAlias(AutoroutePart part) {
|
||||||
ProcessAlias(part);
|
_lockingProvider.Lock(LockString, () => {
|
||||||
|
ProcessAlias(part);
|
||||||
|
|
||||||
// Should it become the home page?
|
// Should it become the home page?
|
||||||
if (part.PromoteToHomePage) {
|
if (part.PromoteToHomePage) {
|
||||||
// Get the current homepage an unmark it as the homepage.
|
// Get the current homepage an unmark it as the homepage.
|
||||||
var currentHomePage = _homeAliasService.GetHomePage(VersionOptions.Latest);
|
var currentHomePage = _homeAliasService.GetHomePage(VersionOptions.Latest);
|
||||||
if(currentHomePage != null && currentHomePage.Id != part.Id) {
|
if (currentHomePage != null && currentHomePage.Id != part.Id) {
|
||||||
var autoroutePart = currentHomePage.As<AutoroutePart>();
|
var autoroutePart = currentHomePage.As<AutoroutePart>();
|
||||||
|
|
||||||
if (autoroutePart != null) {
|
if (autoroutePart != null) {
|
||||||
autoroutePart.PromoteToHomePage = false;
|
autoroutePart.PromoteToHomePage = false;
|
||||||
if(autoroutePart.IsPublished())
|
if (autoroutePart.IsPublished())
|
||||||
_orchardServices.ContentManager.Publish(autoroutePart.ContentItem);
|
_orchardServices.ContentManager.Publish(autoroutePart.ContentItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the home alias to point to this item being published.
|
||||||
|
_homeAliasService.PublishHomeAlias(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the home alias to point to this item being published.
|
_autorouteService.Value.PublishAlias(part);
|
||||||
_homeAliasService.PublishHomeAlias(part);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
_autorouteService.Value.PublishAlias(part);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessAlias(AutoroutePart part) {
|
private void ProcessAlias(AutoroutePart part) {
|
||||||
|
LocalizedString message = null;
|
||||||
// Generate an alias if one as not already been entered.
|
// Generate an alias if one as not already been entered.
|
||||||
if (String.IsNullOrWhiteSpace(part.DisplayAlias)) {
|
if (String.IsNullOrWhiteSpace(part.DisplayAlias)) {
|
||||||
part.DisplayAlias = _autorouteService.Value.GenerateAlias(part);
|
part.DisplayAlias = _autorouteService.Value.GenerateAlias(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the generated alias is empty, compute a new one.
|
|
||||||
if (String.IsNullOrWhiteSpace(part.DisplayAlias)) {
|
if (String.IsNullOrWhiteSpace(part.DisplayAlias)) {
|
||||||
_autorouteService.Value.ProcessPath(part);
|
_autorouteService.Value.ProcessPath(part);
|
||||||
_orchardServices.Notifier.Warning(T("The permalink could not be generated, a new slug has been defined: \"{0}\"", part.Path));
|
message = T("The permalink could not be generated, a new slug has been defined: \"{0}\"", part.Path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for permalink conflict, unless we are trying to set the home page.
|
// Check for permalink conflict, unless we are trying to set the home page.
|
||||||
var previous = part.Path;
|
var previous = part.Path;
|
||||||
if (!_autorouteService.Value.ProcessPath(part))
|
if (!_autorouteService.Value.ProcessPath(part)) {
|
||||||
_orchardServices.Notifier.Warning(
|
message =
|
||||||
T("Permalinks in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
|
T("Permalinks in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
|
||||||
previous, part.Path, part.ContentItem.ContentType));
|
previous, part.Path, part.ContentItem.ContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
_orchardServices.Notifier.Warning(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveAlias(AutoroutePart part) {
|
void RemoveAlias(AutoroutePart part) {
|
||||||
@ -106,7 +131,10 @@ namespace Orchard.Autoroute.Handlers {
|
|||||||
if (part.ContentItem.Id == homePageId) {
|
if (part.ContentItem.Id == homePageId) {
|
||||||
_orchardServices.Notifier.Warning(T("You removed the content item that served as the site's home page. \nMost possibly this means that instead of the home page a \"404 Not Found\" page will be displayed. \n\nTo prevent this you can e.g. publish a content item that has the \"Set as home page\" checkbox ticked."));
|
_orchardServices.Notifier.Warning(T("You removed the content item that served as the site's home page. \nMost possibly this means that instead of the home page a \"404 Not Found\" page will be displayed. \n\nTo prevent this you can e.g. publish a content item that has the \"Set as home page\" checkbox ticked."));
|
||||||
}
|
}
|
||||||
_autorouteService.Value.RemoveAliases(part);
|
|
||||||
|
_lockingProvider.Lock(LockString, () => {
|
||||||
|
_autorouteService.Value.RemoveAliases(part);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,7 @@
|
|||||||
<Compile Include="Recipes\Executors\HomeAliasStep.cs" />
|
<Compile Include="Recipes\Executors\HomeAliasStep.cs" />
|
||||||
<Compile Include="ResourceManifest.cs" />
|
<Compile Include="ResourceManifest.cs" />
|
||||||
<Compile Include="Services\AliasResolverSelector.cs" />
|
<Compile Include="Services\AliasResolverSelector.cs" />
|
||||||
|
<Compile Include="Services\AutorouteNoLockTableProvider.cs" />
|
||||||
<Compile Include="Services\HomeAliasService.cs" />
|
<Compile Include="Services\HomeAliasService.cs" />
|
||||||
<Compile Include="Services\IHomeAliasService.cs" />
|
<Compile Include="Services\IHomeAliasService.cs" />
|
||||||
<Compile Include="Services\PathResolutionService.cs" />
|
<Compile Include="Services\PathResolutionService.cs" />
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using Orchard.Data.Providers;
|
||||||
|
|
||||||
|
namespace Orchard.Autoroute.Services {
|
||||||
|
public class AutorouteNoLockTableProvider : INoLockTableProvider {
|
||||||
|
public IEnumerable<string> GetTableNames() {
|
||||||
|
return new string[] { "Orchard_Autoroute_AutoroutePartRecord" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ using Orchard.ContentManagement;
|
|||||||
using Orchard.Security;
|
using Orchard.Security;
|
||||||
using Orchard.Tags.Models;
|
using Orchard.Tags.Models;
|
||||||
using Orchard.UI.Notify;
|
using Orchard.UI.Notify;
|
||||||
|
using Orchard.Locking;
|
||||||
|
|
||||||
namespace Orchard.Tags.Services {
|
namespace Orchard.Tags.Services {
|
||||||
public class TagService : ITagService {
|
public class TagService : ITagService {
|
||||||
@ -18,19 +19,24 @@ namespace Orchard.Tags.Services {
|
|||||||
private readonly IAuthorizationService _authorizationService;
|
private readonly IAuthorizationService _authorizationService;
|
||||||
private readonly IOrchardServices _orchardServices;
|
private readonly IOrchardServices _orchardServices;
|
||||||
private readonly ISessionFactoryHolder _sessionFactoryHolder;
|
private readonly ISessionFactoryHolder _sessionFactoryHolder;
|
||||||
|
private readonly ILockingProvider _lockingProvider;
|
||||||
|
|
||||||
public TagService(IRepository<TagRecord> tagRepository,
|
public TagService(IRepository<TagRecord> tagRepository,
|
||||||
IRepository<ContentTagRecord> contentTagRepository,
|
IRepository<ContentTagRecord> contentTagRepository,
|
||||||
INotifier notifier,
|
INotifier notifier,
|
||||||
IAuthorizationService authorizationService,
|
IAuthorizationService authorizationService,
|
||||||
IOrchardServices orchardServices,
|
IOrchardServices orchardServices,
|
||||||
ISessionFactoryHolder sessionFactoryHolder) {
|
ISessionFactoryHolder sessionFactoryHolder,
|
||||||
|
ILockingProvider lockingProvider) {
|
||||||
|
|
||||||
_tagRepository = tagRepository;
|
_tagRepository = tagRepository;
|
||||||
_contentTagRepository = contentTagRepository;
|
_contentTagRepository = contentTagRepository;
|
||||||
_notifier = notifier;
|
_notifier = notifier;
|
||||||
_authorizationService = authorizationService;
|
_authorizationService = authorizationService;
|
||||||
_orchardServices = orchardServices;
|
_orchardServices = orchardServices;
|
||||||
_sessionFactoryHolder = sessionFactoryHolder;
|
_sessionFactoryHolder = sessionFactoryHolder;
|
||||||
|
_lockingProvider = lockingProvider;
|
||||||
|
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
}
|
}
|
||||||
@ -56,11 +62,23 @@ namespace Orchard.Tags.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TagRecord CreateTag(string tagName) {
|
public TagRecord CreateTag(string tagName) {
|
||||||
var result = _tagRepository.Get(x => x.TagName == tagName);
|
TagRecord result = null;
|
||||||
if (result == null) {
|
|
||||||
result = new TagRecord { TagName = tagName };
|
var lockString = string.Join(".",
|
||||||
_tagRepository.Create(result);
|
_orchardServices.WorkContext?.CurrentSite?.BaseUrl ?? "",
|
||||||
}
|
_orchardServices.WorkContext?.CurrentSite?.SiteName ?? "",
|
||||||
|
"TagService.CreateTag",
|
||||||
|
tagName);
|
||||||
|
|
||||||
|
_lockingProvider.Lock(lockString,
|
||||||
|
() => {
|
||||||
|
result = _tagRepository.Get(x => x.TagName == tagName);
|
||||||
|
if (result == null) {
|
||||||
|
result = new TagRecord { TagName = tagName };
|
||||||
|
_tagRepository.Create(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
src/Orchard/Data/NoLockInterceptor.cs
Normal file
101
src/Orchard/Data/NoLockInterceptor.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NHibernate;
|
||||||
|
using NHibernate.SqlCommand;
|
||||||
|
using Orchard.Data.Providers;
|
||||||
|
using Orchard.Environment.Configuration;
|
||||||
|
|
||||||
|
namespace Orchard.Data {
|
||||||
|
public class NoLockInterceptor : EmptyInterceptor, ISessionInterceptor {
|
||||||
|
|
||||||
|
private readonly ShellSettings _shellSettings;
|
||||||
|
private readonly IEnumerable<INoLockTableProvider> _noLockTableProviders;
|
||||||
|
|
||||||
|
public NoLockInterceptor(
|
||||||
|
ShellSettings shellSettings,
|
||||||
|
IEnumerable<INoLockTableProvider> noLockTableProviders) {
|
||||||
|
|
||||||
|
_shellSettings = shellSettings;
|
||||||
|
_noLockTableProviders = noLockTableProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> _tableNames;
|
||||||
|
public List<string> TableNames {
|
||||||
|
get {
|
||||||
|
if (_tableNames == null) {
|
||||||
|
_tableNames = new List<string>(
|
||||||
|
_noLockTableProviders
|
||||||
|
.SelectMany(nltp => nltp.GetTableNames())
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(n => GetPrefixedTableName(n.Trim())));
|
||||||
|
}
|
||||||
|
return _tableNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPrefixedTableName(string tableName) {
|
||||||
|
if (string.IsNullOrWhiteSpace(_shellSettings.DataTablePrefix)) {
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _shellSettings.DataTablePrefix + "_" + tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on https://stackoverflow.com/a/39518098/2669614
|
||||||
|
public override SqlString OnPrepareStatement(SqlString sql) {
|
||||||
|
|
||||||
|
// Modify the sql to add hints
|
||||||
|
if (sql.StartsWithCaseInsensitive("select")) {
|
||||||
|
var parts = sql.ToString().Split().ToList();
|
||||||
|
var fromItem = parts.FirstOrDefault(p => p.Trim().Equals("from", StringComparison.OrdinalIgnoreCase));
|
||||||
|
int fromIndex = fromItem != null ? parts.IndexOf(fromItem) : -1;
|
||||||
|
var whereItem = parts.FirstOrDefault(p => p.Trim().Equals("where", StringComparison.OrdinalIgnoreCase));
|
||||||
|
int whereIndex = whereItem != null ? parts.IndexOf(whereItem) : parts.Count;
|
||||||
|
|
||||||
|
if (fromIndex == -1)
|
||||||
|
return sql;
|
||||||
|
|
||||||
|
foreach (var tableName in TableNames) {
|
||||||
|
// set NOLOCK for each one of these tables
|
||||||
|
var tableItem = parts
|
||||||
|
.FirstOrDefault(p => p.Trim()
|
||||||
|
.Equals(tableName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (tableItem != null) {
|
||||||
|
// the table is involved in this statement
|
||||||
|
var tableIndex = parts.IndexOf(tableItem);
|
||||||
|
// recompute whereIndex in case we added stuff to parts
|
||||||
|
whereIndex = whereItem != null ? parts.IndexOf(whereItem) : parts.Count;
|
||||||
|
if (tableIndex > fromIndex && tableIndex < whereIndex) { // sanity check
|
||||||
|
// if before the table name we have "," or "FROM", this is not a join, but rather
|
||||||
|
// something like "FROM tableName alias ..."
|
||||||
|
// we can insert "WITH (NOLOCK)" after that
|
||||||
|
if (tableIndex == fromIndex + 1
|
||||||
|
|| parts[tableIndex - 1].Equals(",")) {
|
||||||
|
|
||||||
|
parts.Insert(tableIndex + 2, "WITH (NOLOCK)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// probably doing a join, so edit the next "on" and make it
|
||||||
|
// "WITH (NOLOCK) on"
|
||||||
|
for (int i = tableIndex + 1; i < whereIndex; i++) {
|
||||||
|
if (parts[i].Trim().Equals("on", StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
parts[i] = "WITH (NOLOCK) on";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MUST use SqlString.Parse() method instead of new SqlString()
|
||||||
|
sql = SqlString.Parse(string.Join(" ", parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/Orchard/Data/Providers/DefaultNoLockTableProvider.cs
Normal file
37
src/Orchard/Data/Providers/DefaultNoLockTableProvider.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Orchard.Data.Providers {
|
||||||
|
public class DefaultNoLockTableProvider : INoLockTableProvider {
|
||||||
|
|
||||||
|
public DefaultNoLockTableProvider() {
|
||||||
|
|
||||||
|
// We may use AutoFac to override the default tables:
|
||||||
|
/*
|
||||||
|
<component instance-scope="per-lifetime-scope"
|
||||||
|
type="Orchard.Data.Providers.DefaultNoLockTableProvider, Orchard.Framework"
|
||||||
|
service="Orchard..Data.Providers.INoLockTableProvider">
|
||||||
|
<properties>
|
||||||
|
<property name="TableName" value="Table_Name_1, Table_Name_2" />
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
*/
|
||||||
|
TableNames = "Orchard_Framework_ContentItemVersionRecord, Title_TitlePartRecord, Orchard_Framework_ContentItemRecord";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TableNames { get; set; }
|
||||||
|
|
||||||
|
private IEnumerable<string> _tableNames;
|
||||||
|
|
||||||
|
public IEnumerable<string> GetTableNames() {
|
||||||
|
if (_tableNames == null) {
|
||||||
|
_tableNames = new List<string>(TableNames
|
||||||
|
.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries));
|
||||||
|
}
|
||||||
|
return _tableNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/Orchard/Data/Providers/INoLockTableProvider.cs
Normal file
18
src/Orchard/Data/Providers/INoLockTableProvider.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Orchard.Data.Providers {
|
||||||
|
public interface INoLockTableProvider : IDependency {
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the names of the tables from which read operations should ignore shared locks.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An IEnumerable<string> with the table names.</string></returns>
|
||||||
|
/// <remarks>Implementations of this should not have to worry about table prefixes used to
|
||||||
|
/// discriminate between tenants sharing a db. That should be taken care of, if needed, where
|
||||||
|
/// the results of this method are used. Implementations of this should avoid returning null.</remarks>
|
||||||
|
IEnumerable<string> GetTableNames();
|
||||||
|
}
|
||||||
|
}
|
237
src/Orchard/Locking/ILockingProvider.cs
Normal file
237
src/Orchard/Locking/ILockingProvider.cs
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Orchard.Locking {
|
||||||
|
public interface ILockingProvider : IDependency {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given object to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The object upon which the lock will be created.</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">Throws an ArgumentNullException the lockOn parameter is null.</exception>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
void Lock(
|
||||||
|
object lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given string to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The string upon which the lock will be created.</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
void Lock(
|
||||||
|
string lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given object to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The object upon which the lock will be created.</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <returns>true if the current thread acquires the lock; otherwise, false.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Throws an ArgumentNullException the lockOn parameter is null.</exception>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
bool TryLock(
|
||||||
|
object lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given string to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The string upon which the lock will be created.</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <returns>true if the current thread acquires the lock; otherwise, false.</returns>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
bool TryLock(
|
||||||
|
string lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given object to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The object upon which the lock will be created.</param>
|
||||||
|
/// <param name="timeout">A TimeSpan representing the amount of time to wait for the lock. A value
|
||||||
|
/// of –1 millisecond specifies an infinite wait.</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <returns>true if the current thread acquires the lock; otherwise, false.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Throws an ArgumentNullException the lockOn parameter is null.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Throws an ArgumentOutOfRangeException if the value of timeout
|
||||||
|
/// in milliseconds is negative and is not equal to Infinite (-1 millisecond), or is greater than MaxValue.</exception>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
bool TryLock(
|
||||||
|
object lockOn,
|
||||||
|
TimeSpan timeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given string to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The string upon which the lock will be created.</param>
|
||||||
|
/// <param name="timeout">A TimeSpan representing the amount of time to wait for the lock. A value
|
||||||
|
/// of –1 millisecond specifies an infinite wait.</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <returns>true if the current thread acquires the lock; otherwise, false.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Throws an ArgumentNullException the lockOn parameter is null.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Throws an ArgumentOutOfRangeException if the value of timeout
|
||||||
|
/// in milliseconds is negative and is not equal to Infinite (-1 millisecond), or is greater than MaxValue.</exception>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
bool TryLock(
|
||||||
|
string lockOn,
|
||||||
|
TimeSpan timeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given object to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The object upon which the lock will be created.</param>
|
||||||
|
/// <param name="millisecondsTimeout">The number of milliseconds to wait for the lock..</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <returns>true if the current thread acquires the lock; otherwise, false.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Throws an ArgumentNullException the lockOn parameter is null.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Throws an ArgumentOutOfRangeException if the value
|
||||||
|
/// of millisecondsTimeout is negative, and not equal to Infinite.</exception>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
bool TryLock(
|
||||||
|
object lockOn,
|
||||||
|
int millisecondsTimeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles locking on a given string to execute the desired critical code. Optionally, it is possible
|
||||||
|
/// to tell how to manage exceptions, e.g. in a case where some cleanup may be required when the
|
||||||
|
/// critical code fails after having been partially executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockOn">The string upon which the lock will be created.</param>
|
||||||
|
/// <param name="millisecondsTimeout">The number of milliseconds to wait for the lock..</param>
|
||||||
|
/// <param name="criticalCode">The critical code to be executed while holding the lock.</param>
|
||||||
|
/// <param name="innerHandler">The (optional) action to be executed to handle exceptions while still
|
||||||
|
/// holding the lock.</param>
|
||||||
|
/// <param name="outerHandler">The (optional) action to be executed to handle exceptions after the
|
||||||
|
/// lock has been released.</param>
|
||||||
|
/// <returns>true if the current thread acquires the lock; otherwise, false.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Throws an ArgumentNullException the lockOn parameter is null.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Throws an ArgumentOutOfRangeException if the value
|
||||||
|
/// of millisecondsTimeout is negative, and not equal to Infinite.</exception>
|
||||||
|
/// <remarks>Internally, this method uses System.Threading.Monitor to delimit the execution of the
|
||||||
|
/// critical code. Unlike the implementation of lock(obj){}, this implementation allows handling of
|
||||||
|
/// exceptions both while holding and after releasing the lock on the object. The default behaviour
|
||||||
|
/// if both the Actions to handle exceptions are null, this method is the same as calling
|
||||||
|
/// lock(obj){criticalCode();}, meaning that it will bubble out the exception while holding the lock, and
|
||||||
|
/// only release it afterwards. If an innerHandler is provided, but outerHandler is null, an exception will
|
||||||
|
/// bubble out after the lock is released. To prevent exceptions from being thrown, both innerHandler and
|
||||||
|
/// outerHandler should be provided.</remarks>
|
||||||
|
bool TryLock(
|
||||||
|
string lockOn,
|
||||||
|
int millisecondsTimeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
251
src/Orchard/Locking/LockingProvider.cs
Normal file
251
src/Orchard/Locking/LockingProvider.cs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.Logging;
|
||||||
|
|
||||||
|
namespace Orchard.Locking {
|
||||||
|
public class LockingProvider : ILockingProvider {
|
||||||
|
|
||||||
|
public LockingProvider() {
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
|
T = NullLocalizer.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
|
public void Lock(
|
||||||
|
object lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
LockInternal(lockOn, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Lock(
|
||||||
|
string lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
LockInternal(String.Intern(lockOn), criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLock(
|
||||||
|
object lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
return TryLockInternal(lockOn, TimeSpan.Zero, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLock(
|
||||||
|
string lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
return TryLockInternal(String.Intern(lockOn), TimeSpan.Zero, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLock(
|
||||||
|
object lockOn,
|
||||||
|
TimeSpan timeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
return TryLockInternal(lockOn, timeout, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLock(
|
||||||
|
string lockOn,
|
||||||
|
TimeSpan timeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
return TryLockInternal(String.Intern(lockOn), timeout, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLock(
|
||||||
|
object lockOn,
|
||||||
|
int millisecondsTimeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
return TryLockInternal(lockOn, millisecondsTimeout, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLock(
|
||||||
|
string lockOn,
|
||||||
|
int millisecondsTimeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
return TryLockInternal(String.Intern(lockOn), millisecondsTimeout, criticalCode, innerHandler, outerHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LockInternal(
|
||||||
|
object lockOn,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
bool taken = false;
|
||||||
|
var tmp = lockOn;
|
||||||
|
Exception outerException = null;
|
||||||
|
try {
|
||||||
|
Monitor.Enter(tmp, ref taken);
|
||||||
|
criticalCode?.Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
outerException = ex;
|
||||||
|
CleanLog(ex);
|
||||||
|
if (innerHandler != null) {
|
||||||
|
innerHandler.Invoke(ex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (outerHandler == null) {
|
||||||
|
// if both the handlers are null, the methods should behave like lock(tmp){}
|
||||||
|
// and only bubble out the exception while holding the lock.
|
||||||
|
outerException = null;
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (taken) {
|
||||||
|
Monitor.Exit(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if there was an handler for the exception to be used in the critical section
|
||||||
|
// (i.e. innerHandler != null) we have further handling here. This may simply mean throwing
|
||||||
|
// the exception out when outerHandler == null
|
||||||
|
if (outerException != null) {
|
||||||
|
if (outerHandler != null) {
|
||||||
|
outerHandler.Invoke(outerException);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw outerException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryLockInternal(
|
||||||
|
object lockOn,
|
||||||
|
TimeSpan timeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
var tmp = lockOn;
|
||||||
|
Exception outerException = null;
|
||||||
|
|
||||||
|
if (Monitor.TryEnter(tmp, timeout)) {
|
||||||
|
try {
|
||||||
|
criticalCode?.Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
outerException = ex;
|
||||||
|
CleanLog(ex);
|
||||||
|
if (innerHandler != null) {
|
||||||
|
innerHandler.Invoke(ex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (outerHandler == null) {
|
||||||
|
// if both the handlers are null, the methods should behave like lock(tmp){}
|
||||||
|
// and only bubble out the exception while holding the lock.
|
||||||
|
outerException = null;
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Monitor.Exit(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if there was an handler for the exception to be used in the critical section
|
||||||
|
// (i.e. innerHandler != null) we have further handling here. This may simply mean throwing
|
||||||
|
// the exception out when outerHandler == null
|
||||||
|
if (outerException != null) {
|
||||||
|
if (outerHandler != null) {
|
||||||
|
outerHandler.Invoke(outerException);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw outerException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryLockInternal(
|
||||||
|
object lockOn,
|
||||||
|
int millisecondsTimeout,
|
||||||
|
Action criticalCode,
|
||||||
|
Action<Exception> innerHandler = null,
|
||||||
|
Action<Exception> outerHandler = null) {
|
||||||
|
|
||||||
|
var tmp = lockOn;
|
||||||
|
Exception outerException = null;
|
||||||
|
|
||||||
|
if (Monitor.TryEnter(tmp, millisecondsTimeout)) {
|
||||||
|
try {
|
||||||
|
criticalCode?.Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
outerException = ex;
|
||||||
|
CleanLog(ex);
|
||||||
|
if (innerHandler != null) {
|
||||||
|
innerHandler.Invoke(ex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (outerHandler == null) {
|
||||||
|
// if both the handlers are null, the methods should behave like lock(tmp){}
|
||||||
|
// and only bubble out the exception while holding the lock.
|
||||||
|
outerException = null;
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Monitor.Exit(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if there was an handler for the exception to be used in the critical section
|
||||||
|
// (i.e. innerHandler != null) we have further handling here. This may simply mean throwing
|
||||||
|
// the exception out when outerHandler == null
|
||||||
|
if (outerException != null) {
|
||||||
|
if (outerHandler != null) {
|
||||||
|
outerHandler.Invoke(outerException);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw outerException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanLog(Exception ex) {
|
||||||
|
try {
|
||||||
|
Logger.Log(Logging.LogLevel.Error, ex, T("Exception while running critical code").Text);
|
||||||
|
}
|
||||||
|
catch (Exception) {
|
||||||
|
// prevent messing things up if the logger fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -176,6 +176,9 @@
|
|||||||
<Compile Include="ContentManagement\Extensions\DriverResultExtensions.cs" />
|
<Compile Include="ContentManagement\Extensions\DriverResultExtensions.cs" />
|
||||||
<Compile Include="ContentManagement\Handlers\CloneContentContext.cs" />
|
<Compile Include="ContentManagement\Handlers\CloneContentContext.cs" />
|
||||||
<Compile Include="ContentManagement\IGlobalCriteriaProvider.cs" />
|
<Compile Include="ContentManagement\IGlobalCriteriaProvider.cs" />
|
||||||
|
<Compile Include="Data\NoLockInterceptor.cs" />
|
||||||
|
<Compile Include="Data\Providers\DefaultNoLockTableProvider.cs" />
|
||||||
|
<Compile Include="Data\Providers\INoLockTableProvider.cs" />
|
||||||
<Compile Include="DisplayManagement\Descriptors\ShapePlacementStrategy\DefaultPlacementParseMatchProviders.cs" />
|
<Compile Include="DisplayManagement\Descriptors\ShapePlacementStrategy\DefaultPlacementParseMatchProviders.cs" />
|
||||||
<Compile Include="DisplayManagement\Descriptors\ShapePlacementStrategy\IPlacementParseMatchProvider.cs" />
|
<Compile Include="DisplayManagement\Descriptors\ShapePlacementStrategy\IPlacementParseMatchProvider.cs" />
|
||||||
<Compile Include="Environment\Configuration\ExtensionLocations.cs" />
|
<Compile Include="Environment\Configuration\ExtensionLocations.cs" />
|
||||||
@ -185,6 +188,8 @@
|
|||||||
<Compile Include="Data\Migration\Schema\DropUniqueConstraintCommand.cs" />
|
<Compile Include="Data\Migration\Schema\DropUniqueConstraintCommand.cs" />
|
||||||
<Compile Include="Environment\Extensions\Models\LifecycleStatus.cs" />
|
<Compile Include="Environment\Extensions\Models\LifecycleStatus.cs" />
|
||||||
<Compile Include="Environment\ShellBuilders\ICompositionStrategy.cs" />
|
<Compile Include="Environment\ShellBuilders\ICompositionStrategy.cs" />
|
||||||
|
<Compile Include="Locking\ILockingProvider.cs" />
|
||||||
|
<Compile Include="Locking\LockingProvider.cs" />
|
||||||
<Compile Include="Mvc\Updater.cs" />
|
<Compile Include="Mvc\Updater.cs" />
|
||||||
<Compile Include="Recipes\Models\ConfigurationContext.cs" />
|
<Compile Include="Recipes\Models\ConfigurationContext.cs" />
|
||||||
<Compile Include="Recipes\Models\RecipeBuilderStepConfigurationContext.cs" />
|
<Compile Include="Recipes\Models\RecipeBuilderStepConfigurationContext.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user