mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-04-05 21:01:35 +08:00
Blocking requests to tenants being restarted, thus not allowing users to see temporary YSODs
This commit is contained in:
parent
740039d6ef
commit
3dc6e6523f
@ -14,8 +14,11 @@ using Orchard.Logging;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.Utility.Extensions;
|
||||
using Orchard.Utility;
|
||||
using Orchard.Exceptions;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Orchard.Mvc;
|
||||
|
||||
namespace Orchard.Environment {
|
||||
// All the event handlers that DefaultOrchardHost implements have to be declared in OrchardStarter.
|
||||
@ -31,6 +34,7 @@ namespace Orchard.Environment {
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly static object _syncLock = new object();
|
||||
private readonly static object _shellContextsWriteLock = new object();
|
||||
private readonly NamedReaderWriterLock _shellActivationLock = new NamedReaderWriterLock();
|
||||
|
||||
private IEnumerable<ShellContext> _shellContexts;
|
||||
private readonly ContextState<IList<ShellSettings>> _tenantsToRestart;
|
||||
@ -267,10 +271,30 @@ namespace Orchard.Environment {
|
||||
protected virtual void BeginRequest() {
|
||||
BlockRequestsDuringSetup();
|
||||
|
||||
// Ensure all shell contexts are loaded, or need to be reloaded if
|
||||
// extensions have changed
|
||||
MonitorExtensions();
|
||||
BuildCurrent();
|
||||
Action ensureInitialized = () => {
|
||||
// Ensure all shell contexts are loaded, or need to be reloaded if
|
||||
// extensions have changed
|
||||
MonitorExtensions();
|
||||
BuildCurrent();
|
||||
};
|
||||
|
||||
ShellSettings currentShellSettings = null;
|
||||
|
||||
var httpContext = _httpContextAccessor.Current();
|
||||
if (httpContext != null) {
|
||||
currentShellSettings = _runningShellTable.Match(httpContext);
|
||||
}
|
||||
|
||||
if (currentShellSettings == null) {
|
||||
ensureInitialized();
|
||||
}
|
||||
else {
|
||||
_shellActivationLock.RunWithReadLock(currentShellSettings.Name, () => {
|
||||
ensureInitialized();
|
||||
});
|
||||
}
|
||||
|
||||
// StartUpdatedShells can cause a writer shell activation lock so it should run outside the reader lock.
|
||||
StartUpdatedShells();
|
||||
}
|
||||
|
||||
@ -328,19 +352,21 @@ namespace Orchard.Environment {
|
||||
}
|
||||
// reload the shell as its settings have changed
|
||||
else {
|
||||
// dispose previous context
|
||||
shellContext.Shell.Terminate();
|
||||
_shellActivationLock.RunWithWriteLock(settings.Name, () => {
|
||||
// dispose previous context
|
||||
shellContext.Shell.Terminate();
|
||||
|
||||
var context = _shellContextFactory.CreateShellContext(settings);
|
||||
var context = _shellContextFactory.CreateShellContext(settings);
|
||||
|
||||
// Activate and register modified context.
|
||||
// Forcing enumeration with ToArray() so a lazy execution isn't causing issues by accessing the disposed shell context.
|
||||
_shellContexts = _shellContexts.Where(shell => shell.Settings.Name != settings.Name).Union(new[] { context }).ToArray();
|
||||
// Activate and register modified context.
|
||||
// Forcing enumeration with ToArray() so a lazy execution isn't causing issues by accessing the disposed shell context.
|
||||
_shellContexts = _shellContexts.Where(shell => shell.Settings.Name != settings.Name).Union(new[] { context }).ToArray();
|
||||
|
||||
shellContext.Dispose();
|
||||
context.Shell.Activate();
|
||||
shellContext.Dispose();
|
||||
context.Shell.Activate();
|
||||
|
||||
_runningShellTable.Update(settings);
|
||||
_runningShellTable.Update(settings);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -699,6 +699,7 @@
|
||||
<Compile Include="Messaging\Services\IMessagingChannel.cs" />
|
||||
<Compile Include="IWorkContextAccessor.cs" />
|
||||
<Compile Include="Utility\Extensions\VirtualPathProviderExtensions.cs" />
|
||||
<Compile Include="Utility\NamedReaderWriterLock.cs" />
|
||||
<Compile Include="Utility\ReflectionHelper.cs" />
|
||||
<Compile Include="Validation\PathValidation.cs" />
|
||||
<Compile Include="Wcf\OrchardDependencyInjectionServiceBehavior.cs" />
|
||||
|
84
src/Orchard/Utility/NamedReaderWriterLock.cs
Normal file
84
src/Orchard/Utility/NamedReaderWriterLock.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Orchard.Utility {
|
||||
/// <summary>
|
||||
/// Provides locking similar to <see cref="ReaderWriterLockSlim"/> but
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Taken from http://johnculviner.com/achieving-named-lock-locker-functionality-in-c-4-0/ and adapted a bit. Namely:
|
||||
/// - <see cref="GetOrAdd"/> uses a new ReaderWriterLockSlim to overcome possible concurrency issues where the factory delegate could run multiple times.
|
||||
/// - Implemented <see cref="IDisposable"/>.
|
||||
/// </remarks>
|
||||
public class NamedReaderWriterLock : IDisposable {
|
||||
private readonly ConcurrentDictionary<string, ReaderWriterLockSlim> _lockDictonary = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
|
||||
|
||||
public ReaderWriterLockSlim GetLock(string name) {
|
||||
return _lockDictonary.GetOrAdd(name, new ReaderWriterLockSlim());
|
||||
}
|
||||
|
||||
public TResult RunWithReadLock<TResult>(string name, Func<TResult> body) {
|
||||
var rwLock = GetLock(name);
|
||||
try {
|
||||
rwLock.EnterReadLock();
|
||||
return body();
|
||||
}
|
||||
finally {
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWithReadLock(string name, Action body) {
|
||||
var rwLock = GetLock(name);
|
||||
try {
|
||||
rwLock.EnterReadLock();
|
||||
body();
|
||||
}
|
||||
finally {
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public TResult RunWithWriteLock<TResult>(string name, Func<TResult> body) {
|
||||
var rwLock = GetLock(name);
|
||||
try {
|
||||
rwLock.EnterWriteLock();
|
||||
return body();
|
||||
}
|
||||
finally {
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWithWriteLock(string name, Action body) {
|
||||
var rwLock = GetLock(name);
|
||||
try {
|
||||
rwLock.EnterWriteLock();
|
||||
body();
|
||||
}
|
||||
finally {
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLock(string name) {
|
||||
ReaderWriterLockSlim o;
|
||||
_lockDictonary.TryRemove(name, out o);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all the internal <see cref="ReaderWriterLockSlim"/> objects. Only call this if you're sure that no concurrent code executes
|
||||
/// any other instance method of this class!
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
foreach (var lockSlim in _lockDictonary.Values) {
|
||||
lockSlim.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user