ExpiresBuckets.cs 17 KB

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