ExpiresBuckets.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. //
  2. // System.Web.Caching
  3. //
  4. // Author:
  5. // Patrik Torstensson ([email protected])
  6. // Daniel Cazzulino [DHC] ([email protected])
  7. //
  8. using System;
  9. using System.Collections;
  10. using System.Threading;
  11. namespace System.Web.Caching {
  12. /// <summary>
  13. /// Responsible for holding a cache entry in the linked list bucket.
  14. /// </summary>
  15. internal struct ExpiresEntry {
  16. internal CacheEntry Entry;
  17. internal long TicksExpires;
  18. internal int _intNext;
  19. }
  20. /// <summary>
  21. /// Holds cache entries that has a expiration in a bucket list.
  22. /// </summary>
  23. internal class ExpiresBucket {
  24. private static int MIN_ENTRIES = 4;
  25. private byte _byteID;
  26. private int _intSize;
  27. private int _intCount;
  28. private int _intNext;
  29. private Cache _objManager;
  30. private ExpiresEntry [] _arrEntries;
  31. private System.Threading.ReaderWriterLock _lock = new System.Threading.ReaderWriterLock();
  32. /// <summary>
  33. /// Keeps a list of indexes in the list which are available to place new items. [DHC]
  34. /// </summary>
  35. private Int32Collection _freeidx = new Int32Collection();
  36. /// <summary>
  37. /// Constructs a new bucket.
  38. /// </summary>
  39. /// <param name="bucket">Current bucket ID.</param>
  40. /// <param name="objManager">Cache manager reponsible for the item(s) in the expires bucket.</param>
  41. internal ExpiresBucket (byte bucket, Cache objManager) {
  42. _objManager = objManager;
  43. Initialize(bucket);
  44. }
  45. /// <summary>
  46. /// Initializes the expires bucket, creates a linked list of MIN_ENTRIES.
  47. /// </summary>
  48. /// <param name="bucket">Bucket ID.</param>
  49. private void Initialize (byte bucket) {
  50. _byteID = bucket;
  51. _intNext = 0;
  52. _intCount = 0;
  53. _arrEntries = new ExpiresEntry [MIN_ENTRIES];
  54. _intSize = MIN_ENTRIES;
  55. int intPos = 0;
  56. do {
  57. _arrEntries[intPos]._intNext = intPos + 1;
  58. _arrEntries[intPos].TicksExpires = Cache.NoAbsoluteExpiration.Ticks;
  59. intPos++;
  60. } while (intPos < _intSize);
  61. _arrEntries[_intSize - 1]._intNext = -1;
  62. }
  63. /// <summary>
  64. /// Expands the bucket linked array list.
  65. /// </summary>
  66. private void Expand () {
  67. _lock.AcquireWriterLock(-1);
  68. try {
  69. int oldsize = _intSize;
  70. _intSize *= 2;
  71. // Copy items to the new list.
  72. ExpiresEntry[] newlist = new ExpiresEntry[_intSize];
  73. _arrEntries.CopyTo(newlist, 0);
  74. // Set last element to point to the next new empty element
  75. _intNext = oldsize;
  76. newlist[oldsize - 1]._intNext = oldsize;
  77. // Initialize positions for the rest of new elements.
  78. for (int i = oldsize; i < _intSize; i++) {
  79. newlist[i]._intNext = i + 1;
  80. newlist[i].TicksExpires = Cache.NoAbsoluteExpiration.Ticks;
  81. }
  82. // Last item signals the expansion of the list.
  83. newlist[_intSize - 1]._intNext = -1;
  84. // Replace the existing list.
  85. _arrEntries = newlist;
  86. }
  87. finally {
  88. _lock.ReleaseWriterLock();
  89. }
  90. }
  91. /// <summary>
  92. /// Adds a cache entry into the expires bucket.
  93. /// </summary>
  94. /// <param name="objEntry">Cache Entry object to be added.</param>
  95. internal void Add (CacheEntry objEntry) {
  96. _lock.AcquireWriterLock(-1);
  97. try {
  98. if (_intNext == -1) {
  99. if (_freeidx.Count == 0)
  100. Expand();
  101. else {
  102. _intNext = _freeidx[0];
  103. _freeidx.Remove(_intNext);
  104. }
  105. }
  106. _arrEntries[_intNext].TicksExpires = objEntry.Expires;
  107. _arrEntries[_intNext].Entry = objEntry;
  108. objEntry.ExpiresBucket = _byteID;
  109. objEntry.ExpiresIndex = _intNext;
  110. // If there are free indexes in the list, reuse them for the _next value.
  111. if (_freeidx.Count != 0) {
  112. _intNext = _freeidx [0];
  113. _freeidx.Remove(_intNext);
  114. } else
  115. _intNext = _arrEntries[_intNext]._intNext;
  116. _intCount++;
  117. }
  118. finally {
  119. _lock.ReleaseWriterLock();
  120. }
  121. }
  122. /// <summary>
  123. /// Removes a cache entry from the expires bucket.
  124. /// </summary>
  125. /// <param name="objEntry">Cache entry to be removed.</param>
  126. internal void Remove(CacheEntry objEntry) {
  127. // Check if this is our bucket
  128. if (objEntry.ExpiresBucket != _byteID) return;
  129. if (objEntry.ExpiresIndex == CacheEntry.NoIndexInBucket) return;
  130. _lock.AcquireWriterLock(-1);
  131. try {
  132. if (_arrEntries.Length < objEntry.ExpiresIndex) return;
  133. _intCount--;
  134. // Push the index as a free one.
  135. _freeidx.Add(objEntry.ExpiresIndex);
  136. _arrEntries[objEntry.ExpiresIndex].Entry = null;
  137. // Clear bucket-related values from the item.
  138. objEntry.ExpiresBucket = CacheEntry.NoBucketHash;
  139. objEntry.ExpiresIndex = CacheEntry.NoIndexInBucket;
  140. }
  141. finally {
  142. //Releases both reader & writer locks
  143. _lock.ReleaseWriterLock();
  144. }
  145. }
  146. /// <summary>
  147. /// Updates a cache entry in the expires bucket, this is called during a hit of an item if the
  148. /// cache item has a sliding expiration. The function is responsible for updating the cache
  149. /// entry.
  150. /// </summary>
  151. /// <param name="objEntry">Cache entry to update.</param>
  152. /// <param name="ticksExpires">New expiration value for the cache entry.</param>
  153. internal void Update(CacheEntry objEntry, long ticksExpires) {
  154. // Check if this is our bucket
  155. if (objEntry.ExpiresBucket != _byteID) return;
  156. if (objEntry.ExpiresIndex == CacheEntry.NoIndexInBucket) return;
  157. _lock.AcquireWriterLock(-1);
  158. try {
  159. if (_arrEntries.Length < objEntry.ExpiresIndex) return;
  160. // Proceed to update.
  161. _arrEntries[objEntry.ExpiresIndex].TicksExpires = ticksExpires;
  162. _arrEntries[objEntry.ExpiresIndex].Entry.Expires = ticksExpires;
  163. }
  164. finally {
  165. //Releases both read & write locks
  166. _lock.ReleaseWriterLock();
  167. }
  168. }
  169. /// <summary>
  170. /// Flushes all cache entries that has expired and removes them from the cache manager.
  171. /// </summary>
  172. internal void FlushExpiredItems() {
  173. ExpiresEntry objEntry;
  174. ArrayList removeList = null;
  175. ArrayList flushList = null;
  176. int intPos;
  177. long ticksNow;
  178. ticksNow = DateTime.Now.Ticks;
  179. intPos = 0;
  180. // Lookup all items that needs to be removed, this is done in a two part
  181. // operation to minimize the locking time.
  182. _lock.AcquireReaderLock (-1);
  183. try {
  184. do {
  185. objEntry = _arrEntries [intPos];
  186. if (null != objEntry.Entry &&
  187. ((objEntry.TicksExpires < ticksNow) || objEntry.Entry.ExpiresBucket != _byteID))
  188. {
  189. if (null == removeList)
  190. removeList = new ArrayList ();
  191. removeList.Add (objEntry);
  192. }
  193. intPos++;
  194. } while (intPos < _intSize);
  195. }
  196. finally {
  197. _lock.ReleaseReaderLock ();
  198. }
  199. if (null != removeList) {
  200. flushList = new ArrayList (removeList.Count);
  201. _lock.AcquireWriterLock (-1);
  202. try {
  203. foreach (ExpiresEntry entry in removeList) {
  204. ExpiresEntry e = entry;
  205. int id = entry.Entry.ExpiresIndex;
  206. //push the index for reuse
  207. _freeidx.Add (id);
  208. if (entry.Entry.ExpiresBucket == _byteID) {
  209. // add to our flush list
  210. flushList.Add (e.Entry);
  211. // Remove from bucket
  212. e.Entry.ExpiresBucket = CacheEntry.NoBucketHash;
  213. e.Entry.ExpiresIndex = CacheEntry.NoIndexInBucket;
  214. }
  215. e.Entry = null;
  216. // Entries is structs, put it back
  217. _arrEntries [id] = e;
  218. }
  219. }
  220. finally {
  221. _lock.ReleaseWriterLock ();
  222. }
  223. // We can call this without locks, it can takes time due to callbacks to user code
  224. foreach (CacheEntry entry in flushList)
  225. _objManager.Remove (entry.Key, CacheItemRemovedReason.Expired);
  226. flushList = null;
  227. removeList = null;
  228. }
  229. }
  230. /// <summary>
  231. /// Returns the current size of the expires bucket.
  232. /// </summary>
  233. internal int Size {
  234. get {
  235. return _intSize;
  236. }
  237. }
  238. /// <summary>
  239. /// Returns number of items in the bucket.
  240. /// </summary>
  241. internal int Count {
  242. get {
  243. return _intCount;
  244. }
  245. }
  246. #region Private Int32Collection
  247. /* This file has been automatically generated by TextBox -- DO NOT EDIT! */
  248. /*
  249. Int32Collection
  250. Int32Collection.Enumerator
  251. These C# classes implement a strongly-typed collection of
  252. Int32 objects.
  253. The internal representation is an array of Int32, so the performance
  254. characteristics will be more like a vector than a list, to use STL terminology.
  255. The implementation is optimized for value-types, as it goes to great length to
  256. avoid the overhead of boxing and unboxing. But it should also work well for
  257. reference types.
  258. Mad props to Nick Wienholt <[email protected]> for assisting me in
  259. this research, and the testing, the benchmarking, and of course, the
  260. implementation!
  261. Last but not least, a quick shout out to Kit George, for his generous
  262. contribution to the dotnet mailing list -- a code-generator for
  263. CollectionBase-derived classes:
  264. http://discuss.develop.com/archives/wa.exe?A2=ind0107C&L=DOTNET&P=R35911
  265. This was the original inspiration for the fine code you are now enjoying.
  266. - Shawn Van Ness
  267. Other folks who've contributed:
  268. Ethan Smith <[email protected]> (minor perf. improvements)
  269. Joel Mueller <[email protected]> (major perf. improvements)
  270. Chris Sells <[email protected]> (generative programming guru)
  271. Patrice Lafond <[email protected]> (a bug fix -- yikes!)
  272. */
  273. /// <summary>
  274. /// An optimized collection for holding <see cref="Int32"/> values.
  275. /// </summary>
  276. [System.Serializable]
  277. private class Int32Collection : System.Collections.ICollection, System.Collections.IList, System.Collections.IEnumerable {
  278. #region Private vars & ctors
  279. private const int DefaultMinimumCapacity = 16;
  280. private System.Int32[] m_array = new System.Int32[DefaultMinimumCapacity];
  281. private int m_count = 0;
  282. private int m_version = 0;
  283. /// <summary />
  284. public Int32Collection() {
  285. }
  286. /// <summary />
  287. public Int32Collection(Int32Collection collection) {
  288. AddRange(collection); }
  289. /// <summary />
  290. public Int32Collection(System.Int32[] array) {
  291. AddRange(array); }
  292. #endregion
  293. #region Public members
  294. /// <summary />
  295. public int Count {
  296. get {
  297. return m_count; }
  298. }
  299. /// <summary />
  300. public void CopyTo(System.Int32[] array) {
  301. this.CopyTo(array, 0);
  302. }
  303. /// <summary />
  304. public void CopyTo(System.Int32[] array, int start) {
  305. if (m_count > array.GetUpperBound(0)+1-start)
  306. throw new System.ArgumentException("Destination array was not long enough.");
  307. // for (int i=0; i < m_count; ++i) array[start+i] = m_array[i];
  308. System.Array.Copy(m_array, 0, array, start, m_count);
  309. }
  310. /// <summary />
  311. public System.Int32 this[int index] {
  312. get {
  313. ValidateIndex(index); // throws
  314. return m_array[index];
  315. }
  316. set {
  317. ValidateIndex(index); // throws
  318. ++m_version;
  319. m_array[index] = value;
  320. }
  321. }
  322. /// <summary />
  323. public int Add(System.Int32 item) {
  324. if (NeedsGrowth())
  325. Grow();
  326. ++m_version;
  327. m_array[m_count] = item;
  328. return m_count++;
  329. }
  330. /// <summary />
  331. public void Clear() {
  332. ++m_version;
  333. m_array = new System.Int32[DefaultMinimumCapacity];
  334. m_count = 0;
  335. }
  336. /// <summary />
  337. public bool Contains(System.Int32 item) {
  338. return ((IndexOf(item) == -1)?false:true);
  339. }
  340. /// <summary />
  341. public int IndexOf(System.Int32 item) {
  342. for (int i=0; i < m_count; ++i)
  343. if (m_array[i].Equals(item))
  344. return i;
  345. return -1;
  346. }
  347. /// <summary />
  348. public void Insert(int position, System.Int32 item) {
  349. ValidateIndex(position,true); // throws
  350. if (NeedsGrowth())
  351. Grow();
  352. ++m_version;
  353. System.Array.Copy(m_array, position, m_array, position+1, m_count-position);
  354. m_array[position] = item;
  355. m_count++;
  356. }
  357. /// <summary />
  358. public void Remove(System.Int32 item) {
  359. int index = IndexOf(item);
  360. if (index < 0)
  361. throw new System.ArgumentException("Cannot remove the specified item because it was not found in the specified Collection.");
  362. RemoveAt(index);
  363. }
  364. /// <summary />
  365. public void RemoveAt(int index) {
  366. ValidateIndex(index); // throws
  367. ++m_version;
  368. m_count--;
  369. System.Array.Copy(m_array, index+1, m_array, index, m_count-index);
  370. if (NeedsTrimming())
  371. Trim();
  372. }
  373. // Public helpers (just to mimic some nice features of ArrayList)
  374. /// <summary />
  375. public int Capacity {
  376. get {
  377. return m_array.Length; }
  378. set {
  379. if (value < m_count) value = m_count;
  380. if (value < DefaultMinimumCapacity) value = DefaultMinimumCapacity;
  381. if (m_array.Length == value) return;
  382. ++m_version;
  383. System.Int32[] temp = new System.Int32[value];
  384. System.Array.Copy(m_array, 0, temp, 0, m_count);
  385. m_array = temp;
  386. }
  387. }
  388. /// <summary />
  389. public void AddRange(Int32Collection collection) {
  390. ++m_version;
  391. Capacity += collection.Count;
  392. System.Array.Copy(collection.m_array, 0, this.m_array, m_count, collection.m_count);
  393. m_count += collection.Count;
  394. }
  395. /// <summary />
  396. public void AddRange(System.Int32[] array) {
  397. ++m_version;
  398. Capacity += array.Length;
  399. System.Array.Copy(array, 0, this.m_array, m_count, array.Length);
  400. m_count += array.Length;
  401. }
  402. #endregion
  403. #region Private helper methods
  404. private void ValidateIndex(int index) {
  405. ValidateIndex(index,false);
  406. }
  407. private void ValidateIndex(int index, bool allowEqualEnd) {
  408. int max = (allowEqualEnd)?(m_count):(m_count-1);
  409. if (index < 0 || index > max)
  410. throw new System.ArgumentOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.", (object)index, "Specified argument was out of the range of valid values.");
  411. }
  412. private bool NeedsGrowth() {
  413. return (m_count >= Capacity);
  414. }
  415. private void Grow() {
  416. if (NeedsGrowth())
  417. Capacity = m_count*2;
  418. }
  419. private bool NeedsTrimming() {
  420. return (m_count <= Capacity/2);
  421. }
  422. private void Trim() {
  423. if (NeedsTrimming())
  424. Capacity = m_count;
  425. }
  426. #endregion
  427. #region System.Collections.ICollection implementation
  428. bool System.Collections.ICollection.IsSynchronized {
  429. get {
  430. return m_array.IsSynchronized; }
  431. }
  432. object System.Collections.ICollection.SyncRoot {
  433. get {
  434. return m_array.SyncRoot; }
  435. }
  436. void System.Collections.ICollection.CopyTo(System.Array array, int start) {
  437. this.CopyTo((System.Int32[])array, start);
  438. }
  439. #endregion
  440. #region System.Collections.IList implementation
  441. bool System.Collections.IList.IsFixedSize {
  442. get {
  443. return false; }
  444. }
  445. bool System.Collections.IList.IsReadOnly {
  446. get {
  447. return false; }
  448. }
  449. object System.Collections.IList.this[int index] {
  450. get { return (object)this[index]; }
  451. set { this[index] = (System.Int32)value; }
  452. }
  453. int System.Collections.IList.Add(object item) {
  454. return this.Add((System.Int32)item);
  455. }
  456. bool System.Collections.IList.Contains(object item) {
  457. return this.Contains((System.Int32)item);
  458. }
  459. int System.Collections.IList.IndexOf(object item) {
  460. return this.IndexOf((System.Int32)item);
  461. }
  462. void System.Collections.IList.Insert(int position, object item) {
  463. this.Insert(position, (System.Int32)item);
  464. }
  465. void System.Collections.IList.Remove(object item) {
  466. this.Remove((System.Int32)item);
  467. }
  468. #endregion
  469. #region System.Collections.IEnumerable and enumerator implementation
  470. /// <summary />
  471. public Enumerator GetEnumerator() {
  472. return new Enumerator(this);
  473. }
  474. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
  475. return GetEnumerator();
  476. }
  477. // Nested enumerator class
  478. /// <summary />
  479. public class Enumerator : System.Collections.IEnumerator {
  480. private Int32Collection m_collection;
  481. private int m_index;
  482. private int m_version;
  483. // Construction
  484. /// <summary />
  485. public Enumerator(Int32Collection tc) {
  486. m_collection = tc;
  487. m_index = -1;
  488. m_version = tc.m_version;
  489. }
  490. /// <summary />
  491. public System.Int32 Current {
  492. get {
  493. return m_collection[m_index]; }
  494. }
  495. /// <summary />
  496. public bool MoveNext() {
  497. if (m_version != m_collection.m_version)
  498. throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute.");
  499. ++m_index;
  500. return (m_index < m_collection.Count)?true:false;
  501. }
  502. /// <summary />
  503. public void Reset() {
  504. if (m_version != m_collection.m_version)
  505. throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute.");
  506. m_index = -1;
  507. }
  508. object System.Collections.IEnumerator.Current {
  509. get {
  510. return (object)(this.Current); }
  511. }
  512. }
  513. #endregion
  514. }
  515. #endregion
  516. }
  517. }