ExpiresBuckets.cs 18 KB

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