ExpiresBuckets.cs 19 KB

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