diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs index e88a51021a..6fdcc28895 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Cache { internal abstract class DictionaryCacheProviderBase : ICacheProvider { - protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + protected readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); protected abstract DictionaryCacheWrapper DictionaryCache { get; } /// @@ -42,8 +42,9 @@ namespace Umbraco.Core.Cache { using (new WriteLock(Locker)) { - if (DictionaryCache[GetCacheKey(key)] == null) return; - DictionaryCache.Remove(GetCacheKey(key)); ; + var cacheKey = GetCacheKey(key); + if (DictionaryCache[cacheKey] == null) return; + DictionaryCache.Remove(cacheKey); } } @@ -123,15 +124,21 @@ namespace Umbraco.Core.Cache /// The start of the key public virtual void ClearCacheByKeySearch(string keyStartsWith) { - var keysToRemove = DictionaryCache.Cast() - .Select(item => new DictionaryItemWrapper(item)) - .Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) - .Select(c => c.Key) - .ToList(); - - foreach (var k in keysToRemove) + using (new WriteLock(Locker)) { - DictionaryCache.Remove(k); + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where( + c => + c.Key is string && + ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } } } @@ -141,54 +148,65 @@ namespace Umbraco.Core.Cache /// public virtual void ClearCacheByKeyExpression(string regexString) { - var keysToRemove = new List(); - foreach (var item in DictionaryCache) + using (new WriteLock(Locker)) { - var c = new DictionaryItemWrapper(item); - var s = c.Key as string; - if (s != null) + var keysToRemove = new List(); + foreach (var item in DictionaryCache) { - var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); - if (Regex.IsMatch(withoutPrefix, regexString)) + var c = new DictionaryItemWrapper(item); + var s = c.Key as string; + if (s != null) { - keysToRemove.Add(c.Key); + var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); + if (Regex.IsMatch(withoutPrefix, regexString)) + { + keysToRemove.Add(c.Key); + } } } - } - foreach (var k in keysToRemove) - { - DictionaryCache.Remove(k); + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } } } public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - return (from object item in DictionaryCache + using (new ReadLock(Locker)) + { + return (from object item in DictionaryCache select new DictionaryItemWrapper(item) into c - where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)) + where + c.Key is string && + ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)) select c.Value).ToList(); + } } - public IEnumerable GetCacheItemsByKeyExpression(string regexString) + public virtual IEnumerable GetCacheItemsByKeyExpression(string regexString) { - var found = new List(); - foreach (var item in DictionaryCache) + using (new ReadLock(Locker)) { - var c = new DictionaryItemWrapper(item); - var s = c.Key as string; - if (s != null) + var found = new List(); + foreach (var item in DictionaryCache) { - var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); - if (Regex.IsMatch(withoutPrefix, regexString)) + var c = new DictionaryItemWrapper(item); + var s = c.Key as string; + if (s != null) { - found.Add(c.Value); + var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); + if (Regex.IsMatch(withoutPrefix, regexString)) + { + found.Add(c.Value); + } } } - } - return found; + return found; + } } /// @@ -198,8 +216,11 @@ namespace Umbraco.Core.Cache /// public virtual object GetCacheItem(string cacheKey) { - var result = DictionaryCache.Get(GetCacheKey(cacheKey)); - return result; + using (new ReadLock(Locker)) + { + var result = DictionaryCache.Get(GetCacheKey(cacheKey)); + return result; + } } public abstract object GetCacheItem(string cacheKey, Func getCacheItem); diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index 46ecbf7c2b..ba733a7c0e 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -1,6 +1,8 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Web; using System.Web.Caching; @@ -28,34 +30,130 @@ namespace Umbraco.Core.Cache get { return _wrapper; } } - /// - /// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate - /// + private IEnumerable> EnumerateDictionaryCache() + { + // DictionaryCache just wraps _cache which has a special enumerator + var enumerator = _cache.GetEnumerator(); + while (enumerator.MoveNext()) + { + var key = enumerator.Key as string; + if (key == null) continue; + yield return new KeyValuePair(key, enumerator.Value); + } + } + + public override void ClearAllCache() + { + using (new WriteLock(Locker)) + { + foreach (var kvp in EnumerateDictionaryCache() + .Where(x => x.Key.StartsWith(CacheItemPrefix) && x.Value != null)) + _cache.Remove(kvp.Key); + } + } + + public override void ClearCacheItem(string key) + { + using (new WriteLock(Locker)) + { + var cacheKey = GetCacheKey(key); + _cache.Remove(cacheKey); + } + } + + public override void ClearCacheObjectTypes(string typeName) + { + var typeName2 = typeName; + using (new WriteLock(Locker)) + { + foreach (var kvp in EnumerateDictionaryCache() + .Where(x => x.Key.StartsWith(CacheItemPrefix) + && x.Value != null + && x.Value.GetType().ToString().InvariantEquals(typeName2))) + _cache.Remove(kvp.Key); + } + } + + public override void ClearCacheObjectTypes() + { + // should we use "is" or compare types? + + //var typeOfT = typeof(T); + using (new WriteLock(Locker)) + { + foreach (var kvp in EnumerateDictionaryCache() + .Where(x => x.Key.StartsWith(CacheItemPrefix) + //&& x.Value != null + //&& x.Value.GetType() == typeOfT)) + && x.Value is T)) + _cache.Remove(kvp.Key); + } + } + public override void ClearCacheObjectTypes(Func predicate) { + // see note above + // should we use "is" or compare types? + try { - lock (Locker) + using (new WriteLock(Locker)) { - foreach (DictionaryEntry c in _cache) - { - var key = c.Key.ToString(); - if (_cache[key] != null - && _cache[key] is T - && predicate(key, (T)_cache[key])) - { - _cache.Remove(c.Key.ToString()); - } - } + foreach (var kvp in EnumerateDictionaryCache() + .Where(x => x.Key.StartsWith(CacheItemPrefix) + && x.Value is T + && predicate(x.Key, (T) x.Value))) + _cache.Remove(kvp.Key); } } catch (Exception e) { + // oops, what is this?! LogHelper.Error("Cache clearing error", e); } } - /// + public override void ClearCacheByKeySearch(string keyStartsWith) + { + using (new WriteLock(Locker)) + { + foreach (var kvp in EnumerateDictionaryCache() + .Where(x => x.Key.InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)))) + _cache.Remove(kvp.Key); + } + } + + public override void ClearCacheByKeyExpression(string regexString) + { + var plen = CacheItemPrefix.Length + 1; // string.Format("{0}-", CacheItemPrefix) + using (new WriteLock(Locker)) + { + foreach (var kvp in EnumerateDictionaryCache() + .Where(x => x.Key.StartsWith(CacheItemPrefix) + && Regex.IsMatch(x.Key.Substring(plen), regexString))) + _cache.Remove(kvp.Key); + } + } + + public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + { + return EnumerateDictionaryCache() + .Where(x => x.Key.InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) + .Select(x => ((Lazy)x.Value).Value) + .ToList(); + } + + public override IEnumerable GetCacheItemsByKeyExpression(string regexString) + { + var plen = CacheItemPrefix.Length + 1; // string.Format("{0}-", CacheItemPrefix) + return EnumerateDictionaryCache() + .Where(x => x.Key.StartsWith(CacheItemPrefix) + && Regex.IsMatch(x.Key.Substring(plen), regexString)) + .Select(x => ((Lazy)x.Value).Value) + .ToList(); + } + + /// /// Gets (and adds if necessary) an item from the cache with all of the default parameters /// /// @@ -139,7 +237,8 @@ namespace Umbraco.Core.Cache // and make sure we don't store a null value. Though I'm not sure it is a good idea. var result = new Lazy(getCacheItem); - //if (result.Value == null) return; + var value = result.Value; // force evaluation now + //if (value == null) return; cacheKey = GetCacheKey(cacheKey); diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index 89a39c86d9..f2e6be47f5 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache /// internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider { - private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); internal ObjectCache MemoryCache; public ObjectCacheRuntimeCacheProvider() @@ -26,7 +26,7 @@ namespace Umbraco.Core.Cache public virtual void ClearAllCache() { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { MemoryCache.DisposeIfDisposable(); MemoryCache = new MemoryCache("in-memory"); @@ -35,7 +35,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheItem(string key) { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { if (MemoryCache[key] == null) return; MemoryCache.Remove(key); @@ -44,7 +44,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes(string typeName) { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { var keysToRemove = MemoryCache .Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName)) @@ -57,7 +57,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes() { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { var typeOfT = typeof (T); var keysToRemove = MemoryCache @@ -71,7 +71,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes(Func predicate) { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { var typeOfT = typeof(T); var keysToRemove = MemoryCache @@ -85,7 +85,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheByKeySearch(string keyStartsWith) { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList(); foreach (var k in keysToRemove) @@ -97,7 +97,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheByKeyExpression(string regexString) { - using (new WriteLock(Locker)) + using (new WriteLock(_locker)) { var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList(); foreach (var k in keysToRemove) @@ -109,22 +109,33 @@ namespace Umbraco.Core.Cache public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - return (from c in MemoryCache - where c.Key.InvariantStartsWith(keyStartsWith) - select c.Value).ToList(); + using (new ReadLock(_locker)) + { + return MemoryCache + .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) + .Select(x => ((Lazy) x.Value).Value) + .ToList(); + } } public IEnumerable GetCacheItemsByKeyExpression(string regexString) { - return (from c in MemoryCache - where Regex.IsMatch(c.Key, regexString) - select c.Value).ToList(); + using (new ReadLock(_locker)) + { + return MemoryCache + .Where(x => Regex.IsMatch(x.Key, regexString)) + .Select(x => ((Lazy) x.Value).Value) + .ToList(); + } } public virtual object GetCacheItem(string cacheKey) { - var result = MemoryCache.Get(cacheKey); - return result; + using (new ReadLock(_locker)) + { + var result = MemoryCache.Get(cacheKey); + return result; + } } public virtual object GetCacheItem(string cacheKey, Func getCacheItem) @@ -145,7 +156,7 @@ namespace Umbraco.Core.Cache Lazy result; - using (var lck = new UpgradeableReadLock(Locker)) + using (var lck = new UpgradeableReadLock(_locker)) { result = MemoryCache.Get(cacheKey) as Lazy; if (result == null /* || (result.IsValueCreated && result.Value == null) */) @@ -166,7 +177,8 @@ namespace Umbraco.Core.Cache // see notes in HttpRuntimeCacheProvider var result = new Lazy(getCacheItem); - //if (result.Value == null) return; + var value = result.Value; // force evaluation now + //if (value == null) return; var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); MemoryCache.Set(cacheKey, result, policy);