Cache.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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. Interlocked.Increment (ref _nItems);
  155. _lockEntries.AcquireWriterLock (-1);
  156. try {
  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. } finally {
  166. _lockEntries.ReleaseLock ();
  167. }
  168. if (objOldEntry != null) {
  169. if (objOldEntry.HasAbsoluteExpiration || objOldEntry.HasSlidingExpiration)
  170. _objExpires.Remove (objOldEntry);
  171. objOldEntry.Close (CacheItemRemovedReason.Removed);
  172. }
  173. // If we have any kind of expiration add into the CacheExpires class
  174. if (objEntry.HasSlidingExpiration || objEntry.HasAbsoluteExpiration) {
  175. if (objEntry.HasSlidingExpiration)
  176. objEntry.Expires = DateTime.UtcNow.Ticks + objEntry.SlidingExpiration;
  177. _objExpires.Add (objEntry);
  178. }
  179. return objEntry.Item;
  180. }
  181. /// <summary>
  182. /// Inserts an item into the Cache object with a cache key to
  183. /// reference its location and using default values provided by
  184. /// the CacheItemPriority enumeration.
  185. /// </summary>
  186. /// <param name="strKey">The cache key used to reference the item.</param>
  187. /// <param name="objItem">The item to be added to the cache.</param>
  188. public void Insert (string strKey, object objItem) {
  189. Add (strKey, objItem, null, NoAbsoluteExpiration, NoSlidingExpiration,
  190. CacheItemPriority.Default, null, true, true);
  191. }
  192. /// <summary>
  193. /// Inserts an object into the Cache that has file or key dependencies.
  194. /// </summary>
  195. /// <param name="strKey">The cache key used to reference the item.</param>
  196. /// <param name="objItem">The item to be added to the cache.</param>
  197. /// <param name="objDependency">
  198. /// The file or cache key dependencies for the item. When any
  199. /// dependency changes, the object becomes invalid and is removed
  200. /// from the cache. If there are no dependencies, this paramter
  201. /// contains a null reference.
  202. /// </param>
  203. public void Insert (string strKey, object objItem, CacheDependency objDependency) {
  204. Add (strKey, objItem, objDependency, NoAbsoluteExpiration, NoSlidingExpiration,
  205. CacheItemPriority.Default, null, true, true);
  206. }
  207. /// <summary>
  208. /// Inserts an object into the Cache with dependencies and expiration policies.
  209. /// </summary>
  210. /// <param name="strKey">The cache key used to reference the item.</param>
  211. /// <param name="objItem">The item to be added to the cache.</param>
  212. /// <param name="objDependency">
  213. /// The file or cache key dependencies for the item. When any
  214. /// dependency changes, the object becomes invalid and is removed
  215. /// from the cache. If there are no dependencies, this paramter
  216. /// contains a null reference.
  217. /// </param>
  218. /// <param name="absolutExpiration">
  219. /// The time at which the added object expires and is removed from the cache.
  220. /// </param>
  221. /// <param name="slidingExpiration">The interval between the
  222. /// time the added object was last accessed and when that object
  223. /// expires. If this value is the equivalent of 20 minutes, the
  224. /// object expires and is removed from the cache 20 minutes after
  225. /// it is last accessed.
  226. /// </param>
  227. public void Insert (string strKey, object objItem, CacheDependency objDependency,
  228. DateTime absolutExpiration, TimeSpan slidingExpiration) {
  229. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  230. CacheItemPriority.Default, null, true, true);
  231. }
  232. /// <summary>
  233. /// Inserts an object into the Cache object with dependencies,
  234. /// expiration and priority policies, and a delegate you can use
  235. /// to notify your application when the inserted item is removed
  236. /// from the Cache.
  237. /// </summary>
  238. /// <param name="strKey">The cache key used to reference the item.</param>
  239. /// <param name="objItem">The item to be added to the cache.</param>
  240. /// <param name="objDependency">
  241. /// The file or cache key dependencies for the item. When any
  242. /// dependency changes, the object becomes invalid and is removed
  243. /// from the cache. If there are no dependencies, this paramter
  244. /// contains a null reference.
  245. /// </param>
  246. /// <param name="absolutExpiration">
  247. /// The time at which the added object expires and is removed from the cache.
  248. /// </param>
  249. /// <param name="slidingExpiration">
  250. /// The interval between the time the added object was last
  251. /// accessed and when that object expires. If this value is the
  252. /// equivalent of 20 minutes, the object expires and is removed
  253. /// from the cache 20 minutes after it is last accessed.
  254. /// </param>
  255. /// <param name="enumPriority">The relative cost of the object,
  256. /// as expressed by the CacheItemPriority enumeration. The cache
  257. /// uses this value when it evicts objects; objects with a lower
  258. /// cost are removed from the cache before objects with a higher
  259. /// cost.
  260. /// </param>
  261. /// <param name="eventRemoveCallback">A delegate that, if
  262. /// provided, is called when an object is removed from the cache.
  263. /// You can use this to notify applications when their objects
  264. /// are deleted from the cache.
  265. /// </param>
  266. public void Insert (string strKey, object objItem, CacheDependency objDependency,
  267. DateTime absolutExpiration, TimeSpan slidingExpiration,
  268. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  269. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  270. enumPriority, eventRemoveCallback, true, true);
  271. }
  272. // Called from other internal System.Web methods to add non-public objects into
  273. // cache, like output cache etc
  274. internal void InsertPrivate (string strKey, object objItem, CacheDependency objDependency,
  275. DateTime absolutExpiration, TimeSpan slidingExpiration,
  276. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  277. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  278. enumPriority, eventRemoveCallback, false, true);
  279. }
  280. /// <summary>
  281. /// Removes the specified item from the Cache object.
  282. /// </summary>
  283. /// <param name="strKey">The cache key for the cache item to remove.</param>
  284. /// <returns>
  285. /// The item removed from the Cache. If the value in the key
  286. /// parameter is not found, returns a null reference.
  287. /// </returns>
  288. public object Remove (string strKey) {
  289. return Remove (strKey, CacheItemRemovedReason.Removed);
  290. }
  291. /// <summary>
  292. /// Internal method that updates the cache, decremenents the
  293. /// number of existing items and call close on the cache entry.
  294. /// This method is also used from the ExpiresBuckets class to
  295. /// remove an item during GC flush.
  296. /// </summary>
  297. /// <param name="strKey">The cache key for the cache item to remove.</param>
  298. /// <param name="enumReason">Reason why the item is removed.</param>
  299. /// <returns>
  300. /// The item removed from the Cache. If the value in the key
  301. /// parameter is not found, returns a null reference.
  302. /// </returns>
  303. internal object Remove (string strKey, CacheItemRemovedReason enumReason) {
  304. CacheEntry objEntry = null;
  305. if (strKey == null)
  306. throw new ArgumentNullException ("strKey");
  307. _lockEntries.AcquireWriterLock (-1);
  308. try {
  309. objEntry = _arrEntries [strKey] as CacheEntry;
  310. if (null == objEntry)
  311. return null;
  312. _arrEntries.Remove (strKey);
  313. }
  314. finally {
  315. _lockEntries.ReleaseWriterLock ();
  316. }
  317. if (objEntry.HasAbsoluteExpiration || objEntry.HasSlidingExpiration)
  318. _objExpires.Remove (objEntry);
  319. objEntry.Close (enumReason);
  320. Interlocked.Decrement (ref _nItems);
  321. return objEntry.Item;
  322. }
  323. /// <summary>
  324. /// Retrieves the specified item from the Cache object.
  325. /// </summary>
  326. /// <param name="strKey">The identifier for the cache item to retrieve.</param>
  327. /// <returns>The retrieved cache item, or a null reference.</returns>
  328. public object Get (string strKey) {
  329. CacheEntry objEntry = GetEntry (strKey);
  330. if (objEntry == null)
  331. return null;
  332. return objEntry.Item;
  333. }
  334. internal CacheEntry GetEntry (string strKey) {
  335. CacheEntry objEntry = null;
  336. long ticksNow = DateTime.UtcNow.Ticks;
  337. if (strKey == null)
  338. throw new ArgumentNullException ("strKey");
  339. _lockEntries.AcquireReaderLock (-1);
  340. try {
  341. objEntry = _arrEntries [strKey] as CacheEntry;
  342. if (null == objEntry)
  343. return null;
  344. }
  345. finally {
  346. _lockEntries.ReleaseReaderLock ();
  347. }
  348. if (objEntry.HasSlidingExpiration || objEntry.HasAbsoluteExpiration) {
  349. if (objEntry.Expires < ticksNow) {
  350. Remove (strKey, CacheItemRemovedReason.Expired);
  351. return null;
  352. }
  353. }
  354. objEntry.Hit ();
  355. if (objEntry.HasSlidingExpiration) {
  356. long ticksExpires = ticksNow + objEntry.SlidingExpiration;
  357. _objExpires.Update (objEntry, ticksExpires);
  358. objEntry.Expires = ticksExpires;
  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. }