1 // <copyright file="MemCache.cs" company="Microsoft">
2 //   Copyright (c) 2009 Microsoft Corporation.  All rights reserved.
3 // </copyright>
4 #if USE_MEMORY_CACHE
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.Specialized;
9 using System.Globalization;
10 using System.Security;
11 using System.Security.Permissions;
12 using System.Reflection;
13 using System.Runtime.Caching;
14 using System.Text;
15 using System.Web.Configuration;
16 using System.Web.Util;
17 
18 namespace System.Web.Caching {
19     internal sealed class MemCache: CacheInternal {
20         private volatile bool _inited;
21         private static object _initLock = new object();
22         private MemoryCache _cacheInternal;
23         private MemoryCache _cachePublic;
24         private volatile bool _disposed;
25 
MemCache(CacheCommon cacheCommon)26         internal MemCache(CacheCommon cacheCommon) : base(cacheCommon) {
27             // config initialization is done by Init.
28             Assembly asm = Assembly.Load("System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL");
29             Type t = asm.GetType("System.Runtime.Caching.MemoryCache", true, false);
30             _cacheInternal = HttpRuntime.CreateNonPublicInstance(t, new object[] {"asp_icache", null, true}) as MemoryCache;
31             _cachePublic = HttpRuntime.CreateNonPublicInstance(t, new object[] {"asp_pcache", null, true}) as MemoryCache;
32         }
33 
Dispose(bool disposing)34         protected override void Dispose(bool disposing) {
35             try {
36                 _disposed = true;
37                 if (disposing) {
38                     if (_cacheInternal != null) {
39                         _cacheInternal.Dispose();
40                     }
41                     if (_cachePublic != null) {
42                         _cachePublic.Dispose();
43                     }
44                 }
45             }
46             finally {
47                 base.Dispose(disposing);
48             }
49         }
50 
51         internal override int PublicCount {
52             get {
53                 return (_cachePublic != null) ? (int)_cachePublic.GetCount() : 0;
54             }
55         }
56 
57         internal override long TotalCount {
58             get {
59                 long internalCount = (_cacheInternal != null) ? _cacheInternal.GetCount(null) : 0;
60                 return internalCount + PublicCount;
61             }
62         }
63 
Init(CacheSection cacheSection)64         internal void Init(CacheSection cacheSection) {
65             if (_inited) {
66                 return;
67             }
68 
69             lock (_initLock) {
70                 if (_inited) {
71                     return;
72                 }
73 
74                 NameValueCollection config = null;
75                 if (cacheSection != null) {
76                     //_enableMemoryCollection = (!cacheSection.DisableMemoryCollection);
77                     //_enableExpiration = (!cacheSection.DisableExpiration);
78                     int physicalMemoryLimitPercentage = cacheSection.PercentagePhysicalMemoryUsedLimit;
79                     long cacheMemoryLimitMegabytes = cacheSection.PrivateBytesLimit << 20;
80                     TimeSpan pollingInterval = cacheSection.PrivateBytesPollTime;
81                     config = new NameValueCollection(3);
82                     config["physicalMemoryLimitPercentage"] = physicalMemoryLimitPercentage.ToString(CultureInfo.InvariantCulture);
83                     config["cacheMemoryLimitMegabytes"] = cacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture);
84                     config["pollingInterval"] = pollingInterval.ToString();
85                 }
86 
87                 Type t = _cacheInternal.GetType();
88 
89                 t.InvokeMember("UpdateConfig",
90                                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
91                                null, // binder
92                                _cacheInternal, // target
93                                new object[] {config}, // args
94                                CultureInfo.InvariantCulture);
95                 t.InvokeMember("UpdateConfig",
96                                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
97                                null, // binder
98                                _cachePublic, // target
99                                new object[] {config}, // args
100                                CultureInfo.InvariantCulture);
101 
102                 _inited = true;
103             }
104         }
105 
106         // return enumerator for public entries
CreateEnumerator()107         internal override IDictionaryEnumerator CreateEnumerator() {
108             return (IDictionaryEnumerator)((IEnumerable)_cachePublic).GetEnumerator();
109         }
110 
CreateCacheEntryChangeMonitor(IEnumerable<String> keys, bool isPublic)111         internal CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<String> keys, bool isPublic) {
112             return (isPublic) ? _cachePublic.CreateCacheEntryChangeMonitor(keys, null) : _cacheInternal.CreateCacheEntryChangeMonitor(keys, null);
113         }
114 
GetPolicy(CacheEntry newEntry)115         private CacheItemPolicy GetPolicy(CacheEntry newEntry) {
116             CacheItemPolicy policy = new CacheItemPolicy();
117             policy.SlidingExpiration = newEntry.SlidingExpiration;
118             if (policy.SlidingExpiration == ObjectCache.NoSlidingExpiration) {
119                 policy.AbsoluteExpiration = (newEntry.UtcExpires != Cache.NoAbsoluteExpiration) ? newEntry.UtcExpires : ObjectCache.InfiniteAbsoluteExpiration;
120             }
121             if (newEntry.Dependency != null) {
122                 policy.ChangeMonitors.Add(new DependencyChangeMonitor(newEntry.Dependency));
123             }
124             policy.Priority = (newEntry.UsageBucket == 0xff) ? System.Runtime.Caching.CacheItemPriority.NotRemovable : System.Runtime.Caching.CacheItemPriority.Default;
125             CacheItemRemovedCallback callback = newEntry.CacheItemRemovedCallback;
126             if (callback != null) {
127                 policy.RemovedCallback = (new RemovedCallback(callback)).CacheEntryRemovedCallback;
128             }
129             return policy;
130         }
131 
UpdateCache(CacheKey cacheKey, CacheEntry newEntry, bool replace, CacheItemRemovedReason removedReason, out object valueOld)132         internal override CacheEntry UpdateCache(CacheKey cacheKey,
133                                                  CacheEntry newEntry,
134                                                  bool replace,
135                                                  CacheItemRemovedReason removedReason,
136                                                  out object valueOld) {
137             valueOld = null;
138             CacheEntry entry = null;
139             string key = cacheKey.Key;
140             bool isPublic = cacheKey.IsPublic;
141             if (_disposed) {
142                 return null;
143             }
144 
145             MemoryCache cache = (isPublic) ? _cachePublic : _cacheInternal;
146             if (newEntry == null && !replace) {
147                 // get
148                 object o = cache.Get(key);
149                 if (o != null) {
150                     entry = new CacheEntry(key, o, null, null,
151                                            Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
152                                            CacheItemPriority.Default, isPublic);
153                     entry.State = CacheEntry.EntryState.AddedToCache;
154                 }
155             }
156             else if (newEntry != null && replace) {
157                 // set
158                 try {
159                 }
160                 finally {
161                     // prevent ThreadAbortEx from interrupting these calls
162                     CacheItemPolicy policy = GetPolicy(newEntry);
163                     cache.Set(key, newEntry.Value, policy);
164                 }
165             }
166             else if (newEntry != null && !replace) {
167                 // add
168                 try {
169                 }
170                 finally {
171                     // prevent ThreadAbortEx from interrupting these calls
172                     CacheItemPolicy policy = GetPolicy(newEntry);
173                     Object o = cache.AddOrGetExisting(key, newEntry.Value, policy);
174                     if (o != null) {
175                         entry = new CacheEntry(key, o, null, null,
176                                                Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
177                                                CacheItemPriority.Default, isPublic);
178                         entry.State = CacheEntry.EntryState.AddedToCache;
179                     }
180                 }
181             }
182             else {
183                 // remove
184                 valueOld = cache.Remove(key);
185             }
186             return entry;
187         }
188 
TrimIfNecessary(int percent)189         internal override long TrimIfNecessary(int percent) {
190             return _cachePublic.Trim(percent) + _cacheInternal.Trim(percent);
191         }
192 
EnableExpirationTimer(bool enable)193         internal override void EnableExpirationTimer(bool enable) {
194             // This is done by Dispose, so it's a no-op here
195         }
196     }
197 }
198 #endif
199