Fixes: #U4-1732

This commit is contained in:
Shannon Deminick
2013-02-19 00:53:20 +06:00
parent 29724b352b
commit b45f6e0393
2 changed files with 70 additions and 70 deletions

View File

@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using Umbraco.Core;
using Umbraco.Core.ObjectResolution;
namespace Umbraco.Web.Routing
{
@@ -28,34 +29,44 @@ namespace Umbraco.Web.Routing
if (bindToEvents)
{
// content - whenever the entire XML cache is rebuilt (from disk cache or from database)
// we must clear the cache entirely
global::umbraco.content.AfterRefreshContent += (sender, e) => Clear();
// document - whenever a document is updated in, or removed from, the XML cache
// we must clear the cache - at the moment, we clear the entire cache
// TODO could we do partial updates instead of clearing the whole cache?
global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear();
global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear();
// domains - whenever a domain change we must clear the cache
// because routes contain the id of root nodes of domains
// TODO could we do partial updates instead of clearing the whole cache?
global::umbraco.cms.businesslogic.web.Domain.AfterDelete += (sender, e) => Clear();
global::umbraco.cms.businesslogic.web.Domain.AfterSave += (sender, e) => Clear();
global::umbraco.cms.businesslogic.web.Domain.New += (sender, e) => Clear();
// FIXME
// the content class needs to be refactored - at the moment
// content.XmlContentInternal setter does not trigger any event
// content.UpdateDocumentCache(List<Document> Documents) does not trigger any event
// content.RefreshContentFromDatabaseAsync triggers AfterRefresh _while_ refreshing
// etc...
// in addition some events do not make sense... we trigger Publish when moving
// a node, which we should not (the node is moved, not published...) etc.
Resolution.Frozen += ResolutionFrozen;
}
}
/// <summary>
/// Once resolution is frozen, then we can bind to the events that we require
/// </summary>
/// <param name="s"></param>
/// <param name="args"></param>
void ResolutionFrozen(object s, EventArgs args)
{
// content - whenever the entire XML cache is rebuilt (from disk cache or from database)
// we must clear the cache entirely
global::umbraco.content.AfterRefreshContent += (sender, e) => Clear();
// document - whenever a document is updated in, or removed from, the XML cache
// we must clear the cache - at the moment, we clear the entire cache
// TODO could we do partial updates instead of clearing the whole cache?
global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear();
global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear();
// domains - whenever a domain change we must clear the cache
// because routes contain the id of root nodes of domains
// TODO could we do partial updates instead of clearing the whole cache?
global::umbraco.cms.businesslogic.web.Domain.AfterDelete += (sender, e) => Clear();
global::umbraco.cms.businesslogic.web.Domain.AfterSave += (sender, e) => Clear();
global::umbraco.cms.businesslogic.web.Domain.New += (sender, e) => Clear();
// FIXME
// the content class needs to be refactored - at the moment
// content.XmlContentInternal setter does not trigger any event
// content.UpdateDocumentCache(List<Document> Documents) does not trigger any event
// content.RefreshContentFromDatabaseAsync triggers AfterRefresh _while_ refreshing
// etc...
// in addition some events do not make sense... we trigger Publish when moving
// a node, which we should not (the node is moved, not published...) etc.
}
/// <summary>
/// Used ONLY for unit tests
/// </summary>

View File

@@ -7,6 +7,7 @@ using System.Threading;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using umbraco.BusinessLogic;
using umbraco.BusinessLogic.Actions;
@@ -15,7 +16,6 @@ using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.cache;
using umbraco.cms.businesslogic.web;
using umbraco.DataLayer;
using umbraco.IO;
using umbraco.presentation.nodeFactory;
using Action = umbraco.BusinessLogic.Actions.Action;
using Node = umbraco.NodeFactory.Node;
@@ -30,17 +30,17 @@ namespace umbraco
#region Declarations
// Sync access to disk file
private static readonly object _readerWriterSyncLock = new object();
private static readonly object ReaderWriterSyncLock = new object();
// Sync access to internal cache
private static readonly object _xmlContentInternalSyncLock = new object();
private static readonly object XmlContentInternalSyncLock = new object();
// Sync access to timestamps
private static readonly object _timestampSyncLock = new object();
private static readonly object TimestampSyncLock = new object();
// Sync database access
private static readonly object _dbReadSyncLock = new object();
private readonly string XmlContextContentItemKey = "UmbracoXmlContextContent";
private static readonly object DbReadSyncLock = new object();
private const string XmlContextContentItemKey = "UmbracoXmlContextContent";
private string _umbracoXmlDiskCacheFileName = string.Empty;
private volatile XmlDocument _xmlContent;
private DateTime _lastDiskCacheReadTime = DateTime.MinValue;
@@ -65,46 +65,29 @@ namespace umbraco
#endregion
#region Constructors
static content()
{
//Trace.Write("Initializing content");
//ThreadPool.QueueUserWorkItem(
// delegate
// {
// XmlDocument xmlDoc = Instance.XmlContentInternal;
// Trace.WriteLine("Content initialized");
// });
Trace.WriteLine("Checking for xml content initialisation...");
Instance.CheckXmlContentPopulation();
}
public content()
{
;
}
#endregion
#region Singleton
private static readonly Lazy<content> LazyInstance = new Lazy<content>(() => new content());
public static content Instance
{
get { return Singleton<content>.Instance; }
get
{
return LazyInstance.Value;
}
}
#endregion
#region Properties
/// <summary>
/// <remarks>
/// Get content. First call to this property will initialize xmldoc
/// subsequent calls will be blocked until initialization is done
/// Further we cache(in context) xmlContent for each request to ensure that
/// we always have the same XmlDoc throughout the whole request.
/// </summary>
/// </remarks>
public virtual XmlDocument XmlContent
{
get
@@ -127,6 +110,9 @@ namespace umbraco
get { return Instance.XmlContent; }
}
//NOTE: We CANNOT use this for a double check lock because it is a property, not a field and to do double
// check locking in c# you MUST have a volatile field. Even thoug this wraps a volatile field it will still
// not work as expected for a double check lock because properties are treated differently in the clr.
public virtual bool isInitializing
{
get { return _xmlContent == null; }
@@ -135,6 +121,9 @@ namespace umbraco
/// <summary>
/// Internal reference to XmlContent
/// </summary>
/// <remarks>
/// Before returning we always check to ensure that the xml is loaded
/// </remarks>
protected virtual XmlDocument XmlContentInternal
{
get
@@ -145,7 +134,7 @@ namespace umbraco
}
set
{
lock (_xmlContentInternalSyncLock)
lock (XmlContentInternalSyncLock)
{
// Clear macro cache
Cache.ClearCacheObjectTypes("umbraco.MacroCacheContent");
@@ -179,14 +168,14 @@ namespace umbraco
if (UmbracoSettings.isXmlContentCacheDisabled)
return;
lock (_timestampSyncLock)
lock (TimestampSyncLock)
{
if (_lastDiskCacheCheckTime > DateTime.UtcNow.AddSeconds(-1.0))
return;
_lastDiskCacheCheckTime = DateTime.UtcNow;
lock (_xmlContentInternalSyncLock)
lock (XmlContentInternalSyncLock)
{
if (GetCacheFileUpdateTime() <= _lastDiskCacheReadTime)
@@ -200,17 +189,17 @@ namespace umbraco
/// <summary>
/// Triggers the XML content population if necessary.
/// </summary>
/// <returns></returns>
/// <returns>Returns true of the XML was not populated, returns false if it was already populated</returns>
private bool CheckXmlContentPopulation()
{
if (UmbracoSettings.XmlContentCheckForDiskChanges)
CheckDiskCacheForUpdate();
if (isInitializing)
if (_xmlContent == null)
{
lock (_xmlContentInternalSyncLock)
lock (XmlContentInternalSyncLock)
{
if (isInitializing)
if (_xmlContent == null)
{
LogHelper.Debug<content>("Initializing content on thread '{0}' (Threadpool? {1})",
true,
@@ -504,7 +493,7 @@ namespace umbraco
{
// lock the xml cache so no other thread can write to it at the same time
// note that some threads could read from it while we hold the lock, though
lock (_xmlContentInternalSyncLock)
lock (XmlContentInternalSyncLock)
{
// modify a clone of the cache because even though we're into the write-lock
// we may have threads reading at the same time. why is this an option?
@@ -551,7 +540,7 @@ namespace umbraco
// making changes at the same time, they need to be queued
int parentid = Documents[0].Id;
lock (_xmlContentInternalSyncLock)
lock (XmlContentInternalSyncLock)
{
// Make copy of memory content, we cannot make changes to the same document
// the is read from elsewhere
@@ -621,7 +610,7 @@ namespace umbraco
// We need to lock content cache here, because we cannot allow other threads
// making changes at the same time, they need to be queued
lock (_xmlContentInternalSyncLock)
lock (XmlContentInternalSyncLock)
{
// Make copy of memory content, we cannot make changes to the same document
// the is read from elsewhere
@@ -961,7 +950,7 @@ namespace umbraco
/// </summary>
private void DeleteXmlCache()
{
lock (_readerWriterSyncLock)
lock (ReaderWriterSyncLock)
{
if (File.Exists(UmbracoXmlDiskCacheFileName))
{
@@ -1040,7 +1029,7 @@ namespace umbraco
/// </summary>
private XmlDocument LoadContentFromDiskCache()
{
lock (_readerWriterSyncLock)
lock (ReaderWriterSyncLock)
{
var xmlDoc = new XmlDocument();
LogHelper.Info<content>("Loading content from disk cache...");
@@ -1106,7 +1095,7 @@ inner join cmsDocument on cmsDocument.nodeId = umbracoNode.id
where cmsDocument.published = 1
order by umbracoNode.level, umbracoNode.sortOrder";
lock (_dbReadSyncLock)
lock (DbReadSyncLock)
{
using (
IRecordsReader dr = SqlHelper.ExecuteReader(sql,
@@ -1256,7 +1245,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
/// <param name="xmlDoc"></param>
internal void PersistXmlToFile(XmlDocument xmlDoc)
{
lock (_readerWriterSyncLock)
lock (ReaderWriterSyncLock)
{
if (xmlDoc != null)
{