Cache.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. //
  2. // System.Web.Caching
  3. //
  4. // Author:
  5. // Patrik Torstensson
  6. // Daniel Cazzulino [DHC] ([email protected])
  7. //
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Collections;
  30. using System.Threading;
  31. namespace System.Web.Caching {
  32. public sealed class Cache : IEnumerable {
  33. public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
  34. public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
  35. // Helper objects
  36. private CacheExpires _objExpires;
  37. // The data storage
  38. private Hashtable _arrEntries;
  39. private ReaderWriterLock _lockEntries;
  40. private int _nItems;
  41. static private TimeSpan _datetimeOneYear = TimeSpan.FromDays (365);
  42. public Cache () {
  43. _nItems = 0;
  44. _lockEntries = new ReaderWriterLock ();
  45. _arrEntries = new Hashtable ();
  46. _objExpires = new CacheExpires (this);
  47. }
  48. /// <summary>
  49. /// Internal method to create a enumerator and over all public
  50. /// items in the cache and is used by GetEnumerator method.
  51. /// </summary>
  52. /// <returns>
  53. /// Returns IDictionaryEnumerator that contains all public items in the cache
  54. /// </returns>
  55. private IDictionaryEnumerator CreateEnumerator () {
  56. Hashtable objTable;
  57. //Locking with -1 provides a non-expiring lock.
  58. _lockEntries.AcquireReaderLock (-1);
  59. try {
  60. // Create a new hashtable to return as collection of public items
  61. objTable = new Hashtable (_arrEntries.Count);
  62. foreach (DictionaryEntry objEntry in _arrEntries) {
  63. if (objEntry.Key == null)
  64. continue;
  65. CacheEntry entry = (CacheEntry) objEntry.Value;
  66. if (entry.IsPublic)
  67. objTable.Add (objEntry.Key, entry.Item);
  68. }
  69. } finally {
  70. _lockEntries.ReleaseReaderLock ();
  71. }
  72. return objTable.GetEnumerator ();
  73. }
  74. IEnumerator IEnumerable.GetEnumerator () {
  75. return GetEnumerator ();
  76. }
  77. public IDictionaryEnumerator GetEnumerator () {
  78. return CreateEnumerator ();
  79. }
  80. internal void Touch(string strKey) {
  81. GetEntry (strKey);
  82. }
  83. /// <summary>
  84. /// Adds the specified item to the Cache object with
  85. /// dependencies, expiration and priority policies, and a
  86. /// delegate you can use to notify your application when the
  87. /// inserted item is removed from the Cache.
  88. /// </summary>
  89. /// <param name="strKey">The cache key used to reference the item.</param>
  90. /// <param name="objItem">The item to be added to the cache.</param>
  91. /// <param name="objDependency">
  92. /// The file or cache key dependencies for the item. When any
  93. /// dependency changes, the object becomes invalid and is removed
  94. /// from the cache. If there are no dependencies, this paramter
  95. /// contains a null reference.
  96. /// </param>
  97. /// <param name="absolutExpiration">
  98. /// The time at which the added object expires and is removed from the cache.
  99. /// </param>
  100. /// <param name="slidingExpiration">
  101. /// The interval between the time the added object was last
  102. /// accessed and when that object expires. If this value is the
  103. /// equivalent of 20 minutes, the object expires and is removed
  104. /// from the cache 20 minutes after it is last accessed.
  105. /// </param>
  106. /// <param name="enumPriority">
  107. /// The relative cost of the object, as expressed by the
  108. /// CacheItemPriority enumeration. The cache uses this value when
  109. /// it evicts objects; objects with a lower cost are removed from
  110. /// the cache before objects with a higher cost.
  111. /// </param>
  112. /// <param name="eventRemoveCallback">
  113. /// A delegate that, if provided, is called when an object is
  114. /// removed from the cache. You can use this to notify
  115. /// applications when their objects are deleted from the
  116. /// cache.
  117. /// </param>
  118. /// <returns>The Object item added to the Cache.</returns>
  119. public object Add (string strKey, object objItem, CacheDependency objDependency,
  120. DateTime absolutExpiration, TimeSpan slidingExpiration,
  121. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  122. return Add (strKey, objItem, objDependency, absolutExpiration,
  123. slidingExpiration, enumPriority, eventRemoveCallback, true, false);
  124. }
  125. private object Add (string strKey,
  126. object objItem,
  127. CacheDependency objDependency,
  128. DateTime absolutExpiration,
  129. TimeSpan slidingExpiration,
  130. CacheItemPriority enumPriority,
  131. CacheItemRemovedCallback eventRemoveCallback,
  132. bool pub,
  133. bool overwrite) {
  134. if (strKey == null)
  135. throw new ArgumentNullException ("strKey");
  136. if (objItem == null)
  137. throw new ArgumentNullException ("objItem");
  138. if (slidingExpiration > _datetimeOneYear)
  139. throw new ArgumentOutOfRangeException ("slidingExpiration");
  140. CacheEntry objEntry;
  141. CacheEntry objOldEntry = null;
  142. long longHitRange = 10000;
  143. // todo: check decay and make up the minHit range
  144. objEntry = new CacheEntry (this,
  145. strKey,
  146. objItem,
  147. objDependency,
  148. eventRemoveCallback,
  149. absolutExpiration,
  150. slidingExpiration,
  151. longHitRange,
  152. pub,
  153. enumPriority);
  154. _lockEntries.AcquireWriterLock (-1);
  155. try {
  156. _nItems++;
  157. if (_arrEntries.Contains (strKey)) {
  158. if (overwrite)
  159. objOldEntry = _arrEntries [strKey] as CacheEntry;
  160. else
  161. return null;
  162. }
  163. objEntry.Hit ();
  164. _arrEntries [strKey] = objEntry;
  165. // If we have any kind of expiration add into the CacheExpires
  166. // Do this under the lock so no-one can retrieve the objEntry
  167. // before it is fully initialized.
  168. if (objEntry.HasSlidingExpiration || objEntry.HasAbsoluteExpiration) {
  169. if (objEntry.HasSlidingExpiration)
  170. objEntry.Expires = DateTime.UtcNow.Ticks + objEntry.SlidingExpiration;
  171. _objExpires.Add (objEntry);
  172. }
  173. } finally {
  174. _lockEntries.ReleaseLock ();
  175. }
  176. if (objOldEntry != null) {
  177. if (objOldEntry.HasAbsoluteExpiration || objOldEntry.HasSlidingExpiration)
  178. _objExpires.Remove (objOldEntry);
  179. objOldEntry.Close (CacheItemRemovedReason.Removed);
  180. }
  181. return objEntry.Item;
  182. }
  183. /// <summary>
  184. /// Inserts an item into the Cache object with a cache key to
  185. /// reference its location and using default values provided by
  186. /// the CacheItemPriority enumeration.
  187. /// </summary>
  188. /// <param name="strKey">The cache key used to reference the item.</param>
  189. /// <param name="objItem">The item to be added to the cache.</param>
  190. public void Insert (string strKey, object objItem) {
  191. Add (strKey, objItem, null, NoAbsoluteExpiration, NoSlidingExpiration,
  192. CacheItemPriority.Default, null, true, true);
  193. }
  194. /// <summary>
  195. /// Inserts an object into the Cache that has file or key dependencies.
  196. /// </summary>
  197. /// <param name="strKey">The cache key used to reference the item.</param>
  198. /// <param name="objItem">The item to be added to the cache.</param>
  199. /// <param name="objDependency">
  200. /// The file or cache key dependencies for the item. When any
  201. /// dependency changes, the object becomes invalid and is removed
  202. /// from the cache. If there are no dependencies, this paramter
  203. /// contains a null reference.
  204. /// </param>
  205. public void Insert (string strKey, object objItem, CacheDependency objDependency) {
  206. Add (strKey, objItem, objDependency, NoAbsoluteExpiration, NoSlidingExpiration,
  207. CacheItemPriority.Default, null, true, true);
  208. }
  209. /// <summary>
  210. /// Inserts an object into the Cache with dependencies and expiration policies.
  211. /// </summary>
  212. /// <param name="strKey">The cache key used to reference the item.</param>
  213. /// <param name="objItem">The item to be added to the cache.</param>
  214. /// <param name="objDependency">
  215. /// The file or cache key dependencies for the item. When any
  216. /// dependency changes, the object becomes invalid and is removed
  217. /// from the cache. If there are no dependencies, this paramter
  218. /// contains a null reference.
  219. /// </param>
  220. /// <param name="absolutExpiration">
  221. /// The time at which the added object expires and is removed from the cache.
  222. /// </param>
  223. /// <param name="slidingExpiration">The interval between the
  224. /// time the added object was last accessed and when that object
  225. /// expires. If this value is the equivalent of 20 minutes, the
  226. /// object expires and is removed from the cache 20 minutes after
  227. /// it is last accessed.
  228. /// </param>
  229. public void Insert (string strKey, object objItem, CacheDependency objDependency,
  230. DateTime absolutExpiration, TimeSpan slidingExpiration) {
  231. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  232. CacheItemPriority.Default, null, true, true);
  233. }
  234. /// <summary>
  235. /// Inserts an object into the Cache object with dependencies,
  236. /// expiration and priority policies, and a delegate you can use
  237. /// to notify your application when the inserted item is removed
  238. /// from the Cache.
  239. /// </summary>
  240. /// <param name="strKey">The cache key used to reference the item.</param>
  241. /// <param name="objItem">The item to be added to the cache.</param>
  242. /// <param name="objDependency">
  243. /// The file or cache key dependencies for the item. When any
  244. /// dependency changes, the object becomes invalid and is removed
  245. /// from the cache. If there are no dependencies, this paramter
  246. /// contains a null reference.
  247. /// </param>
  248. /// <param name="absolutExpiration">
  249. /// The time at which the added object expires and is removed from the cache.
  250. /// </param>
  251. /// <param name="slidingExpiration">
  252. /// The interval between the time the added object was last
  253. /// accessed and when that object expires. If this value is the
  254. /// equivalent of 20 minutes, the object expires and is removed
  255. /// from the cache 20 minutes after it is last accessed.
  256. /// </param>
  257. /// <param name="enumPriority">The relative cost of the object,
  258. /// as expressed by the CacheItemPriority enumeration. The cache
  259. /// uses this value when it evicts objects; objects with a lower
  260. /// cost are removed from the cache before objects with a higher
  261. /// cost.
  262. /// </param>
  263. /// <param name="eventRemoveCallback">A delegate that, if
  264. /// provided, is called when an object is removed from the cache.
  265. /// You can use this to notify applications when their objects
  266. /// are deleted from the cache.
  267. /// </param>
  268. public void Insert (string strKey, object objItem, CacheDependency objDependency,
  269. DateTime absolutExpiration, TimeSpan slidingExpiration,
  270. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  271. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  272. enumPriority, eventRemoveCallback, true, true);
  273. }
  274. // Called from other internal System.Web methods to add non-public objects into
  275. // cache, like output cache etc
  276. internal void InsertPrivate (string strKey, object objItem, CacheDependency objDependency,
  277. DateTime absolutExpiration, TimeSpan slidingExpiration,
  278. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  279. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  280. enumPriority, eventRemoveCallback, false, true);
  281. }
  282. /// <summary>
  283. /// Removes the specified item from the Cache object.
  284. /// </summary>
  285. /// <param name="strKey">The cache key for the cache item to remove.</param>
  286. /// <returns>
  287. /// The item removed from the Cache. If the value in the key
  288. /// parameter is not found, returns a null reference.
  289. /// </returns>
  290. public object Remove (string strKey) {
  291. return Remove (strKey, CacheItemRemovedReason.Removed);
  292. }
  293. /// <summary>
  294. /// Internal method that updates the cache, decremenents the
  295. /// number of existing items and call close on the cache entry.
  296. /// This method is also used from the ExpiresBuckets class to
  297. /// remove an item during GC flush.
  298. /// </summary>
  299. /// <param name="strKey">The cache key for the cache item to remove.</param>
  300. /// <param name="enumReason">Reason why the item is removed.</param>
  301. /// <returns>
  302. /// The item removed from the Cache. If the value in the key
  303. /// parameter is not found, returns a null reference.
  304. /// </returns>
  305. internal object Remove (string strKey, CacheItemRemovedReason enumReason) {
  306. CacheEntry objEntry = null;
  307. if (strKey == null)
  308. throw new ArgumentNullException ("strKey");
  309. _lockEntries.AcquireWriterLock (-1);
  310. try {
  311. objEntry = _arrEntries [strKey] as CacheEntry;
  312. if (null == objEntry)
  313. return null;
  314. _arrEntries.Remove (strKey);
  315. _nItems--;
  316. }
  317. finally {
  318. _lockEntries.ReleaseWriterLock ();
  319. }
  320. if (objEntry.HasAbsoluteExpiration || objEntry.HasSlidingExpiration)
  321. _objExpires.Remove (objEntry);
  322. objEntry.Close (enumReason);
  323. return objEntry.Item;
  324. }
  325. /// <summary>
  326. /// Retrieves the specified item from the Cache object.
  327. /// </summary>
  328. /// <param name="strKey">The identifier for the cache item to retrieve.</param>
  329. /// <returns>The retrieved cache item, or a null reference.</returns>
  330. public object Get (string strKey) {
  331. CacheEntry objEntry = GetEntry (strKey);
  332. if (objEntry == null)
  333. return null;
  334. return objEntry.Item;
  335. }
  336. internal CacheEntry GetEntry (string strKey) {
  337. CacheEntry objEntry = null;
  338. long ticksNow = DateTime.UtcNow.Ticks;
  339. if (strKey == null)
  340. throw new ArgumentNullException ("strKey");
  341. _lockEntries.AcquireReaderLock (-1);
  342. try {
  343. objEntry = _arrEntries [strKey] as CacheEntry;
  344. if (null == objEntry)
  345. return null;
  346. }
  347. finally {
  348. _lockEntries.ReleaseReaderLock ();
  349. }
  350. if (objEntry.HasSlidingExpiration || objEntry.HasAbsoluteExpiration) {
  351. if (objEntry.Expires < ticksNow) {
  352. Remove (strKey, CacheItemRemovedReason.Expired);
  353. return null;
  354. }
  355. }
  356. objEntry.Hit ();
  357. if (objEntry.HasSlidingExpiration) {
  358. objEntry.Expires = ticksNow + objEntry.SlidingExpiration;
  359. }
  360. return objEntry;
  361. }
  362. /// <summary>
  363. /// Gets the number of items stored in the cache.
  364. /// </summary>
  365. public int Count {
  366. get { return _nItems; }
  367. }
  368. /// <summary>
  369. /// Gets or sets the cache item at the specified key.
  370. /// </summary>
  371. public object this [string strKey] {
  372. get {
  373. return Get (strKey);
  374. }
  375. set {
  376. Insert (strKey, value);
  377. }
  378. }
  379. }
  380. }