ExpiresBuckets.cs 17 KB

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