ExpiresBuckets.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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. int id = entry.Entry.ExpiresIndex;
  205. //push the index for reuse
  206. _freeidx.Add (id);
  207. if (entry.Entry.ExpiresBucket == _byteID) {
  208. // add to our flush list
  209. flushList.Add (entry.Entry);
  210. // Remove from bucket
  211. entry.Entry.ExpiresBucket = CacheEntry.NoBucketHash;
  212. entry.Entry.ExpiresIndex = CacheEntry.NoIndexInBucket;
  213. }
  214. entry.Entry = null;
  215. // Entries is structs, put it back
  216. _arrEntries [id] = objEntry;
  217. }
  218. }
  219. finally {
  220. _lock.ReleaseWriterLock ();
  221. }
  222. // We can call this without locks, it can takes time due to callbacks to user code
  223. foreach (CacheEntry entry in flushList)
  224. _objManager.Remove (entry.Key, CacheItemRemovedReason.Expired);
  225. flushList = null;
  226. removeList = null;
  227. }
  228. }
  229. /// <summary>
  230. /// Returns the current size of the expires bucket.
  231. /// </summary>
  232. internal int Size {
  233. get {
  234. return _intSize;
  235. }
  236. }
  237. /// <summary>
  238. /// Returns number of items in the bucket.
  239. /// </summary>
  240. internal int Count {
  241. get {
  242. return _intCount;
  243. }
  244. }
  245. #region Private Int32Collection
  246. /* This file has been automatically generated by TextBox -- DO NOT EDIT! */
  247. /*
  248. Int32Collection
  249. Int32Collection.Enumerator
  250. These C# classes implement a strongly-typed collection of
  251. Int32 objects.
  252. The internal representation is an array of Int32, so the performance
  253. characteristics will be more like a vector than a list, to use STL terminology.
  254. The implementation is optimized for value-types, as it goes to great length to
  255. avoid the overhead of boxing and unboxing. But it should also work well for
  256. reference types.
  257. Mad props to Nick Wienholt <[email protected]> for assisting me in
  258. this research, and the testing, the benchmarking, and of course, the
  259. implementation!
  260. Last but not least, a quick shout out to Kit George, for his generous
  261. contribution to the dotnet mailing list -- a code-generator for
  262. CollectionBase-derived classes:
  263. http://discuss.develop.com/archives/wa.exe?A2=ind0107C&L=DOTNET&P=R35911
  264. This was the original inspiration for the fine code you are now enjoying.
  265. - Shawn Van Ness
  266. Other folks who've contributed:
  267. Ethan Smith <[email protected]> (minor perf. improvements)
  268. Joel Mueller <[email protected]> (major perf. improvements)
  269. Chris Sells <[email protected]> (generative programming guru)
  270. Patrice Lafond <[email protected]> (a bug fix -- yikes!)
  271. */
  272. /// <summary>
  273. /// An optimized collection for holding <see cref="Int32"/> values.
  274. /// </summary>
  275. [System.Serializable]
  276. private class Int32Collection : System.Collections.ICollection, System.Collections.IList, System.Collections.IEnumerable {
  277. #region Private vars & ctors
  278. private const int DefaultMinimumCapacity = 16;
  279. private System.Int32[] m_array = new System.Int32[DefaultMinimumCapacity];
  280. private int m_count = 0;
  281. private int m_version = 0;
  282. /// <summary />
  283. public Int32Collection() {
  284. }
  285. /// <summary />
  286. public Int32Collection(Int32Collection collection) {
  287. AddRange(collection); }
  288. /// <summary />
  289. public Int32Collection(System.Int32[] array) {
  290. AddRange(array); }
  291. #endregion
  292. #region Public members
  293. /// <summary />
  294. public int Count {
  295. get {
  296. return m_count; }
  297. }
  298. /// <summary />
  299. public void CopyTo(System.Int32[] array) {
  300. this.CopyTo(array, 0);
  301. }
  302. /// <summary />
  303. public void CopyTo(System.Int32[] array, int start) {
  304. if (m_count > array.GetUpperBound(0)+1-start)
  305. throw new System.ArgumentException("Destination array was not long enough.");
  306. // for (int i=0; i < m_count; ++i) array[start+i] = m_array[i];
  307. System.Array.Copy(m_array, 0, array, start, m_count);
  308. }
  309. /// <summary />
  310. public System.Int32 this[int index] {
  311. get {
  312. ValidateIndex(index); // throws
  313. return m_array[index];
  314. }
  315. set {
  316. ValidateIndex(index); // throws
  317. ++m_version;
  318. m_array[index] = value;
  319. }
  320. }
  321. /// <summary />
  322. public int Add(System.Int32 item) {
  323. if (NeedsGrowth())
  324. Grow();
  325. ++m_version;
  326. m_array[m_count] = item;
  327. return m_count++;
  328. }
  329. /// <summary />
  330. public void Clear() {
  331. ++m_version;
  332. m_array = new System.Int32[DefaultMinimumCapacity];
  333. m_count = 0;
  334. }
  335. /// <summary />
  336. public bool Contains(System.Int32 item) {
  337. return ((IndexOf(item) == -1)?false:true);
  338. }
  339. /// <summary />
  340. public int IndexOf(System.Int32 item) {
  341. for (int i=0; i < m_count; ++i)
  342. if (m_array[i].Equals(item))
  343. return i;
  344. return -1;
  345. }
  346. /// <summary />
  347. public void Insert(int position, System.Int32 item) {
  348. ValidateIndex(position,true); // throws
  349. if (NeedsGrowth())
  350. Grow();
  351. ++m_version;
  352. System.Array.Copy(m_array, position, m_array, position+1, m_count-position);
  353. m_array[position] = item;
  354. m_count++;
  355. }
  356. /// <summary />
  357. public void Remove(System.Int32 item) {
  358. int index = IndexOf(item);
  359. if (index < 0)
  360. throw new System.ArgumentException("Cannot remove the specified item because it was not found in the specified Collection.");
  361. RemoveAt(index);
  362. }
  363. /// <summary />
  364. public void RemoveAt(int index) {
  365. ValidateIndex(index); // throws
  366. ++m_version;
  367. m_count--;
  368. System.Array.Copy(m_array, index+1, m_array, index, m_count-index);
  369. if (NeedsTrimming())
  370. Trim();
  371. }
  372. // Public helpers (just to mimic some nice features of ArrayList)
  373. /// <summary />
  374. public int Capacity {
  375. get {
  376. return m_array.Length; }
  377. set {
  378. if (value < m_count) value = m_count;
  379. if (value < DefaultMinimumCapacity) value = DefaultMinimumCapacity;
  380. if (m_array.Length == value) return;
  381. ++m_version;
  382. System.Int32[] temp = new System.Int32[value];
  383. System.Array.Copy(m_array, 0, temp, 0, m_count);
  384. m_array = temp;
  385. }
  386. }
  387. /// <summary />
  388. public void AddRange(Int32Collection collection) {
  389. ++m_version;
  390. Capacity += collection.Count;
  391. System.Array.Copy(collection.m_array, 0, this.m_array, m_count, collection.m_count);
  392. m_count += collection.Count;
  393. }
  394. /// <summary />
  395. public void AddRange(System.Int32[] array) {
  396. ++m_version;
  397. Capacity += array.Length;
  398. System.Array.Copy(array, 0, this.m_array, m_count, array.Length);
  399. m_count += array.Length;
  400. }
  401. #endregion
  402. #region Private helper methods
  403. private void ValidateIndex(int index) {
  404. ValidateIndex(index,false);
  405. }
  406. private void ValidateIndex(int index, bool allowEqualEnd) {
  407. int max = (allowEqualEnd)?(m_count):(m_count-1);
  408. if (index < 0 || index > max)
  409. 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.");
  410. }
  411. private bool NeedsGrowth() {
  412. return (m_count >= Capacity);
  413. }
  414. private void Grow() {
  415. if (NeedsGrowth())
  416. Capacity = m_count*2;
  417. }
  418. private bool NeedsTrimming() {
  419. return (m_count <= Capacity/2);
  420. }
  421. private void Trim() {
  422. if (NeedsTrimming())
  423. Capacity = m_count;
  424. }
  425. #endregion
  426. #region System.Collections.ICollection implementation
  427. bool System.Collections.ICollection.IsSynchronized {
  428. get {
  429. return m_array.IsSynchronized; }
  430. }
  431. object System.Collections.ICollection.SyncRoot {
  432. get {
  433. return m_array.SyncRoot; }
  434. }
  435. void System.Collections.ICollection.CopyTo(System.Array array, int start) {
  436. this.CopyTo((System.Int32[])array, start);
  437. }
  438. #endregion
  439. #region System.Collections.IList implementation
  440. bool System.Collections.IList.IsFixedSize {
  441. get {
  442. return false; }
  443. }
  444. bool System.Collections.IList.IsReadOnly {
  445. get {
  446. return false; }
  447. }
  448. object System.Collections.IList.this[int index] {
  449. get { return (object)this[index]; }
  450. set { this[index] = (System.Int32)value; }
  451. }
  452. int System.Collections.IList.Add(object item) {
  453. return this.Add((System.Int32)item);
  454. }
  455. bool System.Collections.IList.Contains(object item) {
  456. return this.Contains((System.Int32)item);
  457. }
  458. int System.Collections.IList.IndexOf(object item) {
  459. return this.IndexOf((System.Int32)item);
  460. }
  461. void System.Collections.IList.Insert(int position, object item) {
  462. this.Insert(position, (System.Int32)item);
  463. }
  464. void System.Collections.IList.Remove(object item) {
  465. this.Remove((System.Int32)item);
  466. }
  467. #endregion
  468. #region System.Collections.IEnumerable and enumerator implementation
  469. /// <summary />
  470. public Enumerator GetEnumerator() {
  471. return new Enumerator(this);
  472. }
  473. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
  474. return GetEnumerator();
  475. }
  476. // Nested enumerator class
  477. /// <summary />
  478. public class Enumerator : System.Collections.IEnumerator {
  479. private Int32Collection m_collection;
  480. private int m_index;
  481. private int m_version;
  482. // Construction
  483. /// <summary />
  484. public Enumerator(Int32Collection tc) {
  485. m_collection = tc;
  486. m_index = -1;
  487. m_version = tc.m_version;
  488. }
  489. /// <summary />
  490. public System.Int32 Current {
  491. get {
  492. return m_collection[m_index]; }
  493. }
  494. /// <summary />
  495. public bool MoveNext() {
  496. if (m_version != m_collection.m_version)
  497. throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute.");
  498. ++m_index;
  499. return (m_index < m_collection.Count)?true:false;
  500. }
  501. /// <summary />
  502. public void Reset() {
  503. if (m_version != m_collection.m_version)
  504. throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute.");
  505. m_index = -1;
  506. }
  507. object System.Collections.IEnumerator.Current {
  508. get {
  509. return (object)(this.Current); }
  510. }
  511. }
  512. #endregion
  513. }
  514. #endregion
  515. }
  516. }