ApplicationContext - manage MainDom

Conflicts:
	src/Umbraco.Core/ApplicationContext.cs
	src/Umbraco.Core/Umbraco.Core.csproj
This commit is contained in:
Stephan
2015-07-03 15:32:37 +02:00
parent a8be796d68
commit c01f864e37
3 changed files with 196 additions and 14 deletions

View File

@@ -1,17 +1,13 @@
using System;
using System.Configuration;
using System.Threading;
using System.Web;
using System.Web.Caching;
using Umbraco.Core.Cache;
using System.Threading.Tasks;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
namespace Umbraco.Core
{
/// <summary>
@@ -65,13 +61,13 @@ namespace Umbraco.Core
/// </remarks>
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
{
if (ApplicationContext.Current != null)
if (Current != null)
{
if (!replaceContext)
return ApplicationContext.Current;
return Current;
}
ApplicationContext.Current = appContext;
return ApplicationContext.Current;
Current = appContext;
return Current;
}
/// <summary>
@@ -90,14 +86,14 @@ namespace Umbraco.Core
/// </remarks>
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
{
if (ApplicationContext.Current != null)
if (Current != null)
{
if (!replaceContext)
return ApplicationContext.Current;
return Current;
}
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
ApplicationContext.Current = ctx;
return ApplicationContext.Current;
Current = ctx;
return Current;
}
/// <summary>
@@ -196,9 +192,12 @@ namespace Umbraco.Core
internal string _umbracoApplicationUrl; // internal for tests
private Lazy<bool> _configured;
internal MainDom MainDom { get; private set; }
private void Init()
{
MainDom = new MainDom();
MainDom.Acquire();
//Create the lazy value to resolve whether or not the application is 'configured'
_configured = new Lazy<bool>(() =>
{

182
src/Umbraco.Core/MainDom.cs Normal file
View File

@@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core
{
// represents the main domain
class MainDom : IRegisteredObject
{
#region Vars
// our own lock for local consistency
private readonly object _locko = new object();
// async lock representing the main domain lock
private readonly AsyncLock _asyncLock;
private IDisposable _asyncLocker;
// event wait handle used to notify current main domain that it should
// release the lock because a new domain wants to be the main domain
private readonly EventWaitHandle _signal;
// indicates whether...
private volatile bool _isMainDom; // we are the main domain
private volatile bool _signaled; // we have been signaled
// actions to run before releasing the main domain
private readonly SortedList<int, Action> _callbacks = new SortedList<int, Action>();
private const int LockTimeoutMilliseconds = 4 * 60 * 1000; // 4'
#endregion
#region Ctor
// initializes a new instance of MainDom
public MainDom()
{
var appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty);
var lockName = "UMBRACO-" + appId + "-MAINDOM-LCK";
_asyncLock = new AsyncLock(lockName);
var eventName = "UMBRACO-" + appId + "-MAINDOM-EVT";
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
}
#endregion
// register a main domain consumer
public bool Register(Action release, int weight = 100)
{
return Register(null, release, weight);
}
// register a main domain consumer
public bool Register(Action install, Action release, int weight = 100)
{
lock (_locko)
{
if (_signaled) return false;
if (install != null)
install();
if (release != null)
_callbacks.Add(weight, release);
return true;
}
}
// handles the signal requesting that the main domain is released
private void OnSignal(string source)
{
// once signaled, we stop waiting, but then there is the hosting environment
// so we have to make sure that we only enter that method once
lock (_locko)
{
LogHelper.Debug<MainDom>("Signaled" + (_signaled ? " (again)" : "") + " (" + source + ").");
if (_signaled) return;
if (_isMainDom == false) return; // probably not needed
_signaled = true;
}
try
{
LogHelper.Debug<MainDom>("Stopping...");
foreach (var callback in _callbacks.Values)
{
try
{
callback(); // no timeout on callbacks
}
catch (Exception e)
{
LogHelper.Error<MainDom>("Error while running callback, remaining callbacks will not run.", e);
throw;
}
}
LogHelper.Debug<MainDom>("Stopped.");
}
finally
{
// in any case...
_isMainDom = false;
_asyncLocker.Dispose();
LogHelper.Debug<MainDom>("Released MainDom.");
}
}
// acquires the main domain
public bool Acquire()
{
lock (_locko) // we don't want the hosting environment to interfere by signaling
{
// if signaled, too late to acquire, give up
// the handler is not installed so that would be the hosting environment
if (_signaled)
{
LogHelper.Debug<MainDom>("Cannot acquire MainDom (signaled).");
return false;
}
LogHelper.Debug<MainDom>("Acquiring MainDom...");
// signal other instances that we want the lock, then wait one the lock,
// which may timeout, and this is accepted - see comments below
// signal, then wait for the lock, then make sure the event is
// resetted (maybe there was noone listening..)
_signal.Set();
// if more than 1 instance reach that point, one will get the lock
// and the other one will timeout, which is accepted
_asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds);
_isMainDom = true;
// we need to reset the event, because otherwise we would end up
// signaling ourselves and commiting suicide immediately.
// only 1 instance can reach that point, but other instances may
// have started and be trying to get the lock - they will timeout,
// which is accepted
_signal.Reset();
_signal.WaitOneAsync()
.ContinueWith(_ => OnSignal("signal"));
HostingEnvironment.RegisterObject(this);
LogHelper.Debug<MainDom>("Acquired MainDom.");
return true;
}
}
// gets a value indicating whether we are the main domain
public bool IsMainDom
{
get { return _isMainDom; }
}
// IRegisteredObject
public void Stop(bool immediate)
{
try
{
OnSignal("environment"); // will run once
}
finally
{
HostingEnvironment.UnregisterObject(this);
}
}
}
}

View File

@@ -317,6 +317,7 @@
<Compile Include="HttpContextExtensions.cs" />
<Compile Include="IApplicationEventHandler.cs" />
<Compile Include="IDisposeOnRequestEnd.cs" />
<Compile Include="MainDom.cs" />
<Compile Include="Manifest\GridEditorConverter.cs" />
<Compile Include="Media\Exif\BitConverterEx.cs" />
<Compile Include="Media\Exif\ExifBitConverter.cs" />