ExpiresBuckets.cs 19 KB

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