5 // Patrik Torstensson (Patrik.Torstensson@labs2.com)
6 // Daniel Cazzulino [DHC] (dcazzulino@users.sf.net)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Collections
;
32 using System
.Threading
;
34 namespace System
.Web
.Caching
{
36 /// Responsible for holding a cache entry in the linked list bucket.
38 internal struct ExpiresEntry
{
39 internal CacheEntry Entry
;
40 internal long TicksExpires
;
41 internal int _intNext
;
45 /// Holds cache entries that has a expiration in a bucket list.
47 internal class ExpiresBucket
{
48 private static int MIN_ENTRIES
= 4;
52 private int _intCount
;
55 private Cache _objManager
;
57 private ExpiresEntry
[] _arrEntries
;
59 private System
.Threading
.ReaderWriterLock _lock
= new System
.Threading
.ReaderWriterLock();
62 /// Keeps a list of indexes in the list which are available to place new items. [DHC]
64 private Int32Collection _freeidx
= new Int32Collection();
67 /// Constructs a new bucket.
69 /// <param name="bucket">Current bucket ID.</param>
70 /// <param name="objManager">Cache manager reponsible for the item(s) in the expires bucket.</param>
71 internal ExpiresBucket (byte bucket
, Cache objManager
) {
72 _objManager
= objManager
;
77 /// Initializes the expires bucket, creates a linked list of MIN_ENTRIES.
79 /// <param name="bucket">Bucket ID.</param>
80 private void Initialize (byte bucket
) {
85 _arrEntries
= new ExpiresEntry
[MIN_ENTRIES
];
86 _intSize
= MIN_ENTRIES
;
90 _arrEntries
[intPos
]._intNext
= intPos
+ 1;
91 _arrEntries
[intPos
].TicksExpires
= Cache
.NoAbsoluteExpiration
.Ticks
;
94 } while (intPos
< _intSize
);
96 _arrEntries
[_intSize
- 1]._intNext
= -1;
100 /// Expands the bucket linked array list.
102 private void Expand () {
103 _lock
.AcquireWriterLock(-1);
105 int oldsize
= _intSize
;
108 // Copy items to the new list.
109 ExpiresEntry
[] newlist
= new ExpiresEntry
[_intSize
];
110 _arrEntries
.CopyTo(newlist
, 0);
112 // Set last element to point to the next new empty element
114 newlist
[oldsize
- 1]._intNext
= oldsize
;
116 // Initialize positions for the rest of new elements.
117 for (int i
= oldsize
; i
< _intSize
; i
++) {
118 newlist
[i
]._intNext
= i
+ 1;
119 newlist
[i
].TicksExpires
= Cache
.NoAbsoluteExpiration
.Ticks
;
122 // Last item signals the expansion of the list.
123 newlist
[_intSize
- 1]._intNext
= -1;
125 // Replace the existing list.
126 _arrEntries
= newlist
;
129 _lock
.ReleaseWriterLock();
135 /// Adds a cache entry into the expires bucket.
137 /// <param name="objEntry">Cache Entry object to be added.</param>
138 internal void Add (CacheEntry objEntry
) {
139 _lock
.AcquireWriterLock(-1);
141 if (_intNext
== -1) {
142 if (_freeidx
.Count
== 0)
145 _intNext
= _freeidx
[0];
146 _freeidx
.Remove(_intNext
);
150 _arrEntries
[_intNext
].TicksExpires
= objEntry
.Expires
;
151 _arrEntries
[_intNext
].Entry
= objEntry
;
153 objEntry
.ExpiresBucket
= _byteID
;
154 objEntry
.ExpiresIndex
= _intNext
;
156 // If there are free indexes in the list, reuse them for the _next value.
157 if (_freeidx
.Count
!= 0) {
158 _intNext
= _freeidx
[0];
159 _freeidx
.Remove(_intNext
);
161 _intNext
= _arrEntries
[_intNext
]._intNext
;
166 _lock
.ReleaseWriterLock();
171 /// Removes a cache entry from the expires bucket.
173 /// <param name="objEntry">Cache entry to be removed.</param>
174 internal void Remove(CacheEntry objEntry
) {
175 // Check if this is our bucket
176 if (objEntry
.ExpiresBucket
!= _byteID
) return;
177 if (objEntry
.ExpiresIndex
== CacheEntry
.NoIndexInBucket
) return;
179 _lock
.AcquireWriterLock(-1);
181 if (_arrEntries
.Length
< objEntry
.ExpiresIndex
) return;
184 // Push the index as a free one.
185 _freeidx
.Add(objEntry
.ExpiresIndex
);
187 _arrEntries
[objEntry
.ExpiresIndex
].Entry
= null;
188 // Clear bucket-related values from the item.
189 objEntry
.ExpiresBucket
= CacheEntry
.NoBucketHash
;
190 objEntry
.ExpiresIndex
= CacheEntry
.NoIndexInBucket
;
193 //Releases both reader & writer locks
194 _lock
.ReleaseWriterLock();
199 /// Updates a cache entry in the expires bucket, this is called during a hit of an item if the
200 /// cache item has a sliding expiration. The function is responsible for updating the cache
203 /// <param name="objEntry">Cache entry to update.</param>
204 /// <param name="ticksExpires">New expiration value for the cache entry.</param>
205 internal void Update(CacheEntry objEntry
, long ticksExpires
) {
206 // Check if this is our bucket
207 if (objEntry
.ExpiresBucket
!= _byteID
) return;
208 if (objEntry
.ExpiresIndex
== CacheEntry
.NoIndexInBucket
) return;
210 _lock
.AcquireWriterLock(-1);
212 if (_arrEntries
.Length
< objEntry
.ExpiresIndex
) return;
214 // Proceed to update.
215 _arrEntries
[objEntry
.ExpiresIndex
].TicksExpires
= ticksExpires
;
216 _arrEntries
[objEntry
.ExpiresIndex
].Entry
.Expires
= ticksExpires
;
219 //Releases both read & write locks
220 _lock
.ReleaseWriterLock();
225 /// Flushes all cache entries that has expired and removes them from the cache manager.
227 internal void FlushExpiredItems() {
228 ExpiresEntry objEntry
;
229 ArrayList removeList
= null;
230 ArrayList flushList
= null;
234 ticksNow
= DateTime
.UtcNow
.Ticks
;
237 // Lookup all items that needs to be removed, this is done in a two part
238 // operation to minimize the locking time.
239 _lock
.AcquireReaderLock (-1);
242 objEntry
= _arrEntries
[intPos
];
243 if (null != objEntry
.Entry
&&
244 ((objEntry
.TicksExpires
< ticksNow
) || objEntry
.Entry
.ExpiresBucket
!= _byteID
))
246 if (null == removeList
)
247 removeList
= new ArrayList ();
249 removeList
.Add (objEntry
);
253 } while (intPos
< _intSize
);
256 _lock
.ReleaseReaderLock ();
259 if (null != removeList
) {
260 flushList
= new ArrayList (removeList
.Count
);
262 _lock
.AcquireWriterLock (-1);
264 foreach (ExpiresEntry entry
in removeList
) {
265 ExpiresEntry e
= entry
;
266 int id
= entry
.Entry
.ExpiresIndex
;
268 //push the index for reuse
271 if (entry
.Entry
.ExpiresBucket
== _byteID
) {
272 // add to our flush list
273 flushList
.Add (e
.Entry
);
275 // Remove from bucket
276 e
.Entry
.ExpiresBucket
= CacheEntry
.NoBucketHash
;
277 e
.Entry
.ExpiresIndex
= CacheEntry
.NoIndexInBucket
;
282 // Entries is structs, put it back
283 _arrEntries
[id
] = e
;
287 _lock
.ReleaseWriterLock ();
290 // We can call this without locks, it can takes time due to callbacks to user code
291 foreach (CacheEntry entry
in flushList
)
292 _objManager
.Remove (entry
.Key
, CacheItemRemovedReason
.Expired
);
300 /// Returns the current size of the expires bucket.
309 /// Returns number of items in the bucket.
317 #region Private Int32Collection
318 /* This file has been automatically generated by TextBox -- DO NOT EDIT! */
321 Int32Collection.Enumerator
323 These C# classes implement a strongly-typed collection of
326 The internal representation is an array of Int32, so the performance
327 characteristics will be more like a vector than a list, to use STL terminology.
329 The implementation is optimized for value-types, as it goes to great length to
330 avoid the overhead of boxing and unboxing. But it should also work well for
333 Mad props to Nick Wienholt <sheyenne@bigpond.com> for assisting me in
334 this research, and the testing, the benchmarking, and of course, the
337 Last but not least, a quick shout out to Kit George, for his generous
338 contribution to the dotnet mailing list -- a code-generator for
339 CollectionBase-derived classes:
340 http://discuss.develop.com/archives/wa.exe?A2=ind0107C&L=DOTNET&P=R35911
341 This was the original inspiration for the fine code you are now enjoying.
345 Other folks who've contributed:
346 Ethan Smith <ethan.smith@pobox.com> (minor perf. improvements)
347 Joel Mueller <jmueller@swiftk.com> (major perf. improvements)
348 Chris Sells <csells@sellsbrothers.com> (generative programming guru)
349 Patrice Lafond <plafond@hemisphere.bm> (a bug fix -- yikes!)
353 /// An optimized collection for holding <see cref="Int32"/> values.
355 [System
.Serializable
]
356 private class Int32Collection
: System
.Collections
.ICollection
, System
.Collections
.IList
, System
.Collections
.IEnumerable
{
357 #region Private vars & ctors
358 private const int DefaultMinimumCapacity
= 16;
360 private System
.Int32
[] m_array
= new System
.Int32
[DefaultMinimumCapacity
];
361 private int m_count
= 0;
362 private int m_version
= 0;
365 public Int32Collection() {
369 public Int32Collection(Int32Collection collection
) {
370 AddRange(collection
); }
373 public Int32Collection(System
.Int32
[] array
) {
377 #region Public members
386 public void CopyTo(System
.Int32
[] array
) {
387 this.CopyTo(array
, 0);
391 public void CopyTo(System
.Int32
[] array
, int start
) {
392 if (m_count
> array
.GetUpperBound(0)+1-start
)
393 throw new System
.ArgumentException("Destination array was not long enough.");
395 // for (int i=0; i < m_count; ++i) array[start+i] = m_array[i];
396 System
.Array
.Copy(m_array
, 0, array
, start
, m_count
);
400 public System
.Int32
this[int index
] {
402 ValidateIndex(index
); // throws
403 return m_array
[index
];
406 ValidateIndex(index
); // throws
409 m_array
[index
] = value;
414 public int Add(System
.Int32 item
) {
419 m_array
[m_count
] = item
;
425 public void Clear() {
427 m_array
= new System
.Int32
[DefaultMinimumCapacity
];
432 public bool Contains(System
.Int32 item
) {
433 return ((IndexOf(item
) == -1)?false:true);
437 public int IndexOf(System
.Int32 item
) {
438 for (int i
=0; i
< m_count
; ++i
)
439 if (m_array
[i
].Equals(item
))
445 public void Insert(int position
, System
.Int32 item
) {
446 ValidateIndex(position
,true); // throws
452 System
.Array
.Copy(m_array
, position
, m_array
, position
+1, m_count
-position
);
454 m_array
[position
] = item
;
459 public void Remove(System
.Int32 item
) {
460 int index
= IndexOf(item
);
462 throw new System
.ArgumentException("Cannot remove the specified item because it was not found in the specified Collection.");
468 public void RemoveAt(int index
) {
469 ValidateIndex(index
); // throws
473 System
.Array
.Copy(m_array
, index
+1, m_array
, index
, m_count
-index
);
479 // Public helpers (just to mimic some nice features of ArrayList)
482 public int Capacity
{
484 return m_array
.Length
; }
486 if (value < m_count
) value = m_count
;
487 if (value < DefaultMinimumCapacity
) value = DefaultMinimumCapacity
;
489 if (m_array
.Length
== value) return;
493 System
.Int32
[] temp
= new System
.Int32
[value];
494 System
.Array
.Copy(m_array
, 0, temp
, 0, m_count
);
500 public void AddRange(Int32Collection collection
) {
503 Capacity
+= collection
.Count
;
504 System
.Array
.Copy(collection
.m_array
, 0, this.m_array
, m_count
, collection
.m_count
);
505 m_count
+= collection
.Count
;
509 public void AddRange(System
.Int32
[] array
) {
512 Capacity
+= array
.Length
;
513 System
.Array
.Copy(array
, 0, this.m_array
, m_count
, array
.Length
);
514 m_count
+= array
.Length
;
518 #region Private helper methods
519 private void ValidateIndex(int index
) {
520 ValidateIndex(index
,false);
523 private void ValidateIndex(int index
, bool allowEqualEnd
) {
524 int max
= (allowEqualEnd
)?(m_count
):(m_count
-1);
525 if (index
< 0 || index
> max
)
526 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.");
529 private bool NeedsGrowth() {
530 return (m_count
>= Capacity
);
533 private void Grow() {
535 Capacity
= m_count
*2;
538 private bool NeedsTrimming() {
539 return (m_count
<= Capacity
/2);
542 private void Trim() {
548 #region System.Collections.ICollection implementation
549 bool System
.Collections
.ICollection
.IsSynchronized
{
551 return m_array
.IsSynchronized
; }
554 object System
.Collections
.ICollection
.SyncRoot
{
556 return m_array
.SyncRoot
; }
559 void System
.Collections
.ICollection
.CopyTo(System
.Array array
, int start
) {
560 this.CopyTo((System
.Int32
[])array
, start
);
564 #region System.Collections.IList implementation
565 bool System
.Collections
.IList
.IsFixedSize
{
570 bool System
.Collections
.IList
.IsReadOnly
{
575 object System
.Collections
.IList
.this[int index
] {
576 get { return (object)this[index]; }
577 set { this[index] = (System.Int32)value; }
580 int System
.Collections
.IList
.Add(object item
) {
581 return this.Add((System
.Int32
)item
);
584 bool System
.Collections
.IList
.Contains(object item
) {
585 return this.Contains((System
.Int32
)item
);
588 int System
.Collections
.IList
.IndexOf(object item
) {
589 return this.IndexOf((System
.Int32
)item
);
592 void System
.Collections
.IList
.Insert(int position
, object item
) {
593 this.Insert(position
, (System
.Int32
)item
);
596 void System
.Collections
.IList
.Remove(object item
) {
597 this.Remove((System
.Int32
)item
);
601 #region System.Collections.IEnumerable and enumerator implementation
603 public Enumerator
GetEnumerator() {
604 return new Enumerator(this);
607 System
.Collections
.IEnumerator System
.Collections
.IEnumerable
.GetEnumerator() {
608 return GetEnumerator();
611 // Nested enumerator class
613 public class Enumerator
: System
.Collections
.IEnumerator
{
614 private Int32Collection m_collection
;
616 private int m_version
;
621 public Enumerator(Int32Collection tc
) {
624 m_version
= tc
.m_version
;
628 public System
.Int32 Current
{
630 return m_collection
[m_index
]; }
634 public bool MoveNext() {
635 if (m_version
!= m_collection
.m_version
)
636 throw new System
.InvalidOperationException("Collection was modified; enumeration operation may not execute.");
639 return (m_index
< m_collection
.Count
)?true:false;
643 public void Reset() {
644 if (m_version
!= m_collection
.m_version
)
645 throw new System
.InvalidOperationException("Collection was modified; enumeration operation may not execute.");
650 object System
.Collections
.IEnumerator
.Current
{
652 return (object)(this.Current
); }