Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mcs/class/referencesource/System.Web/Cache/cache.cs')
-rw-r--r--mcs/class/referencesource/System.Web/Cache/cache.cs1744
1 files changed, 116 insertions, 1628 deletions
diff --git a/mcs/class/referencesource/System.Web/Cache/cache.cs b/mcs/class/referencesource/System.Web/Cache/cache.cs
index 0dd2f027e68..a597396f644 100644
--- a/mcs/class/referencesource/System.Web/Cache/cache.cs
+++ b/mcs/class/referencesource/System.Web/Cache/cache.cs
@@ -14,8 +14,10 @@ namespace System.Web.Caching {
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
+ using System.Configuration.Provider;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Web.Util;
@@ -150,12 +152,6 @@ namespace System.Web.Caching {
DependencyChanged
}
- enum CacheGetOptions {
- None = 0,
- ReturnCacheEntry = 0x1,
- }
-
-
/// <devdoc>
/// <para>Implements the cache for a Web application. There is only one instance of
/// this class per application domain, and it remains valid only as long as the
@@ -165,9 +161,7 @@ namespace System.Web.Caching {
//
// Extra notes:
- // - The Cache object contains a CacheInternal object.
- // - The CacheInternal object is either a CacheSingle, or a CacheMultiple which contains mulitple
- // CacheSingle objects.
+ // - The Cache object contains a ICacheStore object and wraps it for public consumption.
//
public sealed class Cache : IEnumerable {
@@ -187,7 +181,8 @@ namespace System.Web.Caching {
/// </devdoc>
public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
- CacheInternal _cacheInternal;
+ static CacheStoreProvider _objectCache = null;
+ static CacheStoreProvider _internalCache = null;
static CacheItemRemovedCallback s_sentinelRemovedCallback = new CacheItemRemovedCallback(SentinelEntry.OnCacheItemRemovedCallback);
/// <internalonly/>
@@ -204,10 +199,6 @@ namespace System.Web.Caching {
internal Cache(int dummy) {
}
- internal void SetCacheInternal(CacheInternal cacheInternal) {
- _cacheInternal = cacheInternal;
- }
-
/// <devdoc>
/// <para>Gets the number of items stored in the cache. This value can be useful when
@@ -216,14 +207,86 @@ namespace System.Web.Caching {
/// </devdoc>
public int Count {
get {
- return _cacheInternal.PublicCount;
+ return Convert.ToInt32(ObjectCache.ItemCount);
+ }
+ }
+
+
+ internal CacheStoreProvider GetInternalCache(bool createIfDoesNotExist) {
+ if (_internalCache == null && createIfDoesNotExist) {
+ lock (this) {
+ if (_internalCache == null) {
+ NameValueCollection cacheProviderSettings = HostingEnvironment.CacheStoreProviderSettings;
+
+ if (cacheProviderSettings != null) {
+ string providerName = (string)cacheProviderSettings["name"]; // Grab this now, as InstantiateProvider will remove it from settings
+ cacheProviderSettings["isPublic"] = "false";
+ _internalCache = (CacheStoreProvider)ProvidersHelper.InstantiateProvider(cacheProviderSettings, typeof(CacheStoreProvider));
+ _internalCache.Initialize(providerName, cacheProviderSettings);
+ }
+ else {
+ if (_objectCache is AspNetCache) {
+ _internalCache = new AspNetCache((AspNetCache)_objectCache, isPublic: false);
+ }
+ else {
+ _internalCache = new AspNetCache(isPublic: false);
+ }
+ _internalCache.Initialize(null, new NameValueCollection());
+ }
+ }
+ }
+ }
+
+ return _internalCache;
+ }
+
+ [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
+ [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control this method's callers.")]
+ internal CacheStoreProvider GetObjectCache(bool createIfDoesNotExist) {
+ if (_objectCache == null && createIfDoesNotExist) {
+ lock (this) {
+ if (_objectCache == null) {
+ NameValueCollection cacheProviderSettings = HostingEnvironment.CacheStoreProviderSettings;
+
+ if (cacheProviderSettings != null) {
+ string providerName = (string)cacheProviderSettings["name"]; // Grab this now, as InstantiateProvider will remove it from settings
+ cacheProviderSettings["isPublic"] = "true";
+ _objectCache = (CacheStoreProvider)ProvidersHelper.InstantiateProvider(cacheProviderSettings, typeof(CacheStoreProvider));
+ _objectCache.Initialize(providerName, cacheProviderSettings);
+ }
+ else {
+ if (_internalCache is AspNetCache) {
+ _objectCache = new AspNetCache((AspNetCache)_internalCache, isPublic: true);
+ }
+ else {
+ _objectCache = new AspNetCache(isPublic: true);
+ }
+ _objectCache.Initialize(null, new NameValueCollection());
+ }
+ }
+ }
}
+
+ return _objectCache;
+ }
+
+ /// <devdoc>
+ /// <para>Provides access to the cache store used by ASP.Net internals.</para>
+ /// </devdoc>
+ internal CacheStoreProvider InternalCache {
+ get { return GetInternalCache(createIfDoesNotExist: true); }
}
+ /// <devdoc>
+ /// <para>Provides access to the cache store that backs HttpRuntime.Cache.</para>
+ /// </devdoc>
+ internal CacheStoreProvider ObjectCache {
+ get { return GetObjectCache(createIfDoesNotExist: true); }
+ }
/// <internalonly/>
IEnumerator IEnumerable.GetEnumerator() {
- return ((IEnumerable)_cacheInternal).GetEnumerator();
+ return ObjectCache.GetEnumerator();
}
@@ -233,7 +296,7 @@ namespace System.Web.Caching {
/// while this method is enumerating through the cache items.</para>
/// </devdoc>
public IDictionaryEnumerator GetEnumerator() {
- return _cacheInternal.GetEnumerator();
+ return ObjectCache.GetEnumerator();
}
@@ -330,11 +393,7 @@ namespace System.Web.Caching {
/// <para>Retrieves an item from the cache.</para>
/// </devdoc>
public object Get(string key) {
- return _cacheInternal.DoGet(true, key, CacheGetOptions.None);
- }
-
- internal object Get(string key, CacheGetOptions getOptions) {
- return _cacheInternal.DoGet(true, key, getOptions);
+ return ObjectCache.Get(key);
}
@@ -342,16 +401,7 @@ namespace System.Web.Caching {
/// <para>Inserts an item into the Cache with default values.</para>
/// </devdoc>
public void Insert(string key, object value) {
- _cacheInternal.DoInsert(
- true,
- key,
- value,
- null,
- NoAbsoluteExpiration,
- NoSlidingExpiration,
- CacheItemPriority.Default,
- null,
- true);
+ ObjectCache.Insert(key, value, options: null);
}
@@ -360,16 +410,7 @@ namespace System.Web.Caching {
/// dependencies.</para>
/// </devdoc>
public void Insert(string key, object value, CacheDependency dependencies) {
- _cacheInternal.DoInsert(
- true,
- key,
- value,
- dependencies,
- NoAbsoluteExpiration,
- NoSlidingExpiration,
- CacheItemPriority.Default,
- null,
- true);
+ ObjectCache.Insert(key, value, new CacheInsertOptions() { Dependencies = dependencies });
}
@@ -379,16 +420,11 @@ namespace System.Web.Caching {
/// </devdoc>
public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration) {
DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
- _cacheInternal.DoInsert(
- true,
- key,
- value,
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- CacheItemPriority.Default,
- null,
- true);
+ ObjectCache.Insert(key, value, new CacheInsertOptions() {
+ Dependencies = dependencies,
+ AbsoluteExpiration = absoluteExpiration,
+ SlidingExpiration = slidingExpiration
+ });
}
@@ -402,16 +438,13 @@ namespace System.Web.Caching {
CacheItemRemovedCallback onRemoveCallback) {
DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
- _cacheInternal.DoInsert(
- true,
- key,
- value,
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- priority,
- onRemoveCallback,
- true);
+ ObjectCache.Insert(key, value, new CacheInsertOptions() {
+ Dependencies = dependencies,
+ AbsoluteExpiration = utcAbsoluteExpiration,
+ SlidingExpiration = slidingExpiration,
+ Priority = priority,
+ OnRemovedCallback = onRemoveCallback
+ });
}
// DevDiv Bugs 162763:
@@ -432,16 +465,8 @@ namespace System.Web.Caching {
}
DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
// Insert updatable cache entry
- _cacheInternal.DoInsert (
- true,
- key,
- value,
- null,
- Cache.NoAbsoluteExpiration,
- Cache.NoSlidingExpiration,
- CacheItemPriority.NotRemovable,
- null,
- true);
+ ObjectCache.Insert(key, value, new CacheInsertOptions() { Priority = CacheItemPriority.NotRemovable });
+
// Ensure the sentinel depends on its updatable entry
string[] cacheKeys = { key };
CacheDependency expensiveObjectDep = new CacheDependency(null, cacheKeys);
@@ -453,17 +478,17 @@ namespace System.Web.Caching {
deps.Add(dependencies, expensiveObjectDep);
dependencies = deps;
}
- // Insert sentinel entry for the updatable cache entry
- _cacheInternal.DoInsert(
- false,
+ // Insert sentinel entry for the updatable cache entry
+ HttpRuntime.Cache.InternalCache.Insert(
CacheInternal.PrefixValidationSentinel + key,
new SentinelEntry(key, expensiveObjectDep, onUpdateCallback),
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- CacheItemPriority.NotRemovable,
- Cache.s_sentinelRemovedCallback,
- true);
+ new CacheInsertOptions() {
+ Dependencies = dependencies,
+ AbsoluteExpiration = utcAbsoluteExpiration,
+ SlidingExpiration = slidingExpiration,
+ Priority = CacheItemPriority.NotRemovable,
+ OnRemovedCallback = Cache.s_sentinelRemovedCallback
+ });
}
@@ -477,16 +502,13 @@ namespace System.Web.Caching {
CacheItemRemovedCallback onRemoveCallback) {
DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
- return _cacheInternal.DoInsert(
- true,
- key,
- value,
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- priority,
- onRemoveCallback,
- false);
+ return ObjectCache.Add(key, value, new CacheInsertOptions() {
+ Dependencies = dependencies,
+ AbsoluteExpiration = utcAbsoluteExpiration,
+ SlidingExpiration = slidingExpiration,
+ Priority = priority,
+ OnRemovedCallback = onRemoveCallback
+ });
}
@@ -494,1555 +516,21 @@ namespace System.Web.Caching {
/// <para>Removes the specified item from the cache. </para>
/// </devdoc>
public object Remove(string key) {
- CacheKey cacheKey = new CacheKey(key, true);
- return _cacheInternal.DoRemove(cacheKey, CacheItemRemovedReason.Removed);
+ return ObjectCache.Remove(key, CacheItemRemovedReason.Removed);
}
public long EffectivePrivateBytesLimit {
get {
- return _cacheInternal.EffectivePrivateBytesLimit;
+ return AspNetMemoryMonitor.ProcessPrivateBytesLimit;
}
}
public long EffectivePercentagePhysicalMemoryLimit {
get {
- return _cacheInternal.EffectivePercentagePhysicalMemoryLimit;
- }
- }
- }
-
- class CacheCommon {
- const int MEMORYSTATUS_INTERVAL_5_SECONDS = 5 * Msec.ONE_SECOND;
- const int MEMORYSTATUS_INTERVAL_30_SECONDS = 30 * Msec.ONE_SECOND;
-
- internal CacheInternal _cacheInternal;
- internal Cache _cachePublic;
- internal protected CacheMemoryStats _cacheMemoryStats;
- private object _timerLock = new object();
- private DisposableGCHandleRef<Timer> _timerHandleRef;
- private int _currentPollInterval = MEMORYSTATUS_INTERVAL_30_SECONDS;
- internal int _inCacheManagerThread;
- internal bool _enableMemoryCollection;
- internal bool _enableExpiration;
- internal bool _internalConfigRead;
- internal SRefMultiple _srefMultiple;
- private int _disposed = 0;
-
- internal CacheCommon() {
- _cachePublic = new Cache(0);
- _srefMultiple = new SRefMultiple();
- _cacheMemoryStats = new CacheMemoryStats(_srefMultiple);
- _enableMemoryCollection = true;
- _enableExpiration = true;
- }
-
- internal void Dispose(bool disposing) {
- if (disposing) {
- // This method must be tolerant to multiple calls to Dispose on the same instance
- if (Interlocked.Exchange(ref _disposed, 1) == 0) {
- EnableCacheMemoryTimer(false);
- _cacheMemoryStats.Dispose();
- }
- }
- }
-
- internal void AddSRefTarget(object o) {
- _srefMultiple.AddSRefTarget(o);
- }
-
- internal void SetCacheInternal(CacheInternal cacheInternal) {
- _cacheInternal = cacheInternal;
- _cachePublic.SetCacheInternal(cacheInternal);
- }
-
- internal void ReadCacheInternalConfig(CacheSection cacheSection) {
- if (_internalConfigRead) {
- return;
- }
-
- lock (this) {
- if (_internalConfigRead) {
- return;
- }
-
- // Set it to true here so that even if we have to call ReadCacheInternalConfig
- // from the code below, we won't get into an infinite loop.
- _internalConfigRead = true;
-
- if (cacheSection != null) {
- _enableMemoryCollection = (!cacheSection.DisableMemoryCollection);
- _enableExpiration = (!cacheSection.DisableExpiration);
- _cacheMemoryStats.ReadConfig(cacheSection);
- _currentPollInterval = CacheMemorySizePressure.PollInterval;
- ResetFromConfigSettings();
- }
- }
- }
-
- internal void ResetFromConfigSettings() {
- EnableCacheMemoryTimer(_enableMemoryCollection);
- _cacheInternal.EnableExpirationTimer(_enableExpiration);
- }
-
- internal void EnableCacheMemoryTimer(bool enable) {
- lock (_timerLock) {
-#if DBG
- if (Debug.IsTagPresent("Timer") && !Debug.IsTagEnabled("Timer")) {
- enable = false;
- }
-
-#endif
-
- if (enable) {
-
- if (_timerHandleRef == null) {
- // <cache privateBytesPollTime> has not been read yet
- Timer timer = new Timer(new TimerCallback(this.CacheManagerTimerCallback), null, _currentPollInterval, _currentPollInterval);
- _timerHandleRef = new DisposableGCHandleRef<Timer>(timer);
- Debug.Trace("Cache", "Started CacheMemoryTimers");
- }
- else {
- _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval);
- }
- }
- else {
- var timerHandleRef = _timerHandleRef;
- if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) {
- timerHandleRef.Dispose();
- Debug.Trace("Cache", "Stopped CacheMemoryTimers");
- }
- }
- }
-
- if (!enable) {
- // wait for CacheManagerTimerCallback to finish
- while(_inCacheManagerThread != 0) {
- Thread.Sleep(100);
- }
- }
- }
-
- void AdjustTimer() {
- lock (_timerLock) {
-
- if (_timerHandleRef == null)
- return;
-
- // the order of these if statements is important
-
- // When above the high pressure mark, interval should be 5 seconds or less
- if (_cacheMemoryStats.IsAboveHighPressure()) {
- if (_currentPollInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) {
- _currentPollInterval = MEMORYSTATUS_INTERVAL_5_SECONDS;
- _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval);
- }
- return;
- }
-
- // When above half the low pressure mark, interval should be 30 seconds or less
- if ((_cacheMemoryStats.CacheSizePressure.PressureLast > _cacheMemoryStats.CacheSizePressure.PressureLow/2)
- || (_cacheMemoryStats.TotalMemoryPressure.PressureLast > _cacheMemoryStats.TotalMemoryPressure.PressureLow/2)) {
- // DevDivBugs 104034: allow interval to fall back down when memory pressure goes away
- int newPollInterval = Math.Min(CacheMemorySizePressure.PollInterval, MEMORYSTATUS_INTERVAL_30_SECONDS);
- if (_currentPollInterval != newPollInterval) {
- _currentPollInterval = newPollInterval;
- _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval);
- }
- return;
- }
-
- // there is no pressure, interval should be the value from config
- if (_currentPollInterval != CacheMemorySizePressure.PollInterval) {
- _currentPollInterval = CacheMemorySizePressure.PollInterval;
- _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval);
- }
- }
- }
-
- void CacheManagerTimerCallback(object state) {
- CacheManagerThread(0);
- }
-
- internal long CacheManagerThread(int minPercent) {
- if (Interlocked.Exchange(ref _inCacheManagerThread, 1) != 0)
- return 0;
-#if DBG
- Debug.Trace("CacheMemory", "**BEG** CacheManagerThread " + HttpRuntime.AppDomainAppId + ", " + DateTime.Now.ToString("T", CultureInfo.InvariantCulture));
-#endif
- try {
- // Dev10 633335: if the timer has been disposed, return without doing anything
- if (_timerHandleRef == null)
- return 0;
-
- // The timer thread must always call Update so that the CacheManager
- // knows the size of the cache.
- _cacheMemoryStats.Update();
- AdjustTimer();
- int percent = Math.Max(minPercent, _cacheMemoryStats.GetPercentToTrim());
- long beginTotalCount = _cacheInternal.TotalCount;
- Stopwatch sw = Stopwatch.StartNew();
- long trimmedOrExpired = _cacheInternal.TrimIfNecessary(percent);
- sw.Stop();
- // 1) don't update stats if the trim happend because MAX_COUNT was exceeded
- // 2) don't update stats unless we removed at least one entry
- if (percent > 0 && trimmedOrExpired > 0) {
- _cacheMemoryStats.SetTrimStats(sw.Elapsed.Ticks, beginTotalCount, trimmedOrExpired);
- }
-
-#if DBG
- Debug.Trace("CacheMemory", "**END** CacheManagerThread: " + HttpRuntime.AppDomainAppId
- + ", percent=" + percent
- + ", beginTotalCount=" + beginTotalCount
- + ", trimmed=" + trimmedOrExpired
- + ", Milliseconds=" + sw.ElapsedMilliseconds);
-#endif
-
-#if PERF
- SafeNativeMethods.OutputDebugString("CacheCommon.CacheManagerThread:"
- + " minPercent= " + minPercent
- + ", percent= " + percent
- + ", beginTotalCount=" + beginTotalCount
- + ", trimmed=" + trimmedOrExpired
- + ", Milliseconds=" + sw.ElapsedMilliseconds + "\n");
-#endif
- return trimmedOrExpired;
- }
- finally {
- Interlocked.Exchange(ref _inCacheManagerThread, 0);
- }
- }
- }
-
- abstract class CacheInternal : IEnumerable, IDisposable {
- // cache key prefixes - they keep cache keys short and prevent conflicts
-
- // NOTE: Since we already used up all the lowercase letters from 'a' to 'z',
- // we are now using uppercase letters from 'A' to 'Z'
- internal const string PrefixFIRST = "A";
- internal const string PrefixResourceProvider = "A";
- internal const string PrefixMapPathVPPFile = "Bf";
- internal const string PrefixMapPathVPPDir = "Bd";
-
- // Next prefix goes here, until we get to 'Z'
-
- internal const string PrefixOutputCache = "a";
- internal const string PrefixSqlCacheDependency = "b";
- internal const string PrefixMemoryBuildResult = "c";
- internal const string PrefixPathData = "d";
- internal const string PrefixHttpCapabilities = "e";
- internal const string PrefixMapPath = "f";
- internal const string PrefixHttpSys = "g";
- internal const string PrefixFileSecurity = "h";
- internal const string PrefixInProcSessionState = "j";
- internal const string PrefixStateApplication = "k";
- internal const string PrefixPartialCachingControl = "l";
- internal const string UNUSED = "m";
- internal const string PrefixAdRotator = "n";
- internal const string PrefixWebServiceDataSource = "o";
- internal const string PrefixLoadXPath = "p";
- internal const string PrefixLoadXml = "q";
- internal const string PrefixLoadTransform = "r";
- internal const string PrefixAspCompatThreading = "s";
- internal const string PrefixDataSourceControl = "u";
- internal const string PrefixValidationSentinel = "w";
- internal const string PrefixWebEventResource = "x";
- internal const string PrefixAssemblyPath = "y";
- internal const string PrefixBrowserCapsHash = "z";
- internal const string PrefixLAST = "z";
-
- protected CacheCommon _cacheCommon;
- private int _disposed;
-
- // virtual methods requiring implementation
- internal abstract int PublicCount {get;}
-
- internal abstract long TotalCount {get;}
-
- internal abstract IDictionaryEnumerator CreateEnumerator();
-
- internal abstract CacheEntry UpdateCache(
- CacheKey cacheKey,
- CacheEntry newEntry,
- bool replace,
- CacheItemRemovedReason removedReason,
- out object valueOld);
-
- internal abstract long TrimIfNecessary(int percent);
-
- internal abstract void EnableExpirationTimer(bool enable);
-
- // If UseMemoryCache is true, we will direct all ASP.NET
- // cache usage into System.Runtime.Caching.dll. This allows
- // us to test System.Runtime.Caching.dll with all existing
- // ASP.NET test cases (functional, perf, and stress).
-#if USE_MEMORY_CACHE
- private static bool _useMemoryCache;
- private static volatile bool _useMemoryCacheInited;
- internal static bool UseMemoryCache {
- get {
- if (!_useMemoryCacheInited) {
- RegistryKey regKey = null;
- try {
- regKey = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\ASP.NET");
- if (regKey != null) {
- if ((int)regKey.GetValue("UseMemoryCache", 0)== 1) {
- _useMemoryCache = true;
- }
- }
- }
- finally {
- if (regKey != null) {
- regKey.Close();
- }
- }
- _useMemoryCacheInited = true;
- }
- return _useMemoryCache;
- }
- }
-#endif
-
- // common implementation
- static internal CacheInternal Create() {
- CacheCommon cacheCommon = new CacheCommon();
- CacheInternal cacheInternal;
-#if USE_MEMORY_CACHE
- if (UseMemoryCache) {
- cacheInternal = new MemCache(cacheCommon);
- cacheCommon.AddSRefTarget(cacheInternal);
- }
- else {
-#endif
- int numSubCaches = 0;
- uint numCPUs = (uint) SystemInfo.GetNumProcessCPUs();
- // the number of subcaches is the minimal power of 2 greater
- // than or equal to the number of cpus
- numSubCaches = 1;
- numCPUs -= 1;
- while (numCPUs > 0) {
- numSubCaches <<= 1;
- numCPUs >>= 1;
- }
- if (numSubCaches == 1) {
- cacheInternal = new CacheSingle(cacheCommon, null, 0);
- }
- else {
- cacheInternal = new CacheMultiple(cacheCommon, numSubCaches);
- }
-#if USE_MEMORY_CACHE
- }
-#endif
- cacheCommon.SetCacheInternal(cacheInternal);
- cacheCommon.ResetFromConfigSettings();
-
- return cacheInternal;
- }
-
- protected CacheInternal(CacheCommon cacheCommon) {
- _cacheCommon = cacheCommon;
- }
-
- protected virtual void Dispose(bool disposing) {
- _cacheCommon.Dispose(disposing);
- }
-
- public void Dispose() {
- _disposed = 1;
- Dispose(true);
- // no destructor, don't need it.
- // System.GC.SuppressFinalize(this);
- }
-
- internal bool IsDisposed { get { return _disposed == 1; } }
-
- internal void ReadCacheInternalConfig(CacheSection cacheSection) {
- _cacheCommon.ReadCacheInternalConfig(cacheSection);
- }
-
- internal long TrimCache(int percent) {
- return _cacheCommon.CacheManagerThread(percent);
- }
-
- internal Cache CachePublic {
- get {return _cacheCommon._cachePublic;}
- }
-
- internal long EffectivePrivateBytesLimit {
- get { return _cacheCommon._cacheMemoryStats.CacheSizePressure.MemoryLimit; }
- }
-
- internal long EffectivePercentagePhysicalMemoryLimit {
- get { return _cacheCommon._cacheMemoryStats.TotalMemoryPressure.MemoryLimit; }
- }
-
- IEnumerator IEnumerable.GetEnumerator() {
- return CreateEnumerator();
- }
-
- public IDictionaryEnumerator GetEnumerator() {
- return CreateEnumerator();
- }
-
- internal object this[string key] {
- get {
- return Get(key);
+ return AspNetMemoryMonitor.PhysicalMemoryPercentageLimit;
}
}
-
- internal object Get(string key) {
- return DoGet(false, key, CacheGetOptions.None);
- }
-
- internal object Get(string key, CacheGetOptions getOptions) {
- return DoGet(false, key, getOptions);
- }
-
- internal object DoGet(bool isPublic, string key, CacheGetOptions getOptions) {
- CacheEntry entry;
- CacheKey cacheKey;
- object dummy;
-
- cacheKey = new CacheKey(key, isPublic);
- entry = UpdateCache(cacheKey, null, false, CacheItemRemovedReason.Removed, out dummy);
- if (entry != null) {
- if ((getOptions & CacheGetOptions.ReturnCacheEntry) != 0) {
- return entry;
- }
- else {
- return entry.Value;
- }
- }
- else {
- return null;
- }
- }
-
- internal void UtcInsert(string key, object value) {
- DoInsert(false,
- key,
- value,
- null,
- Cache.NoAbsoluteExpiration,
- Cache.NoSlidingExpiration,
- CacheItemPriority.Default,
- null,
- true);
-
- }
-
- internal void UtcInsert(string key, object value, CacheDependency dependencies) {
- DoInsert(false,
- key,
- value,
- dependencies,
- Cache.NoAbsoluteExpiration,
- Cache.NoSlidingExpiration,
- CacheItemPriority.Default,
- null,
- true);
- }
-
- internal void UtcInsert(
- string key,
- object value,
- CacheDependency dependencies,
- DateTime utcAbsoluteExpiration,
- TimeSpan slidingExpiration) {
-
- DoInsert(false,
- key,
- value,
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- CacheItemPriority.Default,
- null,
- true);
- }
-
- internal void UtcInsert(
- string key,
- object value,
- CacheDependency dependencies,
- DateTime utcAbsoluteExpiration,
- TimeSpan slidingExpiration,
- CacheItemPriority priority,
- CacheItemRemovedCallback onRemoveCallback) {
-
- DoInsert(false,
- key,
- value,
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- priority,
- onRemoveCallback,
- true);
- }
-
- internal object UtcAdd(
- string key,
- object value,
- CacheDependency dependencies,
- DateTime utcAbsoluteExpiration,
- TimeSpan slidingExpiration,
- CacheItemPriority priority,
- CacheItemRemovedCallback onRemoveCallback) {
-
- return DoInsert(
- false,
- key,
- value,
- dependencies,
- utcAbsoluteExpiration,
- slidingExpiration,
- priority,
- onRemoveCallback,
- false);
-
- }
-
- internal object DoInsert(
- bool isPublic,
- string key,
- object value,
- CacheDependency dependencies,
- DateTime utcAbsoluteExpiration,
- TimeSpan slidingExpiration,
- CacheItemPriority priority,
- CacheItemRemovedCallback onRemoveCallback,
- bool replace) {
-
-
- /*
- * If we throw an exception, prevent a leak by a user who
- * writes the following:
- *
- * Cache.Insert(key, value, new CacheDependency(file));
- */
- using (dependencies) {
- CacheEntry entry;
- object dummy;
-
- entry = new CacheEntry(
- key,
- value,
- dependencies,
- onRemoveCallback,
- utcAbsoluteExpiration,
- slidingExpiration,
- priority,
- isPublic);
-
- entry = UpdateCache(entry, entry, replace, CacheItemRemovedReason.Removed, out dummy);
-
- /*
- * N.B. A set can fail if two or more threads set the same key
- * at the same time.
- */
-#if DBG
- if (replace) {
- string yesno = (entry != null) ? "succeeded" : "failed";
- Debug.Trace("CacheAPIInsert", "Cache.Insert " + yesno + ": " + key);
- }
- else {
- if (entry == null) {
- Debug.Trace("CacheAPIAdd", "Cache.Add added new item: " + key);
- }
- else {
- Debug.Trace("CacheAPIAdd", "Cache.Add returned existing item: " + key);
- }
- }
-#endif
-
- if (entry != null) {
- return entry.Value;
- }
- else {
- return null;
- }
- }
- }
-
- internal object Remove(string key) {
- CacheKey cacheKey = new CacheKey(key, false);
- return DoRemove(cacheKey, CacheItemRemovedReason.Removed);
- }
-
- internal object Remove(CacheKey cacheKey, CacheItemRemovedReason reason) {
- return DoRemove(cacheKey, reason);
- }
-
- /*
- * Remove an item from the cache, with a specific reason.
- * This is package access so only the cache can specify
- * a reason other than REMOVED.
- *
- * @param key The key for the item.
- * @exception ArgumentException
- */
- internal object DoRemove(CacheKey cacheKey, CacheItemRemovedReason reason) {
- object valueOld;
-
- UpdateCache(cacheKey, null, true, reason, out valueOld);
-
-#if DBG
- if (valueOld != null) {
- Debug.Trace("CacheAPIRemove", "Cache.Remove succeeded, reason=" + reason + ": " + cacheKey);
- }
- else {
- Debug.Trace("CacheAPIRemove", "Cache.Remove failed, reason=" + reason + ": " + cacheKey);
- }
-#endif
-
- return valueOld;
- }
}
- sealed class CacheKeyComparer : IEqualityComparer {
- static CacheKeyComparer s_comparerInstance;
-
- static internal CacheKeyComparer GetInstance() {
- if (s_comparerInstance == null) {
- s_comparerInstance = new CacheKeyComparer();
- }
-
- return s_comparerInstance;
- }
-
- private CacheKeyComparer()
- {
- }
-
- bool IEqualityComparer.Equals(Object x, Object y)
- {
- return Compare(x, y) == 0;
- }
-
- // Compares two objects. An implementation of this method must return a
- // value less than zero if x is less than y, zero if x is equal to y, or a
- // value greater than zero if x is greater than y.
- private int Compare(Object x, Object y) {
- CacheKey a, b;
-
- Debug.Assert(x != null && x is CacheKey);
- Debug.Assert(y != null && y is CacheKey);
-
- a = (CacheKey) x;
- b = (CacheKey) y;
-
- if (a.IsPublic) {
- if (b.IsPublic) {
- return String.Compare(a.Key, b.Key, StringComparison.Ordinal);
- }
- else {
- return 1;
- }
- }
- else {
- if (!b.IsPublic) {
- return String.Compare(a.Key, b.Key, StringComparison.Ordinal);
- }
- else {
- return -1;
- }
- }
- }
- // Returns a hash code for the given object.
- //
- int IEqualityComparer.GetHashCode(Object obj) {
- Debug.Assert(obj != null && obj is CacheKey);
-
- CacheKey cacheKey = (CacheKey) obj;
-
- return cacheKey.GetHashCode();
- }
- }
-
- /*
- * The cache.
- */
- sealed class CacheSingle : CacheInternal {
- // cache stats
- static readonly TimeSpan INSERT_BLOCK_WAIT = new TimeSpan(0, 0, 10);
- const int MAX_COUNT = Int32.MaxValue / 2;
- const int MIN_COUNT = 10;
-
-
- Hashtable _entries; /* lookup table of entries */
- CacheExpires _expires; /* expires tables */
- CacheUsage _usage; /* usage tables */
- object _lock; /* read/write synchronization for _entries */
- int _disposed; /* disposed */
- int _totalCount; /* count of total entries */
- int _publicCount; /* count of public entries */
- ManualResetEvent _insertBlock; /* event to block inserts during high mem usage */
- bool _useInsertBlock; /* use insert block? */
- int _insertBlockCalls; /* number of callers using insert block */
- int _iSubCache; /* index of this cache */
- CacheMultiple _cacheMultiple; /* the CacheMultiple containing this cache */
-
- /*
- * Constructs a new Cache.
- */
- internal CacheSingle(CacheCommon cacheCommon, CacheMultiple cacheMultiple, int iSubCache) : base(cacheCommon) {
- _cacheMultiple = cacheMultiple;
- _iSubCache = iSubCache;
- _entries = new Hashtable(CacheKeyComparer.GetInstance());
- _expires = new CacheExpires(this);
- _usage = new CacheUsage(this);
- _lock = new object();
- _insertBlock = new ManualResetEvent(true);
- cacheCommon.AddSRefTarget(new { _entries, _expires, _usage });
- }
-
- /*
- * Dispose the cache.
- */
- protected override void Dispose(bool disposing) {
- if (disposing) {
- if (Interlocked.Exchange(ref _disposed, 1) == 0) {
- if (_expires != null) {
- _expires.EnableExpirationTimer(false);
- }
-
- // close all items
- CacheEntry[] entries = null;
-
- lock (_lock) {
- entries = new CacheEntry[_entries.Count];
- int i = 0;
- foreach (DictionaryEntry d in _entries) {
- entries[i++] = (CacheEntry) d.Value;
- }
- }
-
- foreach (CacheEntry entry in entries) {
- Remove(entry, CacheItemRemovedReason.Removed);
- }
-
- // force any waiters to complete their waits. Note
- // that the insert block cannot be reacquired, as UseInsertBlock
- // checks the _disposed field.
- _insertBlock.Set();
-
- // release the block, causing it to be disposed when there
- // are no more callers.
- ReleaseInsertBlock();
-
- Debug.Trace("CacheDispose", "Cache disposed");
- }
- }
-
- base.Dispose(disposing);
- }
-
- // Get the insert block manual reset event if it has not been disposed.
- ManualResetEvent UseInsertBlock() {
- for (;;) {
- if (_disposed == 1)
- return null;
-
- int n = _insertBlockCalls;
- if (n < 0) {
- return null;
- }
-
- if (Interlocked.CompareExchange(ref _insertBlockCalls, n + 1, n) == n) {
- return _insertBlock;
- }
- }
- }
-
- // Release the insert block event, and dispose it if it has been released
- // more times than it has been used
- void ReleaseInsertBlock() {
- if (Interlocked.Decrement(ref _insertBlockCalls) < 0) {
- ManualResetEvent e = _insertBlock;
- _insertBlock = null;
-
- // now close
- e.Close();
- }
- }
-
- // Set the insert block event.
- void SetInsertBlock() {
- ManualResetEvent e = null;
- try {
- e = UseInsertBlock();
- if (e != null) {
- e.Set();
- }
- }
- finally {
- if (e != null) {
- ReleaseInsertBlock();
- }
- }
- }
-
- // Reset the insert block event.
- void ResetInsertBlock() {
- ManualResetEvent e = null;
- try {
- e = UseInsertBlock();
- if (e != null) {
- e.Reset();
- }
- }
- finally {
- if (e != null) {
- ReleaseInsertBlock();
- }
- }
- }
-
- // Wait on the insert block event.
- bool WaitInsertBlock() {
- bool signaled = false;
- ManualResetEvent e = null;
- try {
- e = UseInsertBlock();
- if (e != null) {
- Debug.Trace("CacheMemoryTrimInsertBlock", "WaitInsertBlock: Cache " + _iSubCache + ": _useInsertBlock=true");
- signaled = e.WaitOne(INSERT_BLOCK_WAIT, false);
- Debug.Trace("CacheMemoryTrimInsertBlock", "Done waiting");
- }
- }
- finally {
- if (e != null) {
- ReleaseInsertBlock();
- }
- }
-
- return signaled;
- }
-
- internal void BlockInsertIfNeeded() {
- if (_cacheCommon._cacheMemoryStats.IsAboveHighPressure()) {
- Debug.Trace("CacheMemoryTrimInsertBlock", "BlockInsertIfNeeded: Cache " + _iSubCache + ": _useInsertBlock=true");
- _useInsertBlock = true;
- ResetInsertBlock();
- }
- }
-
- internal void UnblockInsert() {
- if (_useInsertBlock) {
- _useInsertBlock = false;
- SetInsertBlock();
- Debug.Trace("CacheMemoryTrimInsertBlock", "UnblockInsert: Cache " + _iSubCache + ": _useInsertBlock=false");
- }
- }
-
-
- internal override int PublicCount {
- get {return _publicCount;}
- }
-
- internal override long TotalCount {
- get {return _totalCount;}
- }
-
- internal override IDictionaryEnumerator CreateEnumerator() {
- Hashtable h = new Hashtable(_publicCount);
- DateTime utcNow = DateTime.UtcNow;
-
- lock (_lock) {
- foreach (DictionaryEntry d in _entries) {
- CacheEntry entry = (CacheEntry) d.Value;
-
- // note that ASP.NET does not use this enumerator internally,
- // so we just choose public items.
- if (entry.IsPublic &&
- entry.State == CacheEntry.EntryState.AddedToCache &&
- ((!_cacheCommon._enableExpiration) || (utcNow <= entry.UtcExpires))) {
- h[entry.Key] = entry.Value;
- }
- }
- }
-
- return h.GetEnumerator();
- }
-
- /*
- * Performs all operations on the cache, with the
- * exception of Clear. The arguments indicate the type of operation:
- *
- * @param key The key of the object.
- * @param newItem The new entry to be added to the cache.
- * @param replace Whether or not newEntry should replace an existing object in the cache.
- * @return The item requested. May be null.
- */
- internal override CacheEntry UpdateCache(
- CacheKey cacheKey,
- CacheEntry newEntry,
- bool replace,
- CacheItemRemovedReason removedReason,
- out object valueOld)
- {
- CacheEntry entry = null;
- CacheEntry oldEntry = null;
- bool expired = false;
- DateTime utcNow;
- CacheDependency newEntryDependency = null;
- bool isGet, isAdd;
- bool removeExpired = false;
- bool updateExpires = false;
- DateTime utcNewExpires = DateTime.MinValue;
- CacheEntry.EntryState entryState = CacheEntry.EntryState.NotInCache;
- bool newEntryNeedsClose = false;
- CacheItemRemovedReason newEntryRemovedReason = CacheItemRemovedReason.Removed;
-
- valueOld = null;
- isGet = !replace && newEntry == null;
- isAdd = !replace && newEntry != null;
-
- /*
- * Perform update of cache data structures in a series to
- * avoid overlapping locks.
- *
- * First, update the hashtable. The hashtable is the place
- * that guarantees what is in or out of the cache.
- *
- * Loop here to remove expired items in a Get or Add, where
- * we can't otherwise delete an item.
- */
- for (;;) {
- if (removeExpired) {
- Debug.Trace("CacheUpdate", "Removing expired item found in Get: " + cacheKey);
- UpdateCache(cacheKey, null, true, CacheItemRemovedReason.Expired, out valueOld);
- removeExpired = false;
- }
-
- entry = null;
- utcNow = DateTime.UtcNow;
-
- if (_useInsertBlock && newEntry != null && newEntry.HasUsage() /* HasUsage() means it's not NonRemovable */) {
- bool insertBlockReleased = WaitInsertBlock();
-
-#if DBG
- if (!insertBlockReleased) {
- Debug.Trace("CacheUpdateWaitFailed", "WaitInsertBlock failed.");
- }
-#endif
- }
-
- // the _entries hashtable supports multiple readers or one writer
- bool isLockEntered = false;
- if (!isGet) {
- Monitor.Enter(_lock, ref isLockEntered);
- }
- try {
- entry = (CacheEntry) _entries[cacheKey];
- Debug.Trace("CacheUpdate", "Entry " + ((entry != null) ? "found" : "not found") + "in hashtable: " + cacheKey);
-
- if (entry != null) {
- entryState = entry.State;
-
- // If isGet == true, we are not hold any lock and so entryState can be anything
- Debug.Assert(
- isGet ||
- entryState == CacheEntry.EntryState.AddingToCache ||
- entryState == CacheEntry.EntryState.AddedToCache,
- "entryState == CacheEntry.EntryState.AddingToCache || entryState == CacheEntry.EntryState.AddedToCache");
-
- expired = (_cacheCommon._enableExpiration) && (entry.UtcExpires < utcNow);
- if (expired) {
- if (isGet) {
- /*
- * If the expired item is Added to the cache, remove it now before
- * its expiration timer fires up to a minute in the future.
- * Otherwise, just return null to indicate the item is not available.
- */
- if (entryState == CacheEntry.EntryState.AddedToCache) {
- removeExpired = true;
- continue;
- }
-
- entry = null;
- }
- else {
- /*
- * If it's a call to Add, replace the item
- * when it has expired.
- */
- replace = true;
-
- /*
- * Change the removed reason.
- */
- removedReason = CacheItemRemovedReason.Expired;
- }
- }
- else {
- updateExpires = (_cacheCommon._enableExpiration) && (entry.SlidingExpiration > TimeSpan.Zero);
- }
- }
-
- /*
- * Avoid running unnecessary code in a Get request by this simple test:
- */
- if (!isGet) {
- /*
- * Remove an item from the hashtable.
- */
- if (replace && entry != null) {
- bool doRemove = (entryState != CacheEntry.EntryState.AddingToCache);
- if (doRemove) {
- oldEntry = entry;
-
- oldEntry.State = CacheEntry.EntryState.RemovingFromCache;
-
- _entries.Remove(oldEntry);
- Debug.Trace("CacheUpdate", "Entry removed from hashtable: " + cacheKey);
- }
- else {
- /*
- * If we're removing and couldn't remove the old item
- * because its state was AddingToCache, return null
- * to indicate failure.
- */
- if (newEntry == null) {
- Debug.Trace("CacheUpdate", "Removal from hashtable failed: " + cacheKey);
- entry = null;
- }
- }
- }
-
- /*
- * Add an item to the hashtable.
- */
- if (newEntry != null) {
- bool doAdd = true;
-
- if (entry != null) {
- if (oldEntry == null) {
- /*
- * We could not remove the existing entry,
- * either because it simply exists and replace == false,
- * or replace == true and it's state was AddingToCache when
- * we tried to remove it.
- */
- doAdd = false;
- newEntryRemovedReason = CacheItemRemovedReason.Removed;
- }
-
-#if DBG
- if (!doAdd) {
- Debug.Trace("CacheUpdate", "Insertion into hashtable failed because old entry was not removed: " + cacheKey);
- }
-#endif
- }
-
-
- if (doAdd) {
- /* non-definitive check */
- newEntryDependency = newEntry.Dependency;
- if (newEntryDependency != null) {
- if (newEntryDependency.HasChanged) {
- doAdd = false;
- newEntryRemovedReason = CacheItemRemovedReason.DependencyChanged;
- }
-
-#if DBG
- if (!doAdd) {
- Debug.Trace("CacheUpdate", "Insertion into hashtable failed because dependency changed: " + cacheKey);
- }
-#endif
- }
- }
-
- if (doAdd) {
- newEntry.State = CacheEntry.EntryState.AddingToCache;
- _entries.Add(newEntry, newEntry);
-
- /*
- * If this is an Add operation, indicate success
- * by returning null.
- */
- if (isAdd) {
- Debug.Assert(entry == null || expired, "entry == null || expired");
- entry = null;
- }
- else {
- /*
- * Indicate success by returning the inserted entry.
- */
- entry = newEntry;
- }
-
- Debug.Trace("CacheUpdate", "Entry added to hashtable: " + cacheKey);
- }
- else {
- if (!isAdd) {
- /*
- * If we failed for an Insert, indicate failure by returning null.
- */
- entry = null;
- newEntryNeedsClose = true;
- }
- else {
- /*
- * If we failed for an Add (e.g. Dependency has changed),
- * return the existing value. If existing value is null,
- * we have to close the newEntry ourselves. Otherwise, we'll
- * return non-null and the caller should close the item.
- */
- newEntryNeedsClose = (entry == null);
- }
-
- /*
- * If newEntry cannot be inserted, and it does not need to be
- * closed, set it to null so that we don't insert it later.
- * Leave it non-null when it needs to be closed that that we
- * can close it.
- */
- if (!newEntryNeedsClose) {
- newEntry = null;
- }
-
- }
- }
- }
-
- break;
- }
- finally {
- if (isLockEntered) {
- Monitor.Exit(_lock);
- }
- }
- }
-
- /*
- * Since we want Get to be fast, check here for a get without
- * alteration to cache.
- */
- if (isGet) {
- if (entry != null) {
- if (updateExpires) {
- utcNewExpires = utcNow + entry.SlidingExpiration;
- if (utcNewExpires - entry.UtcExpires >= CacheExpires.MIN_UPDATE_DELTA || utcNewExpires < entry.UtcExpires) {
- _expires.UtcUpdate(entry, utcNewExpires);
- }
- }
-
- UtcUpdateUsageRecursive(entry, utcNow);
- }
-
- if (cacheKey.IsPublic) {
- PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_RATIO_BASE);
- if (entry != null) {
- PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_HITS);
- }
- else {
- PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_MISSES);
- }
- }
-
- PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_RATIO_BASE);
- if (entry != null) {
- PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_HITS);
- }
- else {
- PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_MISSES);
- }
-
-#if DBG
- if (entry != null) {
- Debug.Trace("CacheUpdate", "Cache hit: " + cacheKey);
- }
- else {
- Debug.Trace("CacheUpdate", "Cache miss: " + cacheKey);
- }
-#endif
-
- }
- else {
- int totalDelta = 0;
- int publicDelta = 0;
- int totalTurnover = 0;
- int publicTurnover = 0;
-
- if (oldEntry != null) {
- if (oldEntry.InExpires()) {
- _expires.Remove(oldEntry);
- }
-
- if (oldEntry.InUsage()) {
- _usage.Remove(oldEntry);
- }
-
- Debug.Assert(oldEntry.State == CacheEntry.EntryState.RemovingFromCache, "oldEntry.State == CacheEntry.EntryState.RemovingFromCache");
- oldEntry.State = CacheEntry.EntryState.RemovedFromCache;
- valueOld = oldEntry.Value;
-
- totalDelta--;
- totalTurnover++;
- if (oldEntry.IsPublic) {
- publicDelta--;
- publicTurnover++;
- }
-
-#if DBG
- Debug.Trace("CacheUpdate", "Entry removed from cache, reason=" + removedReason + ": " + (CacheKey) oldEntry);
-#endif
- }
-
- if (newEntry != null) {
- if (newEntryNeedsClose) {
- // Call close if newEntry could not be added.
- newEntry.State = CacheEntry.EntryState.RemovedFromCache;
- newEntry.Close(newEntryRemovedReason);
- newEntry = null;
- }
- else {
- Debug.Assert(!newEntry.InExpires());
- Debug.Assert(!newEntry.InUsage());
-
- if (_cacheCommon._enableExpiration && newEntry.HasExpiration()) {
- _expires.Add(newEntry);
- }
-
- if ( _cacheCommon._enableMemoryCollection && newEntry.HasUsage() &&
- ( // Don't bother to set usage if it's going to expire very soon
- !newEntry.HasExpiration() ||
- newEntry.SlidingExpiration > TimeSpan.Zero ||
- newEntry.UtcExpires - utcNow >= CacheUsage.MIN_LIFETIME_FOR_USAGE)) {
-
- _usage.Add(newEntry);
- }
-
- newEntry.State = CacheEntry.EntryState.AddedToCache;
-
- Debug.Trace("CacheUpdate", "Entry added to cache: " + (CacheKey)newEntry);
-
- totalDelta++;
- totalTurnover++;
- if (newEntry.IsPublic) {
- publicDelta++;
- publicTurnover++;
- }
- }
- }
-
- // Call close after the newEntry has been fully added to the cache,
- // so the OnRemoveCallback can take a dependency on the newly inserted item.
- if (oldEntry != null) {
- oldEntry.Close(removedReason);
- }
-
- // Delay monitoring change events until the oldEntry has been completely removed
- // from the cache, and its OnRemoveCallback called. This way we won't call the
- // OnRemoveCallback for newEntry before doing so for oldEntry.
- if (newEntry != null) {
- // listen to change events
- newEntry.MonitorDependencyChanges();
-
- /*
- * NB: We have to check for dependency changes after we add the item
- * to cache, because otherwise we may not remove it if it changes
- * between the time we check for a dependency change and the time
- * we set the AddedToCache bit. The worst that will happen is that
- * a get can occur on an item that has changed, but that can happen
- * anyway. The important thing is that we always remove an item that
- * has changed.
- */
- if (newEntryDependency != null && newEntryDependency.HasChanged) {
- Remove(newEntry, CacheItemRemovedReason.DependencyChanged);
- }
- }
-
- // update counts and counters
- if (totalDelta == 1) {
- Interlocked.Increment(ref _totalCount);
- PerfCounters.IncrementCounter(AppPerfCounter.TOTAL_CACHE_ENTRIES);
- }
- else if (totalDelta == -1) {
- Interlocked.Decrement(ref _totalCount);
- PerfCounters.DecrementCounter(AppPerfCounter.TOTAL_CACHE_ENTRIES);
- }
-
- if (publicDelta == 1) {
- Interlocked.Increment(ref _publicCount);
- PerfCounters.IncrementCounter(AppPerfCounter.API_CACHE_ENTRIES);
- }
- else if (publicDelta == -1) {
- Interlocked.Decrement(ref _publicCount);
- PerfCounters.DecrementCounter(AppPerfCounter.API_CACHE_ENTRIES);
- }
-
- if (totalTurnover > 0) {
- PerfCounters.IncrementCounterEx(AppPerfCounter.TOTAL_CACHE_TURNOVER_RATE, totalTurnover);
- }
-
- if (publicTurnover > 0) {
- PerfCounters.IncrementCounterEx(AppPerfCounter.API_CACHE_TURNOVER_RATE, publicTurnover);
- }
- }
-
- return entry;
- }
-
- void UtcUpdateUsageRecursive(CacheEntry entry, DateTime utcNow) {
- CacheDependency dependency;
- CacheEntry[] entries;
-
- // Don't update if the last update is less than 1 sec away. This way we'll
- // avoid over updating the usage in the scenario where a cache makes several
- // update requests.
- if (utcNow - entry.UtcLastUsageUpdate > CacheUsage.CORRELATED_REQUEST_TIMEOUT || utcNow < entry.UtcLastUsageUpdate) {
- entry.UtcLastUsageUpdate = utcNow;
- if (entry.InUsage()) {
- CacheSingle cacheSingle;
- if (_cacheMultiple == null) {
- cacheSingle = this;
- }
- else {
- cacheSingle = _cacheMultiple.GetCacheSingle(entry.Key.GetHashCode());
- }
-
- cacheSingle._usage.Update(entry);
- }
-
- dependency = entry.Dependency;
- if (dependency != null) {
- entries = dependency.CacheEntries;
- if (entries != null) {
- foreach (CacheEntry dependent in entries) {
- UtcUpdateUsageRecursive(dependent, utcNow);
- }
- }
- }
- }
- }
-
- internal override long TrimIfNecessary(int percent) {
- Debug.Assert(_cacheCommon._inCacheManagerThread == 1, "Trim should only occur when we're updating memory statistics.");
- if (!_cacheCommon._enableMemoryCollection)
- return 0;
-
- int toTrim = 0;
- // do we need to drop a percentage of entries?
- if (percent > 0) {
- toTrim = (int)(((long)_totalCount * (long)percent) / 100L);
- }
- // would this leave us above MAX_COUNT?
- int minTrim = _totalCount - MAX_COUNT;
- if (toTrim < minTrim) {
- toTrim = minTrim;
- }
- // would this put us below MIN_COUNT?
- int maxTrim = _totalCount - MIN_COUNT;
- if (toTrim > maxTrim) {
- toTrim = maxTrim;
- }
- // do we need to trim?
- if (toTrim <= 0 || HostingEnvironment.ShutdownInitiated) {
- return 0;
- }
-
- int ocEntriesTrimmed = 0; // number of output cache entries trimmed
- int publicEntriesTrimmed = 0; // number of public entries trimmed
- int totalTrimmed = 0; // total number of entries trimmed
- int trimmedOrExpired = 0;
- int beginTotalCount = _totalCount;
-
- try {
- trimmedOrExpired = _expires.FlushExpiredItems(true);
- if (trimmedOrExpired < toTrim) {
- totalTrimmed = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired, ref publicEntriesTrimmed, ref ocEntriesTrimmed);
- trimmedOrExpired += totalTrimmed;
- }
-
- if (totalTrimmed > 0) {
- // Update values for perfcounters
- PerfCounters.IncrementCounterEx(AppPerfCounter.CACHE_TOTAL_TRIMS, totalTrimmed);
- PerfCounters.IncrementCounterEx(AppPerfCounter.CACHE_API_TRIMS, publicEntriesTrimmed);
- PerfCounters.IncrementCounterEx(AppPerfCounter.CACHE_OUTPUT_TRIMS, ocEntriesTrimmed);
- }
- }
- catch {
- }
-
-#if DBG
- Debug.Trace("CacheMemory", "TrimIfNecessary: _iSubCache= " + _iSubCache
- + ", beginTotalCount=" + beginTotalCount
- + ", endTotalCount=" + _totalCount
- + ", percent=" + percent
- + ", trimmed=" + totalTrimmed);
-#endif
- return trimmedOrExpired;
- }
-
- internal override void EnableExpirationTimer(bool enable) {
- if (_expires != null) {
- _expires.EnableExpirationTimer(enable);
- }
- }
- }
-
- class CacheMultiple : CacheInternal {
- int _disposed;
- DisposableGCHandleRef<CacheSingle>[] _cachesRefs;
- int _cacheIndexMask;
-
- internal CacheMultiple(CacheCommon cacheCommon, int numSingleCaches) : base(cacheCommon) {
- Debug.Assert(numSingleCaches > 1, "numSingleCaches is not greater than 1");
- Debug.Assert((numSingleCaches & (numSingleCaches - 1)) == 0, "numSingleCaches is not a power of 2");
- _cacheIndexMask = numSingleCaches - 1;
-
- // Each CacheSingle will have its own SRef reporting the size of the data it references.
- // Objects in this CacheSingle may have refs to the root Cache and therefore reference other instances of CacheSingle.
- // This leads to an unbalanced tree of SRefs and makes GC less efficient while calculating multiple SRefs on multiple cores.
- // Using DisposableGCHandleRef here prevents SRefs from calculating data that does not belong to other CacheSingle instances.
- _cachesRefs = new DisposableGCHandleRef<CacheSingle>[numSingleCaches];
- for (int i = 0; i < numSingleCaches; i++) {
- _cachesRefs[i] = new DisposableGCHandleRef<CacheSingle>(new CacheSingle(cacheCommon, this, i));
- }
- }
-
- protected override void Dispose(bool disposing) {
- if (disposing) {
- if (Interlocked.Exchange(ref _disposed, 1) == 0) {
- foreach (var cacheSingleRef in _cachesRefs) {
- // Unfortunately the application shutdown logic allows user to access cache even after its disposal.
- // We'll keep the GCHandle inside cacheSingleRef until it gets reclaimed during appdomain shutdown.
- // And we'll only dispose the Target to preserve the old behavior.
- cacheSingleRef.Target.Dispose();
- }
- }
- }
-
- base.Dispose(disposing);
- }
-
- internal override int PublicCount {
- get {
- int count = 0;
- foreach (var cacheSingleRef in _cachesRefs) {
- count += cacheSingleRef.Target.PublicCount;
- }
-
- return count;
- }
- }
-
- internal override long TotalCount {
- get {
- long count = 0;
- foreach (var cacheSingleRef in _cachesRefs) {
- count += cacheSingleRef.Target.TotalCount;
- }
-
- return count;
- }
- }
-
- internal override IDictionaryEnumerator CreateEnumerator() {
- IDictionaryEnumerator[] enumerators = new IDictionaryEnumerator[_cachesRefs.Length];
- for (int i = 0, c = _cachesRefs.Length; i < c; i++) {
- enumerators[i] = _cachesRefs[i].Target.CreateEnumerator();
- }
-
- return new AggregateEnumerator(enumerators);
- }
-
- internal CacheSingle GetCacheSingle(int hashCode) {
- Debug.Assert(_cachesRefs != null && _cachesRefs.Length != 0);
- // Dev10 865907: Math.Abs throws OverflowException for Int32.MinValue
- if (hashCode < 0) {
- hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode;
- }
- int index = (hashCode & _cacheIndexMask);
- Debug.Assert(_cachesRefs[index].Target != null);
- return _cachesRefs[index].Target;
- }
-
- internal override CacheEntry UpdateCache(
- CacheKey cacheKey,
- CacheEntry newEntry,
- bool replace,
- CacheItemRemovedReason removedReason,
- out object valueOld) {
-
- int hashCode = cacheKey.Key.GetHashCode();
- CacheSingle cacheSingle = GetCacheSingle(hashCode);
- return cacheSingle.UpdateCache(cacheKey, newEntry, replace, removedReason, out valueOld);
- }
-
- internal override long TrimIfNecessary(int percent) {
- long count = 0;
- foreach (var cacheSingleRef in _cachesRefs) {
- count += cacheSingleRef.Target.TrimIfNecessary(percent);
- }
- return count;
- }
-
- internal override void EnableExpirationTimer(bool enable) {
- foreach (var cacheSingleRef in _cachesRefs) {
- cacheSingleRef.Target.EnableExpirationTimer(enable);
- }
- }
- }
-
- class AggregateEnumerator : IDictionaryEnumerator {
- IDictionaryEnumerator [] _enumerators;
- int _iCurrent;
-
- internal AggregateEnumerator(IDictionaryEnumerator [] enumerators) {
- _enumerators = enumerators;
- }
-
- public bool MoveNext() {
- bool more;
-
- for (;;) {
- more = _enumerators[_iCurrent].MoveNext();
- if (more)
- break;
-
- if (_iCurrent == _enumerators.Length - 1)
- break;
-
- _iCurrent++;
- }
-
- return more;
- }
-
- public void Reset() {
- for (int i = 0; i <= _iCurrent; i++) {
- _enumerators[i].Reset();
- }
-
- _iCurrent = 0;
- }
-
- public Object Current {
- get {
- return _enumerators[_iCurrent].Current;
- }
- }
-
- public Object Key {
- get {
- return _enumerators[_iCurrent].Key;
- }
- }
-
- public Object Value {
- get {
- return _enumerators[_iCurrent].Value;
- }
- }
-
- public DictionaryEntry Entry {
- get {
- return _enumerators[_iCurrent].Entry;
- }
- }
- }
}