aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDahlia Trimble2009-06-02 22:42:47 +0000
committerDahlia Trimble2009-06-02 22:42:47 +0000
commitb38be1a7fdf71a7d3c6d6f761590827b1f45483c (patch)
tree2be409eb714dfd93d78e50d8b0def07b8e9758e1
parentThank you kindly, MattSetzer, for a patch that solves a Mantis: (diff)
downloadopensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.zip
opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.tar.gz
opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.tar.bz2
opensim-SC_OLD-b38be1a7fdf71a7d3c6d6f761590827b1f45483c.tar.xz
Thank you Imaze Rhiano for a patch that implements Cenome Memory Asset Cache (Mantis #3759)
See the files: bin/config-include/GridCommon.ini.example and bin/config-include/StandaloneCommon.ini.example to configure and enable this caching method.
-rw-r--r--CONTRIBUTORS.txt1
-rw-r--r--OpenSim/Framework/CnmMemoryCache.cs1829
-rw-r--r--OpenSim/Framework/CnmSynchronizedCache.cs746
-rw-r--r--OpenSim/Framework/ICnmCache.cs441
-rw-r--r--OpenSim/Framework/PrimeNumberHelper.cs114
-rw-r--r--OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs381
-rw-r--r--OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml1
-rw-r--r--bin/config-include/GridCommon.ini.example9
-rw-r--r--bin/config-include/StandaloneCommon.ini.example8
9 files changed, 3530 insertions, 0 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index e774812..6917c59 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -73,6 +73,7 @@ what it is today.
73* Ewe Loon 73* Ewe Loon
74* Fly-Man 74* Fly-Man
75* Flyte Xevious 75* Flyte Xevious
76* Imaze Rhiano
76* Intimidated 77* Intimidated
77* Jeremy Bongio (IBM) 78* Jeremy Bongio (IBM)
78* jhurliman 79* jhurliman
diff --git a/OpenSim/Framework/CnmMemoryCache.cs b/OpenSim/Framework/CnmMemoryCache.cs
new file mode 100644
index 0000000..05a71cf
--- /dev/null
+++ b/OpenSim/Framework/CnmMemoryCache.cs
@@ -0,0 +1,1829 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Diagnostics;
32
33namespace OpenSim.Framework
34{
35 /// <summary>
36 /// Cenome memory based cache to store key/value pairs (elements) limited time and/or limited size.
37 /// </summary>
38 /// <typeparam name="TKey">
39 /// The type of keys in the cache.
40 /// </typeparam>
41 /// <typeparam name="TValue">
42 /// The type of values in the dictionary.
43 /// </typeparam>
44 /// <remarks>
45 /// <para>
46 /// Cenome memory cache stores elements to hash table generations. When new element is being added to cache, and new size would exceed
47 /// maximal allowed size or maximal amount of allowed element count, then elements in oldest generation are deleted. Last access time
48 /// is also tracked in generation level - thus it is possible that some elements are staying in cache far beyond their expiration time.
49 /// If elements in older generations are accessed through <see cref="TryGetValue"/> method, they are moved to newest generation.
50 /// </para>
51 /// </remarks>
52 public class CnmMemoryCache<TKey, TValue> : ICnmCache<TKey, TValue>
53 {
54 /// <summary>
55 /// Default maximal count.
56 /// </summary>
57 /// <seealso cref="MaxCount"/>
58 public const int DefaultMaxCount = 4096;
59
60 /// <summary>
61 /// Default maximal size.
62 /// </summary>
63 /// <remarks>
64 /// <para>
65 /// 128MB = 128 * 1024^2 = 134 217 728 bytes.
66 /// </para>
67 /// </remarks>
68 /// <seealso cref="MaxSize"/>
69 public const long DefaultMaxSize = 134217728;
70
71 /// <summary>
72 /// How many operations between time checks.
73 /// </summary>
74 private const int DefaultOperationsBetweenTimeChecks = 40;
75
76 /// <summary>
77 /// Default expiration time.
78 /// </summary>
79 /// <remarks>
80 /// <para>
81 /// 30 minutes.
82 /// </para>
83 /// </remarks>
84 public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes( 30.0 );
85
86 /// <summary>
87 /// Minimal allowed expiration time.
88 /// </summary>
89 /// <remarks>
90 /// <para>
91 /// 5 minutes.
92 /// </para>
93 /// </remarks>
94 public static readonly TimeSpan MinExpirationTime = TimeSpan.FromSeconds( 10.0 );
95
96 /// <summary>
97 /// Comparer used to compare element keys.
98 /// </summary>
99 /// <remarks>
100 /// Comparer is initialized by constructor.
101 /// </remarks>
102 /// <seealso cref="CnmMemoryCache{TKey,TValue}"/>
103 public readonly IEqualityComparer<TKey> Comparer;
104
105 /// <summary>
106 /// Expiration time.
107 /// </summary>
108 private TimeSpan m_expirationTime = DefaultExpirationTime;
109
110 /// <summary>
111 /// Generation bucket count.
112 /// </summary>
113 private int m_generationBucketCount;
114
115 /// <summary>
116 /// Generation entry count.
117 /// </summary>
118 private int m_generationElementCount;
119
120 /// <summary>
121 /// Generation max size.
122 /// </summary>
123 private long m_generationMaxSize;
124
125 /// <summary>
126 /// Maximal allowed count of elements.
127 /// </summary>
128 private int m_maxCount;
129
130 /// <summary>
131 /// Maximal size.
132 /// </summary>
133 private long m_maxSize;
134
135 /// <summary>
136 /// New generation.
137 /// </summary>
138 private IGeneration m_newGeneration;
139
140 /// <summary>
141 /// Old generation.
142 /// </summary>
143 private IGeneration m_oldGeneration;
144
145 /// <summary>
146 /// Operations between time check.
147 /// </summary>
148 private int m_operationsBetweenTimeChecks = DefaultOperationsBetweenTimeChecks;
149
150 /// <summary>
151 /// Synchronization root object, should always be private and exists always
152 /// </summary>
153 private readonly object m_syncRoot = new object();
154
155 /// <summary>
156 /// Version of cache.
157 /// </summary>
158 /// <remarks>
159 /// <para>
160 /// Updated every time when cache has been changed (element removed, expired, added, replaced).
161 /// </para>
162 /// </remarks>
163 private int m_version;
164
165 /// <summary>
166 /// Initializes a new instance of the <see cref="CnmMemoryCache{TKey,TValue}"/> class.
167 /// </summary>
168 public CnmMemoryCache()
169 : this( DefaultMaxSize )
170 {
171 }
172
173 /// <summary>
174 /// Initializes a new instance of the <see cref="CnmMemoryCache{TKey,TValue}"/> class.
175 /// </summary>
176 /// <param name="maximalSize">
177 /// Maximal cache size.
178 /// </param>
179 public CnmMemoryCache( long maximalSize )
180 : this( maximalSize, DefaultMaxCount )
181 {
182 }
183
184 /// <summary>
185 /// Initializes a new instance of the <see cref="CnmMemoryCache{TKey,TValue}"/> class.
186 /// </summary>
187 /// <param name="maximalSize">
188 /// Maximal cache size.
189 /// </param>
190 /// <param name="maximalCount">
191 /// Maximal element count.
192 /// </param>
193 public CnmMemoryCache( long maximalSize, int maximalCount )
194 : this( maximalSize, maximalCount, DefaultExpirationTime )
195 {
196 }
197
198 /// <summary>
199 /// Initializes a new instance of the <see cref="CnmMemoryCache{TKey,TValue}"/> class.
200 /// </summary>
201 /// <param name="maximalSize">
202 /// Maximal cache size.
203 /// </param>
204 /// <param name="maximalCount">
205 /// Maximal element count.
206 /// </param>
207 /// <param name="expirationTime">
208 /// Elements expiration time.
209 /// </param>
210 public CnmMemoryCache( long maximalSize, int maximalCount, TimeSpan expirationTime )
211 : this( maximalSize, maximalCount, expirationTime, EqualityComparer<TKey>.Default )
212 {
213 }
214
215 /// <summary>
216 /// Initializes a new instance of the <see cref="CnmMemoryCache{TKey,TValue}"/> class.
217 /// </summary>
218 /// <param name="maximalSize">
219 /// Maximal cache size.
220 /// </param>
221 /// <param name="maximalCount">
222 /// Maximal element count.
223 /// </param>
224 /// <param name="expirationTime">
225 /// Elements expiration time.
226 /// </param>
227 /// <param name="comparer">
228 /// Comparer used for comparing elements.
229 /// </param>
230 /// <exception cref="ArgumentNullException">
231 /// <see cref="comparer"/>is <see langword="null"/> reference.
232 /// </exception>
233 public CnmMemoryCache(
234 long maximalSize,
235 int maximalCount,
236 TimeSpan expirationTime,
237 IEqualityComparer<TKey> comparer )
238 {
239 if( comparer == null )
240 throw new ArgumentNullException( "comparer" );
241
242 if( expirationTime < MinExpirationTime )
243 expirationTime = MinExpirationTime;
244 if( maximalCount < 8 )
245 maximalCount = 8;
246 if( maximalSize < 8 )
247 maximalSize = 8;
248 if( maximalCount > maximalSize )
249 maximalCount = (int) maximalSize;
250
251 Comparer = comparer;
252 m_expirationTime = expirationTime;
253 m_maxSize = maximalSize;
254 m_maxCount = maximalCount;
255
256 Initialize();
257 }
258
259 /// <summary>
260 /// Add element to new generation.
261 /// </summary>
262 /// <param name="bucketIndex">
263 /// The bucket index.
264 /// </param>
265 /// <param name="key">
266 /// The element's key.
267 /// </param>
268 /// <param name="value">
269 /// The element's value.
270 /// </param>
271 /// <param name="size">
272 /// The element's size.
273 /// </param>
274 protected virtual void AddToNewGeneration( int bucketIndex, TKey key, TValue value, long size )
275 {
276 // Add to newest generation
277 if( !m_newGeneration.Set( bucketIndex, key, value, size ) )
278 {
279 // Failed to add new generation
280 RecycleGenerations();
281 m_newGeneration.Set( bucketIndex, key, value, size );
282 }
283
284 m_version++;
285 }
286
287 /// <summary>
288 /// <para>
289 /// Get keys bucket index.
290 /// </para>
291 /// </summary>
292 /// <param name="key">
293 /// <para>
294 /// Key which bucket index is being retrieved.
295 /// </para>
296 /// </param>
297 /// <returns>
298 /// <para>
299 /// Bucket index.
300 /// </para>
301 /// </returns>
302 /// <remarks>
303 /// <para>
304 /// Method uses <see cref="Comparer"/> to calculate <see cref="key"/> hash code.
305 /// </para>
306 /// <para>
307 /// Bucket index is remainder when element key's hash value is divided by bucket count.
308 /// </para>
309 /// <para>
310 /// For example: key's hash is 72, bucket count is 5, element's bucket index is 72 % 5 = 2.
311 /// </para>
312 /// </remarks>
313 protected virtual int GetBucketIndex( TKey key )
314 {
315 return (Comparer.GetHashCode( key ) & 0x7FFFFFFF) % m_generationBucketCount;
316 }
317
318 /// <summary>
319 /// Purge generation from the cache.
320 /// </summary>
321 /// <param name="generation">
322 /// The generation that is purged.
323 /// </param>
324 protected virtual void PurgeGeneration( IGeneration generation )
325 {
326 generation.Clear();
327 m_version++;
328 }
329
330 /// <summary>
331 /// check expired.
332 /// </summary>
333 private void CheckExpired()
334 {
335 // Do this only one in every m_operationsBetweenTimeChecks
336 // Fetching time is using several millisecons - it is better not to do all time.
337 m_operationsBetweenTimeChecks--;
338 if( m_operationsBetweenTimeChecks <= 0 )
339 PurgeExpired();
340 }
341
342 /// <summary>
343 /// Initialize cache.
344 /// </summary>
345 private void Initialize()
346 {
347 m_version++;
348
349 m_generationMaxSize = MaxSize / 2;
350 MaxElementSize = MaxSize / 8;
351 m_generationElementCount = MaxCount / 2;
352
353 // Buckets need to be prime number to get better spread of hash values
354 m_generationBucketCount = PrimeNumberHelper.GetPrime( m_generationElementCount );
355
356 m_newGeneration = new HashGeneration( this );
357 m_oldGeneration = new HashGeneration( this );
358 m_oldGeneration.MakeOld();
359 }
360
361 /// <summary>
362 /// Recycle generations.
363 /// </summary>
364 private void RecycleGenerations()
365 {
366 // Rotate old generation to new generation, new generation to old generation
367 var temp = m_newGeneration;
368 m_newGeneration = m_oldGeneration;
369 m_newGeneration.Clear();
370 m_oldGeneration = temp;
371 m_oldGeneration.MakeOld();
372 }
373
374 #region Nested type: Enumerator
375
376 /// <summary>
377 /// Key and value pair enumerator.
378 /// </summary>
379 private class Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
380 {
381 /// <summary>
382 /// Current enumerator.
383 /// </summary>
384 private int m_currentEnumerator = -1;
385
386 /// <summary>
387 /// Enumerators to different generations.
388 /// </summary>
389 private readonly IEnumerator<KeyValuePair<TKey, TValue>>[] m_generationEnumerators =
390 new IEnumerator<KeyValuePair<TKey, TValue>>[2];
391
392 /// <summary>
393 /// Initializes a new instance of the <see cref="Enumerator"/> class.
394 /// </summary>
395 /// <param name="cache">
396 /// The cache.
397 /// </param>
398 public Enumerator( CnmMemoryCache<TKey, TValue> cache )
399 {
400 m_generationEnumerators[ 0 ] = cache.m_newGeneration.GetEnumerator();
401 m_generationEnumerators[ 1 ] = cache.m_oldGeneration.GetEnumerator();
402 }
403
404 #region IEnumerator<KeyValuePair<TKey,TValue>> Members
405
406 /// <summary>
407 /// Gets the element in the collection at the current position of the enumerator.
408 /// </summary>
409 /// <returns>
410 /// The element in the collection at the current position of the enumerator.
411 /// </returns>
412 /// <exception cref="InvalidOperationException">
413 /// The enumerator has reach end of collection or <see cref="MoveNext"/> is not called.
414 /// </exception>
415 public KeyValuePair<TKey, TValue> Current
416 {
417 get
418 {
419 if( m_currentEnumerator == -1 || m_currentEnumerator >= m_generationEnumerators.Length )
420 throw new InvalidOperationException();
421
422 return m_generationEnumerators[ m_currentEnumerator ].Current;
423 }
424 }
425
426 /// <summary>
427 /// Gets the current element in the collection.
428 /// </summary>
429 /// <returns>
430 /// The current element in the collection.
431 /// </returns>
432 /// <exception cref="T:System.InvalidOperationException">
433 /// The enumerator is positioned before the first element of the collection or after the last element.
434 /// </exception><filterpriority>2</filterpriority>
435 object IEnumerator.Current
436 {
437 get { return Current; }
438 }
439
440 /// <summary>
441 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
442 /// </summary>
443 /// <filterpriority>2</filterpriority>
444 public void Dispose()
445 {
446 }
447
448 /// <summary>
449 /// Advances the enumerator to the next element of the collection.
450 /// </summary>
451 /// <returns>
452 /// <see langword="true"/>if the enumerator was successfully advanced to the next element; <see langword="false"/> if the enumerator has passed the end of the collection.
453 /// </returns>
454 /// <exception cref="T:System.InvalidOperationException">
455 /// The collection was modified after the enumerator was created.
456 /// </exception>
457 /// <filterpriority>2</filterpriority>
458 public bool MoveNext()
459 {
460 if( m_currentEnumerator == -1 )
461 m_currentEnumerator = 0;
462
463 while( m_currentEnumerator < m_generationEnumerators.Length )
464 {
465 if( m_generationEnumerators[ m_currentEnumerator ].MoveNext() )
466 return true;
467
468 m_currentEnumerator++;
469 }
470
471 return false;
472 }
473
474 /// <summary>
475 /// Sets the enumerator to its initial position, which is before the first element in the collection.
476 /// </summary>
477 /// <exception cref="T:System.InvalidOperationException">
478 /// The collection was modified after the enumerator was created.
479 /// </exception>
480 /// <filterpriority>2</filterpriority>
481 public void Reset()
482 {
483 foreach( var enumerator in m_generationEnumerators )
484 {
485 enumerator.Reset();
486 }
487
488 m_currentEnumerator = -1;
489 }
490
491 #endregion
492 }
493
494 #endregion
495
496 #region Nested type: HashGeneration
497
498 /// <summary>
499 /// Hash generation class
500 /// </summary>
501 /// <remarks>
502 /// <para>
503 /// Current implementation is based to separated chaining with move-to-front heuristics. Hash generations have fixed
504 /// amount of buckets and it is never rehashed.
505 /// </para>
506 /// <para>
507 /// Read more about hash tables from <a href="http://en.wikipedia.org/wiki/Hash_table">Wiki article</a>.
508 /// </para>
509 /// </remarks>
510 /// <seealso href="http://en.wikipedia.org/wiki/Hash_table"/>
511 private class HashGeneration : IGeneration
512 {
513 /// <summary>
514 /// Index of first element's in element chain.
515 /// </summary>
516 /// <value>
517 /// -1 if there is no element in bucket; otherwise first element's index in the element chain.
518 /// </value>
519 /// <remarks>
520 /// Bucket index is remainder when element key's hash value is divided by bucket count.
521 /// For example: key's hash is 72, bucket count is 5, element's bucket index is 72 % 5 = 2.
522 /// </remarks>
523 private readonly int[] m_buckets;
524
525 /// <summary>
526 /// Cache object.
527 /// </summary>
528 private readonly CnmMemoryCache<TKey, TValue> m_cache;
529
530 /// <summary>
531 /// Generation's element array.
532 /// </summary>
533 /// <seealso cref="Element"/>
534 private readonly Element[] m_elements;
535
536 /// <summary>
537 /// Index to first free element.
538 /// </summary>
539 private int m_firstFreeElement;
540
541 /// <summary>
542 /// Free element count.
543 /// </summary>
544 /// <remarks>
545 /// When generation is cleared or constructed, this is NOT set to element count.
546 /// This is only tracking elements that are removed and are currently free.
547 /// </remarks>
548 private int m_freeCount;
549
550 /// <summary>
551 /// Is this generation "new generation".
552 /// </summary>
553 private bool m_newGeneration;
554
555 /// <summary>
556 /// Next unused entry.
557 /// </summary>
558 private int m_nextUnusedElement;
559
560 /// <summary>
561 /// Initializes a new instance of the <see cref="HashGeneration"/> class.
562 /// </summary>
563 /// <param name="cache">
564 /// The cache.
565 /// </param>
566 public HashGeneration( CnmMemoryCache<TKey, TValue> cache )
567 {
568 m_cache = cache;
569 m_elements = new Element[m_cache.m_generationElementCount];
570 m_buckets = new int[m_cache.m_generationBucketCount];
571 Clear();
572 }
573
574 /// <summary>
575 /// Find element's index
576 /// </summary>
577 /// <param name="bucketIndex">
578 /// The element's bucket index.
579 /// </param>
580 /// <param name="key">
581 /// The element's key.
582 /// </param>
583 /// <param name="moveToFront">
584 /// Move element to front of elements.
585 /// </param>
586 /// <param name="previousIndex">
587 /// The previous element's index.
588 /// </param>
589 /// <returns>
590 /// Element's index, if found from the generation; -1 otherwise (if element is not found the generation).
591 /// </returns>
592 private int FindElementIndex( int bucketIndex, TKey key, bool moveToFront, out int previousIndex )
593 {
594 previousIndex = -1;
595 var elementIndex = m_buckets[ bucketIndex ];
596 while( elementIndex >= 0 )
597 {
598 if( m_cache.Comparer.Equals( key, m_elements[ elementIndex ].Key ) )
599 {
600 // Found match
601 if( moveToFront && previousIndex >= 0 )
602 {
603 // Move entry to front
604 m_elements[ previousIndex ].Next = m_elements[ elementIndex ].Next;
605 m_elements[ elementIndex ].Next = m_buckets[ bucketIndex ];
606 m_buckets[ bucketIndex ] = elementIndex;
607 previousIndex = 0;
608 }
609
610 return elementIndex;
611 }
612
613 previousIndex = elementIndex;
614 elementIndex = m_elements[ elementIndex ].Next;
615 }
616
617 return -1;
618 }
619
620 /// <summary>
621 /// Remove element front the generation.
622 /// </summary>
623 /// <param name="bucketIndex">
624 /// The bucket index.
625 /// </param>
626 /// <param name="entryIndex">
627 /// The element index.
628 /// </param>
629 /// <param name="previousIndex">
630 /// The element's previous index.
631 /// </param>
632 private void RemoveElement( int bucketIndex, int entryIndex, int previousIndex )
633 {
634 if( previousIndex >= 0 )
635 m_elements[ previousIndex ].Next = m_elements[ entryIndex ].Next;
636 else
637 m_buckets[ bucketIndex ] = m_elements[ entryIndex ].Next;
638
639 Size -= m_elements[ entryIndex ].Size;
640 m_elements[ entryIndex ].Value = default(TValue);
641 m_elements[ entryIndex ].Key = default(TKey);
642
643 // Add element to free elements list
644 m_elements[ entryIndex ].Next = m_firstFreeElement;
645 m_firstFreeElement = entryIndex;
646 m_freeCount++;
647 }
648
649 #region Nested type: Element
650
651 /// <summary>
652 /// Element that stores key, next element in chain, size and value.
653 /// </summary>
654 private struct Element
655 {
656 /// <summary>
657 /// Element's key.
658 /// </summary>
659 public TKey Key;
660
661 /// <summary>
662 /// Next element in chain.
663 /// </summary>
664 /// <remarks>
665 /// When element have value (something is stored to it), this is index of
666 /// next element with same bucket index. When element is free, this
667 /// is index of next element in free element's list.
668 /// </remarks>
669 public int Next;
670
671 /// <summary>
672 /// Size of element.
673 /// </summary>
674 /// <value>
675 /// 0 if element is free; otherwise larger than 0.
676 /// </value>
677 public long Size;
678
679 /// <summary>
680 /// Element's value.
681 /// </summary>
682 /// <remarks>
683 /// It is possible that this value is <see langword="null"/> even when element
684 /// have value - element's value is then <see langword="null"/> reference.
685 /// </remarks>
686 public TValue Value;
687
688 /// <summary>
689 /// Gets a value indicating whether element is free or have value.
690 /// </summary>
691 /// <value>
692 /// <see langword="true"/> when element is free; otherwise <see langword="false"/>.
693 /// </value>
694 public bool IsFree
695 {
696 get { return Size == 0; }
697 }
698 }
699
700 #endregion
701
702 #region Nested type: Enumerator
703
704 /// <summary>
705 /// Key value pair enumerator for <see cref="HashGeneration"/> object.
706 /// </summary>
707 private class Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
708 {
709 /// <summary>
710 /// Current element.
711 /// </summary>
712 private KeyValuePair<TKey, TValue> m_current;
713
714 /// <summary>
715 /// Current index.
716 /// </summary>
717 private int m_currentIndex;
718
719 /// <summary>
720 /// Generation that is being enumerated.
721 /// </summary>
722 private readonly HashGeneration m_generation;
723
724 /// <summary>
725 /// Cache version.
726 /// </summary>
727 /// <remarks>
728 /// When cache is change, version number is changed.
729 /// </remarks>
730 /// <seealso cref="CnmMemoryCache{TKey,TValue}.m_version"/>
731 private readonly int m_version;
732
733 /// <summary>
734 /// Initializes a new instance of the <see cref="Enumerator"/> class.
735 /// </summary>
736 /// <param name="generation">
737 /// The generation.
738 /// </param>
739 public Enumerator( HashGeneration generation )
740 {
741 m_generation = generation;
742 m_version = m_generation.m_cache.m_version;
743 }
744
745 #region IEnumerator<KeyValuePair<TKey,TValue>> Members
746
747 /// <summary>
748 /// Gets the element in the collection at the current position of the enumerator.
749 /// </summary>
750 /// <returns>
751 /// The element in the collection at the current position of the enumerator.
752 /// </returns>
753 /// <exception cref="InvalidOperationException">
754 /// The enumerator has reach end of collection or <see cref="MoveNext"/> is not called.
755 /// </exception>
756 public KeyValuePair<TKey, TValue> Current
757 {
758 get
759 {
760 if( m_currentIndex == 0 || m_currentIndex >= m_generation.Count )
761 throw new InvalidOperationException();
762
763 return m_current;
764 }
765 }
766
767 /// <summary>
768 /// Gets the current element in the collection.
769 /// </summary>
770 /// <returns>
771 /// The current element in the collection.
772 /// </returns>
773 /// <exception cref="InvalidOperationException">
774 /// The enumerator is positioned before the first element of the collection or after the last element.
775 /// </exception><filterpriority>2</filterpriority>
776 object IEnumerator.Current
777 {
778 get { return Current; }
779 }
780
781 /// <summary>
782 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
783 /// </summary>
784 /// <filterpriority>2</filterpriority>
785 public void Dispose()
786 {
787 }
788
789 /// <summary>
790 /// Advances the enumerator to the next element of the collection.
791 /// </summary>
792 /// <returns>
793 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
794 /// </returns>
795 /// <exception cref="InvalidOperationException">
796 /// The collection was modified after the enumerator was created.
797 /// </exception>
798 public bool MoveNext()
799 {
800 if( m_version != m_generation.m_cache.m_version )
801 throw new InvalidOperationException();
802
803 while( m_currentIndex < m_generation.Count )
804 {
805 if( m_generation.m_elements[ m_currentIndex ].IsFree )
806 {
807 m_currentIndex++;
808 continue;
809 }
810
811 m_current = new KeyValuePair<TKey, TValue>( m_generation.m_elements[ m_currentIndex ].Key,
812 m_generation.m_elements[ m_currentIndex ].Value );
813 m_currentIndex++;
814 return true;
815 }
816
817 m_current = new KeyValuePair<TKey, TValue>();
818 return false;
819 }
820
821 /// <summary>
822 /// Sets the enumerator to its initial position, which is before the first element in the collection.
823 /// </summary>
824 /// <exception cref="InvalidOperationException">
825 /// The collection was modified after the enumerator was created.
826 /// </exception>
827 /// <filterpriority>2</filterpriority>
828 public void Reset()
829 {
830 if( m_version != m_generation.m_cache.m_version )
831 throw new InvalidOperationException();
832
833 m_currentIndex = 0;
834 }
835
836 #endregion
837 }
838
839 #endregion
840
841 #region IGeneration Members
842
843 /// <summary>
844 /// Gets or sets a value indicating whether generation was accessed since last time check.
845 /// </summary>
846 public bool AccessedSinceLastTimeCheck { get; set; }
847
848 /// <summary>
849 /// Gets element count in generation.
850 /// </summary>
851 public int Count
852 {
853 get { return m_nextUnusedElement - m_freeCount; }
854 }
855
856 /// <summary>
857 /// Gets or sets generation's expiration time.
858 /// </summary>
859 public DateTime ExpirationTime { get; set; }
860
861 /// <summary>
862 /// Gets or sets size of data stored to generation.
863 /// </summary>
864 public long Size { get; private set; }
865
866 /// <summary>
867 /// Clear all elements from the generation and make generation new again.
868 /// </summary>
869 /// <remarks>
870 /// When generation is new, it is allowed to add new elements to it and
871 /// <see cref="IGeneration.TryGetValue"/>doesn't remove elements from it.
872 /// </remarks>
873 /// <seealso cref="IGeneration.MakeOld"/>
874 public void Clear()
875 {
876 for( var i = m_buckets.Length - 1 ; i >= 0 ; i-- )
877 {
878 m_buckets[ i ] = -1;
879 }
880
881 Array.Clear( m_elements, 0, m_elements.Length );
882 Size = 0;
883 m_firstFreeElement = -1;
884 m_freeCount = 0;
885 m_nextUnusedElement = 0;
886 m_newGeneration = true;
887 ExpirationTime = DateTime.MaxValue;
888 }
889
890 /// <summary>
891 /// Determines whether the <see cref="IGeneration"/> contains an element with the specific key.
892 /// </summary>
893 /// <param name="bucketIndex">
894 /// The bucket index for the <see cref="key"/> to locate in <see cref="IGeneration"/>.
895 /// </param>
896 /// <param name="key">
897 /// The key to locate in the <see cref="IGeneration"/>.
898 /// </param>
899 /// <returns>
900 /// <see langword="true"/>if the <see cref="IGeneration"/> contains an element with the <see cref="key"/>;
901 /// otherwise <see langword="false"/>.
902 /// </returns>
903 public bool Contains( int bucketIndex, TKey key )
904 {
905 int previousIndex;
906 if( FindElementIndex( bucketIndex, key, true, out previousIndex ) == -1 )
907 return false;
908
909 AccessedSinceLastTimeCheck = true;
910 return true;
911 }
912
913 /// <summary>
914 /// Returns an enumerator that iterates through the elements stored <see cref="HashGeneration"/>.
915 /// </summary>
916 /// <returns>
917 /// A <see cref="IEnumerator"/> that can be used to iterate through the <see cref="HashGeneration"/>.
918 /// </returns>
919 /// <filterpriority>1</filterpriority>
920 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
921 {
922 return new Enumerator( this );
923 }
924
925 /// <summary>
926 /// Make from generation old generation.
927 /// </summary>
928 /// <remarks>
929 /// When generation is old, <see cref="IGeneration.TryGetValue"/> hit removes element from the generation.
930 /// </remarks>
931 /// <seealso cref="IGeneration.Clear"/>
932 public void MakeOld()
933 {
934 m_newGeneration = false;
935 }
936
937 /// <summary>
938 /// Remove element associated with the key from the generation.
939 /// </summary>
940 /// <param name="bucketIndex">
941 /// The element's bucket index.
942 /// </param>
943 /// <param name="key">
944 /// The element's key.
945 /// </param>
946 /// <returns>
947 /// <see langword="true"/>, if remove was successful; otherwise <see langword="false"/>.
948 /// </returns>
949 public bool Remove( int bucketIndex, TKey key )
950 {
951 int previousIndex;
952 var entryIndex = FindElementIndex( bucketIndex, key, false, out previousIndex );
953 if( entryIndex != -1 )
954 {
955 RemoveElement( bucketIndex, entryIndex, previousIndex );
956 AccessedSinceLastTimeCheck = true;
957 return true;
958 }
959
960 return false;
961 }
962
963 /// <summary>
964 /// Set or add element to generation.
965 /// </summary>
966 /// <param name="bucketIndex">
967 /// The element's bucket index.
968 /// </param>
969 /// <param name="key">
970 /// The element's key.
971 /// </param>
972 /// <param name="value">
973 /// The element's value.
974 /// </param>
975 /// <param name="size">
976 /// The element's size.
977 /// </param>
978 /// <returns>
979 /// <see langword="true"/>, if setting or adding was successful; otherwise <see langword="false"/>.
980 /// </returns>
981 /// <remarks>
982 /// <para>
983 /// If element was already existing in generation and new element size fits to collection limits,
984 /// then it's value is replaced with new one and size information is updated. If element didn't
985 /// exists in generation before, then generation must have empty space for a new element and
986 /// size must fit generation's limits, before element is added to generation.
987 /// </para>
988 /// </remarks>
989 public bool Set( int bucketIndex, TKey key, TValue value, long size )
990 {
991 Debug.Assert( m_newGeneration, "It is possible to insert new elements only to newest generation." );
992 Debug.Assert( size > 0, "New element size should be more than 0." );
993
994 int previousIndex;
995 var elementIndex = FindElementIndex( bucketIndex, key, true, out previousIndex );
996 if( elementIndex == -1 )
997 {
998 // New key
999 if( Size + size > m_cache.m_generationMaxSize ||
1000 (m_nextUnusedElement == m_cache.m_generationElementCount && m_freeCount == 0) )
1001 {
1002 // Generation is full
1003 return false;
1004 }
1005
1006 // Increase size of generation
1007 Size += size;
1008
1009 // Get first free entry and update free entry list
1010 if( m_firstFreeElement != -1 )
1011 {
1012 // There was entry that was removed
1013 elementIndex = m_firstFreeElement;
1014 m_firstFreeElement = m_elements[ elementIndex ].Next;
1015 m_freeCount--;
1016 }
1017 else
1018 {
1019 // No entries removed so far - just take a last one
1020 elementIndex = m_nextUnusedElement;
1021 m_nextUnusedElement++;
1022 }
1023
1024 Debug.Assert( m_elements[ elementIndex ].IsFree, "Allocated element is not free." );
1025
1026 // Move new entry to front
1027 m_elements[ elementIndex ].Next = m_buckets[ bucketIndex ];
1028 m_buckets[ bucketIndex ] = elementIndex;
1029
1030 // Set key and update count
1031 m_elements[ elementIndex ].Key = key;
1032 }
1033 else
1034 {
1035 // Existing key
1036 if( Size - m_elements[ elementIndex ].Size + size > m_cache.m_generationMaxSize )
1037 {
1038 // Generation is full
1039 // Remove existing element, because generation is going to be recycled to
1040 // old generation and element is stored to new generation
1041 RemoveElement( bucketIndex, elementIndex, previousIndex );
1042 return false;
1043 }
1044
1045 // Update generation's size
1046 Size = Size - m_elements[ elementIndex ].Size + size;
1047 }
1048
1049 // Finally set value and size
1050 m_elements[ elementIndex ].Value = value;
1051 m_elements[ elementIndex ].Size = size;
1052
1053 // Success - key was inserterted to generation
1054 AccessedSinceLastTimeCheck = true;
1055 return true;
1056 }
1057
1058 /// <summary>
1059 /// Try to get element associated with key.
1060 /// </summary>
1061 /// <param name="bucketIndex">
1062 /// The element's bucket index.
1063 /// </param>
1064 /// <param name="key">
1065 /// The element's key.
1066 /// </param>
1067 /// <param name="value">
1068 /// The element's value.
1069 /// </param>
1070 /// <param name="size">
1071 /// The element's size.
1072 /// </param>
1073 /// <returns>
1074 /// <see langword="true"/>, if element was successful retrieved; otherwise <see langword="false"/>.
1075 /// </returns>
1076 /// <remarks>
1077 /// <para>
1078 /// If element is not found from generation then <paramref name="value"/> and <paramref name="size"/>
1079 /// are set to default value (default(TValue) and 0).
1080 /// </para>
1081 /// </remarks>
1082 public bool TryGetValue( int bucketIndex, TKey key, out TValue value, out long size )
1083 {
1084 // Find entry index,
1085 int previousIndex;
1086 var elementIndex = FindElementIndex( bucketIndex, key, m_newGeneration, out previousIndex );
1087 if( elementIndex == -1 )
1088 {
1089 value = default(TValue);
1090 size = 0;
1091 return false;
1092 }
1093
1094 value = m_elements[ elementIndex ].Value;
1095 size = m_elements[ elementIndex ].Size;
1096
1097 if( !m_newGeneration )
1098 {
1099 // Old generation - remove element, because it is moved to new generation
1100 RemoveElement( bucketIndex, elementIndex, previousIndex );
1101 }
1102
1103 AccessedSinceLastTimeCheck = true;
1104 return true;
1105 }
1106
1107 /// <summary>
1108 /// Returns an enumerator that iterates through a collection.
1109 /// </summary>
1110 /// <returns>
1111 /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
1112 /// </returns>
1113 /// <filterpriority>2</filterpriority>
1114 IEnumerator IEnumerable.GetEnumerator()
1115 {
1116 return GetEnumerator();
1117 }
1118
1119 #endregion
1120 }
1121
1122 #endregion
1123
1124 #region Nested type: IGeneration
1125
1126 /// <summary>
1127 /// Cache element generation interface
1128 /// </summary>
1129 /// <remarks>
1130 /// <para>
1131 /// Generation can hold limited count of elements and limited size of data.
1132 /// </para>
1133 /// <para>
1134 /// There are two kind generations: "new generation" and "old generation(s)". All new elements
1135 /// are added to "new generation".
1136 /// </para>
1137 /// </remarks>
1138 protected interface IGeneration : IEnumerable<KeyValuePair<TKey, TValue>>
1139 {
1140 /// <summary>
1141 /// Gets or sets a value indicating whether generation was accessed since last time check.
1142 /// </summary>
1143 bool AccessedSinceLastTimeCheck { get; set; }
1144
1145 /// <summary>
1146 /// Gets element count in generation.
1147 /// </summary>
1148 int Count { get; }
1149
1150 /// <summary>
1151 /// Gets or sets generation's expiration time.
1152 /// </summary>
1153 DateTime ExpirationTime { get; set; }
1154
1155 /// <summary>
1156 /// Gets size of data stored to generation.
1157 /// </summary>
1158 long Size { get; }
1159
1160 /// <summary>
1161 /// Clear all elements from the generation and make generation new again.
1162 /// </summary>
1163 /// <remarks>
1164 /// When generation is new, it is allowed to add new elements to it and
1165 /// <see cref="TryGetValue"/>doesn't remove elements from it.
1166 /// </remarks>
1167 /// <seealso cref="MakeOld"/>
1168 void Clear();
1169
1170 /// <summary>
1171 /// Determines whether the <see cref="IGeneration"/> contains an element with the specific key.
1172 /// </summary>
1173 /// <param name="bucketIndex">
1174 /// The bucket index for the <see cref="key"/> to locate in <see cref="IGeneration"/>.
1175 /// </param>
1176 /// <param name="key">
1177 /// The key to locate in the <see cref="IGeneration"/>.
1178 /// </param>
1179 /// <returns>
1180 /// <see langword="true"/>if the <see cref="IGeneration"/> contains an element with the <see cref="key"/>;
1181 /// otherwise <see langword="false"/>.
1182 /// </returns>
1183 bool Contains( int bucketIndex, TKey key );
1184
1185 /// <summary>
1186 /// Make from generation old generation.
1187 /// </summary>
1188 /// <remarks>
1189 /// When generation is old, <see cref="TryGetValue"/> hit removes element from the generation.
1190 /// </remarks>
1191 /// <seealso cref="Clear"/>
1192 void MakeOld();
1193
1194 /// <summary>
1195 /// Remove element associated with the key from the generation.
1196 /// </summary>
1197 /// <param name="bucketIndex">
1198 /// The element's bucket index.
1199 /// </param>
1200 /// <param name="key">
1201 /// The element's key.
1202 /// </param>
1203 /// <returns>
1204 /// <see langword="true"/>, if remove was successful; otherwise <see langword="false"/>.
1205 /// </returns>
1206 bool Remove( int bucketIndex, TKey key );
1207
1208 /// <summary>
1209 /// Set or add element to generation.
1210 /// </summary>
1211 /// <param name="bucketIndex">
1212 /// The element's bucket index.
1213 /// </param>
1214 /// <param name="key">
1215 /// The element's key.
1216 /// </param>
1217 /// <param name="value">
1218 /// The element's value.
1219 /// </param>
1220 /// <param name="size">
1221 /// The element's size.
1222 /// </param>
1223 /// <returns>
1224 /// <see langword="true"/>, if setting or adding was successful; otherwise <see langword="false"/>.
1225 /// </returns>
1226 /// <remarks>
1227 /// <para>
1228 /// If element was already existing in generation and new element size fits to collection limits,
1229 /// then it's value is replaced with new one and size information is updated. If element didn't
1230 /// exists in generation before, then generation must have empty space for a new element and
1231 /// size must fit generation's limits, before element is added to generation.
1232 /// </para>
1233 /// </remarks>
1234 bool Set( int bucketIndex, TKey key, TValue value, long size );
1235
1236 /// <summary>
1237 /// Try to get element associated with key.
1238 /// </summary>
1239 /// <param name="bucketIndex">
1240 /// The element's bucket index.
1241 /// </param>
1242 /// <param name="key">
1243 /// The element's key.
1244 /// </param>
1245 /// <param name="value">
1246 /// The element's value.
1247 /// </param>
1248 /// <param name="size">
1249 /// The element's size.
1250 /// </param>
1251 /// <returns>
1252 /// <see langword="true"/>, if element was successful retrieved; otherwise <see langword="false"/>.
1253 /// </returns>
1254 /// <remarks>
1255 /// <para>
1256 /// If element is not found from generation then <paramref name="value"/> and <paramref name="size"/>
1257 /// are set to default value (default(TValue) and 0).
1258 /// </para>
1259 /// </remarks>
1260 bool TryGetValue( int bucketIndex, TKey key, out TValue value, out long size );
1261 }
1262
1263 #endregion
1264
1265 #region ICnmCache<TKey,TValue> Members
1266
1267 /// <summary>
1268 /// Gets current count of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
1269 /// </summary>
1270 /// <remarks>
1271 /// <para>
1272 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
1273 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
1274 /// </para>
1275 /// </remarks>
1276 /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
1277 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
1278 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1279 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
1280 public int Count
1281 {
1282 get { return m_newGeneration.Count + m_oldGeneration.Count; }
1283 }
1284
1285 /// <summary>
1286 /// Gets or sets elements expiration time.
1287 /// </summary>
1288 /// <value>
1289 /// Elements expiration time.
1290 /// </value>
1291 /// <remarks>
1292 /// <para>
1293 /// When element has been stored in <see cref="ICnmCache{TKey,TValue}"/> longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
1294 /// and it is not accessed through <see cref="ICnmCache{TKey,TValue}.TryGetValue"/> method or element's value is
1295 /// not replaced by <see cref="ICnmCache{TKey,TValue}.Set"/> method, then it is automatically removed from the
1296 /// <see cref="ICnmCache{TKey,TValue}"/>.
1297 /// </para>
1298 /// <para>
1299 /// It is possible that <see cref="ICnmCache{TKey,TValue}"/> implementation removes element before it's expiration time,
1300 /// because total size or count of elements stored to cache is larger than <see cref="ICnmCache{TKey,TValue}.MaxSize"/> or <see cref="ICnmCache{TKey,TValue}.MaxCount"/>.
1301 /// </para>
1302 /// <para>
1303 /// It is also possible that element stays in cache longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>.
1304 /// </para>
1305 /// <para>
1306 /// Calling <see cref="ICnmCache{TKey,TValue}.PurgeExpired"/> try to remove all elements that are expired.
1307 /// </para>
1308 /// <para>
1309 /// To disable time limit in cache, set <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> to <see cref="DateTime.MaxValue"/>.
1310 /// </para>
1311 /// </remarks>
1312 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
1313 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
1314 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1315 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1316 /// <seealso cref="ICnmCache{TKey,TValue}.Count"/>
1317 /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
1318 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
1319 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
1320 public TimeSpan ExpirationTime
1321 {
1322 get { return m_expirationTime; }
1323
1324 set
1325 {
1326 if( value < MinExpirationTime )
1327 value = MinExpirationTime;
1328
1329 if( m_expirationTime == value )
1330 return;
1331
1332 var now = DateTime.Now;
1333 m_newGeneration.ExpirationTime = (m_newGeneration.ExpirationTime - m_expirationTime) + value;
1334 m_oldGeneration.ExpirationTime = (m_oldGeneration.ExpirationTime - m_expirationTime) + value;
1335 m_expirationTime = value;
1336
1337 PurgeExpired();
1338 }
1339 }
1340
1341 /// <summary>
1342 /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting count of elements.
1343 /// </summary>
1344 /// <value>
1345 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> count of elements is limited;
1346 /// otherwise, <see langword="false"/>.
1347 /// </value>
1348 /// <remarks>
1349 /// <para>
1350 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
1351 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
1352 /// </para>
1353 /// </remarks>
1354 /// <seealso cref="ICnmCache{TKey,TValue}.Count"/>
1355 /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
1356 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1357 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
1358 public bool IsCountLimited
1359 {
1360 get { return true; }
1361 }
1362
1363 /// <summary>
1364 /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting size of elements.
1365 /// </summary>
1366 /// <value>
1367 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> total size of elements is limited;
1368 /// otherwise, <see langword="false"/>.
1369 /// </value>
1370 /// <remarks>
1371 /// <para>
1372 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
1373 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
1374 /// </para>
1375 /// </remarks>
1376 /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
1377 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
1378 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
1379 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
1380 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
1381 public bool IsSizeLimited
1382 {
1383 get { return true; }
1384 }
1385
1386 /// <summary>
1387 /// Gets a value indicating whether or not access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe).
1388 /// </summary>
1389 /// <value>
1390 /// <see langword="true"/> if access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe);
1391 /// otherwise, <see langword="false"/>.
1392 /// </value>
1393 /// <remarks>
1394 /// <para>
1395 /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/> object, use
1396 /// <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> in <see cref="CnmSynchronizedCache{TKey,TValue}"/> class
1397 /// to retrieve synchronized wrapper for <see cref="ICnmCache{TKey,TValue}"/> object.
1398 /// </para>
1399 /// </remarks>
1400 /// <seealso cref="ICnmCache{TKey,TValue}.SyncRoot"/>
1401 /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
1402 public bool IsSynchronized
1403 {
1404 get { return false; }
1405 }
1406
1407 /// <summary>
1408 /// Gets a value indicating whether elements stored to <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time.
1409 /// </summary>
1410 /// <value>
1411 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> has a fixed total size of elements;
1412 /// otherwise, <see langword="false"/>.
1413 /// </value>
1414 /// <remarks>
1415 /// If <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time and element is not accessed through <see cref="ICnmCache{TKey,TValue}.Set"/>
1416 /// or <see cref="ICnmCache{TKey,TValue}.TryGetValue"/> methods in <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> , then element is automatically removed from
1417 /// the cache. Depending on implementation of the <see cref="ICnmCache{TKey,TValue}"/>, some of the elements may
1418 /// stay longer in cache.
1419 /// </remarks>
1420 /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
1421 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1422 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
1423 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1424 public bool IsTimeLimited
1425 {
1426 get { return ExpirationTime != TimeSpan.MaxValue; }
1427 }
1428
1429 /// <summary>
1430 /// Gets or sets maximal allowed count of elements that can be stored to <see cref="ICnmCache{TKey,TValue}"/>.
1431 /// </summary>
1432 /// <value>
1433 /// <see cref="int.MaxValue"/>, if <see cref="ICnmCache{TKey,TValue}"/> is not limited by count of elements;
1434 /// otherwise maximal allowed count of elements.
1435 /// </value>
1436 /// <remarks>
1437 /// <para>
1438 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
1439 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
1440 /// </para>
1441 /// </remarks>
1442 public int MaxCount
1443 {
1444 get { return m_maxCount; }
1445
1446 set
1447 {
1448 if( value < 8 )
1449 value = 8;
1450 if( m_maxCount == value )
1451 return;
1452
1453 m_maxCount = value;
1454 Initialize();
1455 }
1456 }
1457
1458 /// <summary>
1459 /// <para>Gets maximal allowed element size.</para>
1460 /// </summary>
1461 /// <value>
1462 /// Maximal allowed element size.
1463 /// </value>
1464 /// <remarks>
1465 /// <para>
1466 /// If element's size is larger than <see cref="ICnmCache{TKey,TValue}.MaxElementSize"/>, then element is
1467 /// not added to the <see cref="ICnmCache{TKey,TValue}"/>.
1468 /// </para>
1469 /// </remarks>
1470 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
1471 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1472 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
1473 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
1474 public long MaxElementSize { get; private set; }
1475
1476 /// <summary>
1477 /// Gets or sets maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
1478 /// </summary>
1479 /// <value>
1480 /// Maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
1481 /// </value>
1482 /// <remarks>
1483 /// <para>
1484 /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure.
1485 /// </para>
1486 /// <para>
1487 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
1488 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
1489 /// </para>
1490 /// </remarks>
1491 /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
1492 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1493 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
1494 public long MaxSize
1495 {
1496 get { return m_maxSize; }
1497
1498 set
1499 {
1500 if( value < 8 )
1501 value = 8;
1502 if( m_maxSize == value )
1503 return;
1504
1505 m_maxSize = value;
1506 Initialize();
1507 }
1508 }
1509
1510 /// <summary>
1511 /// Gets total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
1512 /// </summary>
1513 /// <value>
1514 /// Total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
1515 /// </value>
1516 /// <remarks>
1517 /// <para>
1518 /// Normally bytes, but can be any suitable unit of measure.
1519 /// </para>
1520 /// <para>
1521 /// Element's size is given when element is added or replaced by <see cref="ICnmCache{TKey,TValue}.Set"/> method.
1522 /// </para>
1523 /// <para>
1524 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
1525 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
1526 /// </para>
1527 /// </remarks>
1528 /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
1529 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1530 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
1531 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
1532 /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
1533 public long Size
1534 {
1535 get { return m_newGeneration.Size + m_oldGeneration.Size; }
1536 }
1537
1538 /// <summary>
1539 /// Gets an object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
1540 /// </summary>
1541 /// <value>
1542 /// An object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
1543 /// </value>
1544 /// <remarks>
1545 /// <para>
1546 /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/>, use <see cref="CnmSynchronizedCache{TKey,TValue}"/>
1547 /// method <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> to retrieve synchronized wrapper interface to
1548 /// <see cref="ICnmCache{TKey,TValue}"/>.
1549 /// </para>
1550 /// </remarks>
1551 /// <seealso cref="ICnmCache{TKey,TValue}.IsSynchronized"/>
1552 /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
1553 public object SyncRoot
1554 {
1555 get { return m_syncRoot; }
1556 }
1557
1558 /// <summary>
1559 /// Removes all elements from the <see cref="ICnmCache{TKey,TValue}"/>.
1560 /// </summary>
1561 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
1562 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
1563 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
1564 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
1565 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1566 public void Clear()
1567 {
1568 m_newGeneration.Clear();
1569 m_oldGeneration.Clear();
1570 m_oldGeneration.MakeOld();
1571 m_version++;
1572 }
1573
1574 /// <summary>
1575 /// Returns an enumerator that iterates through the elements stored to <see cref="CnmMemoryCache{TKey,TValue}"/>.
1576 /// </summary>
1577 /// <returns>
1578 /// A <see cref="IEnumerator{T}"/> that can be used to iterate through the collection.
1579 /// </returns>
1580 /// <filterpriority>1</filterpriority>
1581 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
1582 {
1583 return new Enumerator( this );
1584 }
1585
1586 /// <summary>
1587 /// Purge expired elements from the <see cref="ICnmCache{TKey,TValue}"/>.
1588 /// </summary>
1589 /// <remarks>
1590 /// <para>
1591 /// Element becomes expired when last access time to it has been longer time than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>.
1592 /// </para>
1593 /// <para>
1594 /// Depending on <see cref="ICnmCache{TKey,TValue}"/> implementation, some of expired elements
1595 /// may stay longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> in the cache.
1596 /// </para>
1597 /// </remarks>
1598 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
1599 /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
1600 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
1601 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
1602 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
1603 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
1604 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
1605 public void PurgeExpired()
1606 {
1607 m_operationsBetweenTimeChecks = DefaultOperationsBetweenTimeChecks;
1608
1609 var now = DateTime.Now;
1610 if( m_newGeneration.AccessedSinceLastTimeCheck )
1611 {
1612 // New generation has been accessed since last check
1613 // Update it's expiration time.
1614 m_newGeneration.ExpirationTime = now + ExpirationTime;
1615 m_newGeneration.AccessedSinceLastTimeCheck = false;
1616 }
1617 else if( m_newGeneration.ExpirationTime < now )
1618 {
1619 // New generation has been expired.
1620 // --> also old generation must be expired.
1621 PurgeGeneration( m_newGeneration );
1622 PurgeGeneration( m_oldGeneration );
1623 return;
1624 }
1625
1626 if( m_oldGeneration.ExpirationTime < now )
1627 PurgeGeneration( m_oldGeneration );
1628 }
1629
1630 /// <summary>
1631 /// Removes element associated with <paramref name="key"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
1632 /// </summary>
1633 /// <param name="key">
1634 /// The key that is associated with element to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
1635 /// </param>
1636 /// <exception cref="ArgumentNullException">
1637 /// <paramref name="key"/>is <see langword="null"/>.
1638 /// </exception>
1639 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
1640 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
1641 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
1642 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
1643 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1644 public void Remove( TKey key )
1645 {
1646 if( key == null )
1647 throw new ArgumentNullException( "key" );
1648
1649 var bucketIndex = GetBucketIndex( key );
1650 if( !m_newGeneration.Remove( bucketIndex, key ) )
1651 {
1652 if( !m_oldGeneration.Remove( bucketIndex, key ) )
1653 {
1654 CheckExpired();
1655 return;
1656 }
1657 }
1658
1659 CheckExpired();
1660 m_version++;
1661 }
1662
1663 /// <summary>
1664 /// Removes elements that are associated with one of <paramref name="keys"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
1665 /// </summary>
1666 /// <param name="keys">
1667 /// The keys that are associated with elements to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
1668 /// </param>
1669 /// <exception cref="ArgumentNullException">
1670 /// <paramref name="keys"/>is <see langword="null"/>.
1671 /// </exception>
1672 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
1673 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
1674 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
1675 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
1676 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1677 public void RemoveRange( IEnumerable<TKey> keys )
1678 {
1679 if( keys == null )
1680 throw new ArgumentNullException( "keys" );
1681
1682 foreach( var key in keys )
1683 {
1684 if( key == null )
1685 continue;
1686
1687 var bucketIndex = GetBucketIndex( key );
1688 if( !m_newGeneration.Remove( bucketIndex, key ) )
1689 m_oldGeneration.Remove( bucketIndex, key );
1690 }
1691
1692 CheckExpired();
1693 m_version++;
1694 }
1695
1696 /// <summary>
1697 /// Add or replace an element with the provided <paramref name="key"/>, <paramref name="value"/> and <paramref name="size"/> to
1698 /// <see cref="ICnmCache{TKey,TValue}"/>.
1699 /// </summary>
1700 /// <param name="key">
1701 /// The object used as the key of the element. Can't be <see langword="null"/> reference.
1702 /// </param>
1703 /// <param name="value">
1704 /// The object used as the value of the element to add or replace. <see langword="null"/> is allowed.
1705 /// </param>
1706 /// <param name="size">
1707 /// The element's size. Normally bytes, but can be any suitable unit of measure.
1708 /// </param>
1709 /// <returns>
1710 /// <see langword="true"/>if element has been added successfully to the <see cref="ICnmCache{TKey,TValue}"/>;
1711 /// otherwise <see langword="false"/>.
1712 /// </returns>
1713 /// <exception cref="ArgumentNullException">
1714 /// <paramref name="key"/>is <see langword="null"/>.
1715 /// </exception>
1716 /// <exception cref="ArgumentOutOfRangeException">
1717 /// The element's <paramref name="size"/> is less than 0.
1718 /// </exception>
1719 /// <remarks>
1720 /// <para>
1721 /// If element's <paramref name="size"/> is larger than <see cref="ICnmCache{TKey,TValue}.MaxElementSize"/>, then element is
1722 /// not added to the <see cref="ICnmCache{TKey,TValue}"/>, however - possible older element is
1723 /// removed from the <see cref="ICnmCache{TKey,TValue}"/>.
1724 /// </para>
1725 /// <para>
1726 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
1727 /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element.
1728 /// </para>
1729 /// <para>
1730 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
1731 /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element.
1732 /// </para>
1733 /// </remarks>
1734 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
1735 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
1736 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
1737 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
1738 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
1739 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
1740 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1741 public bool Set( TKey key, TValue value, long size )
1742 {
1743 if( key == null )
1744 throw new ArgumentNullException( "key" );
1745
1746 if( size < 0 )
1747 throw new ArgumentOutOfRangeException( "size", size, "Value's size can't be less than 0." );
1748
1749 if( size > MaxElementSize )
1750 {
1751 // Entry size is too big to fit cache - ignore it
1752 Remove( key );
1753 return false;
1754 }
1755
1756 if( size == 0 )
1757 size = 1;
1758
1759 var bucketIndex = GetBucketIndex( key );
1760 m_oldGeneration.Remove( bucketIndex, key );
1761 AddToNewGeneration( bucketIndex, key, value, size );
1762 CheckExpired();
1763
1764 return true;
1765 }
1766
1767 /// <summary>
1768 /// Gets the <paramref name="value"/> associated with the specified <paramref name="key"/>.
1769 /// </summary>
1770 /// <returns>
1771 /// <see langword="true"/>if the <see cref="ICnmCache{TKey,TValue}"/> contains an element with
1772 /// the specified key; otherwise, <see langword="false"/>.
1773 /// </returns>
1774 /// <param name="key">
1775 /// The key whose <paramref name="value"/> to get.
1776 /// </param>
1777 /// <param name="value">
1778 /// When this method returns, the value associated with the specified <paramref name="key"/>,
1779 /// if the <paramref name="key"/> is found; otherwise, the
1780 /// default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.
1781 /// </param>
1782 /// <exception cref="ArgumentNullException">
1783 /// <paramref name="key"/>is <see langword="null"/>.
1784 /// </exception>
1785 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
1786 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
1787 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
1788 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
1789 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
1790 public bool TryGetValue( TKey key, out TValue value )
1791 {
1792 if( key == null )
1793 throw new ArgumentNullException( "key" );
1794
1795 var bucketIndex = GetBucketIndex( key );
1796 long size;
1797 if( m_newGeneration.TryGetValue( bucketIndex, key, out value, out size ) )
1798 {
1799 CheckExpired();
1800 return true;
1801 }
1802
1803 if( m_oldGeneration.TryGetValue( bucketIndex, key, out value, out size ) )
1804 {
1805 // Move element to new generation
1806 AddToNewGeneration( bucketIndex, key, value, size );
1807 CheckExpired();
1808 return true;
1809 }
1810
1811 CheckExpired();
1812 return false;
1813 }
1814
1815 /// <summary>
1816 /// Returns an enumerator that iterates through a collection.
1817 /// </summary>
1818 /// <returns>
1819 /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
1820 /// </returns>
1821 /// <filterpriority>2</filterpriority>
1822 IEnumerator IEnumerable.GetEnumerator()
1823 {
1824 return GetEnumerator();
1825 }
1826
1827 #endregion
1828 }
1829}
diff --git a/OpenSim/Framework/CnmSynchronizedCache.cs b/OpenSim/Framework/CnmSynchronizedCache.cs
new file mode 100644
index 0000000..418a095
--- /dev/null
+++ b/OpenSim/Framework/CnmSynchronizedCache.cs
@@ -0,0 +1,746 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using System.Threading;
31
32namespace OpenSim.Framework
33{
34 /// <summary>
35 /// Synchronized Cenome cache wrapper.
36 /// </summary>
37 /// <typeparam name="TKey">
38 /// The type of keys in the cache.
39 /// </typeparam>
40 /// <typeparam name="TValue">
41 /// The type of values in the cache.
42 /// </typeparam>
43 /// <remarks>
44 /// <para>
45 /// Enumerator will block other threads, until enumerator's <see cref="IDisposable.Dispose"/> method is called.
46 /// "foreach" statement is automatically calling it.
47 /// </para>
48 /// </remarks>
49 public class CnmSynchronizedCache<TKey, TValue> : ICnmCache<TKey, TValue>
50 {
51 /// <summary>
52 /// The cache object.
53 /// </summary>
54 private readonly ICnmCache<TKey, TValue> m_cache;
55
56 /// <summary>
57 /// Synchronization root.
58 /// </summary>
59 private readonly object m_syncRoot;
60
61 /// <summary>
62 /// Initializes a new instance of the <see cref="CnmSynchronizedCache{TKey,TValue}"/> class.
63 /// Initializes a new instance of the <see cref="CnmSynchronizedCache{TKey,TValue}"/> class.
64 /// </summary>
65 /// <param name="cache">
66 /// The cache.
67 /// </param>
68 private CnmSynchronizedCache( ICnmCache<TKey, TValue> cache )
69 {
70 m_cache = cache;
71 m_syncRoot = m_cache.SyncRoot;
72 }
73
74 /// <summary>
75 /// Returns a <see cref="ICnmCache{TKey,TValue}"/> wrapper that is synchronized (thread safe).
76 /// </summary>
77 /// <param name="cache">
78 /// The <see cref="ICnmCache{TKey,TValue}"/> to synchronize.
79 /// </param>
80 /// <returns>
81 /// A <see cref="ICnmCache{TKey,TValue}"/> wrapper that is synchronized (thread safe).
82 /// </returns>
83 /// <exception cref="ArgumentNullException">
84 /// <paramref name="cache"/>is null.
85 /// </exception>
86 public static ICnmCache<TKey, TValue> Synchronized( ICnmCache<TKey, TValue> cache )
87 {
88 if( cache == null )
89 throw new ArgumentNullException( "cache" );
90 return cache.IsSynchronized ? cache : new CnmSynchronizedCache<TKey, TValue>( cache );
91 }
92
93 #region Nested type: SynchronizedEnumerator
94
95 /// <summary>
96 /// Synchronized enumerator.
97 /// </summary>
98 private class SynchronizedEnumerator : IEnumerator<KeyValuePair<TKey, TValue>>
99 {
100 /// <summary>
101 /// Enumerator that is being synchronized.
102 /// </summary>
103 private readonly IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator;
104
105 /// <summary>
106 /// Synchronization root.
107 /// </summary>
108 private object m_syncRoot;
109
110 /// <summary>
111 /// Initializes a new instance of the <see cref="SynchronizedEnumerator"/> class.
112 /// </summary>
113 /// <param name="enumerator">
114 /// The enumerator that is being synchronized.
115 /// </param>
116 /// <param name="syncRoot">
117 /// The sync root.
118 /// </param>
119 public SynchronizedEnumerator( IEnumerator<KeyValuePair<TKey, TValue>> enumerator, object syncRoot )
120 {
121 m_syncRoot = syncRoot;
122 m_enumerator = enumerator;
123 Monitor.Enter( m_syncRoot );
124 }
125
126 /// <summary>
127 /// Finalizes an instance of the <see cref="SynchronizedEnumerator"/> class.
128 /// </summary>
129 ~SynchronizedEnumerator()
130 {
131 Dispose();
132 }
133
134 #region IEnumerator<KeyValuePair<TKey,TValue>> Members
135
136 /// <summary>
137 /// Gets the element in the collection at the current position of the enumerator.
138 /// </summary>
139 /// <returns>
140 /// The element in the collection at the current position of the enumerator.
141 /// </returns>
142 /// <exception cref="InvalidOperationException">
143 /// The enumerator has reach end of collection or <see cref="MoveNext"/> is not called.
144 /// </exception>
145 public KeyValuePair<TKey, TValue> Current
146 {
147 get { return m_enumerator.Current; }
148 }
149
150 /// <summary>
151 /// Gets the current element in the collection.
152 /// </summary>
153 /// <returns>
154 /// The current element in the collection.
155 /// </returns>
156 /// <exception cref="InvalidOperationException">
157 /// The enumerator is positioned before the first element of the collection or after the last element.
158 /// </exception><filterpriority>2</filterpriority>
159 object IEnumerator.Current
160 {
161 get { return Current; }
162 }
163
164 /// <summary>
165 /// Releases synchronization lock.
166 /// </summary>
167 public void Dispose()
168 {
169 if( m_syncRoot != null )
170 {
171 Monitor.Exit( m_syncRoot );
172 m_syncRoot = null;
173 }
174
175 m_enumerator.Dispose();
176 GC.SuppressFinalize( this );
177 }
178
179 /// <summary>
180 /// Advances the enumerator to the next element of the collection.
181 /// </summary>
182 /// <returns>
183 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
184 /// </returns>
185 /// <exception cref="InvalidOperationException">
186 /// The collection was modified after the enumerator was created.
187 /// </exception>
188 public bool MoveNext()
189 {
190 return m_enumerator.MoveNext();
191 }
192
193 /// <summary>
194 /// Sets the enumerator to its initial position, which is before the first element in the collection.
195 /// </summary>
196 /// <exception cref="InvalidOperationException">
197 /// The collection was modified after the enumerator was created.
198 /// </exception>
199 public void Reset()
200 {
201 m_enumerator.Reset();
202 }
203
204 #endregion
205 }
206
207 #endregion
208
209 #region ICnmCache<TKey,TValue> Members
210
211 /// <summary>
212 /// Gets current count of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
213 /// </summary>
214 /// <remarks>
215 /// <para>
216 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
217 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
218 /// </para>
219 /// </remarks>
220 /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
221 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
222 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
223 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
224 public int Count
225 {
226 get
227 {
228 lock( m_syncRoot )
229 {
230 return m_cache.Count;
231 }
232 }
233 }
234
235 /// <summary>
236 /// Gets or sets elements expiration time.
237 /// </summary>
238 /// <value>
239 /// Elements expiration time.
240 /// </value>
241 /// <remarks>
242 /// <para>
243 /// When element has been stored in <see cref="ICnmCache{TKey,TValue}"/> longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
244 /// and it is not accessed through <see cref="ICnmCache{TKey,TValue}.TryGetValue"/> method or element's value is
245 /// not replaced by <see cref="ICnmCache{TKey,TValue}.Set"/> method, then it is automatically removed from the
246 /// <see cref="ICnmCache{TKey,TValue}"/>.
247 /// </para>
248 /// <para>
249 /// It is possible that <see cref="ICnmCache{TKey,TValue}"/> implementation removes element before it's expiration time,
250 /// because total size or count of elements stored to cache is larger than <see cref="ICnmCache{TKey,TValue}.MaxSize"/> or <see cref="ICnmCache{TKey,TValue}.MaxCount"/>.
251 /// </para>
252 /// <para>
253 /// It is also possible that element stays in cache longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>.
254 /// </para>
255 /// <para>
256 /// Calling <see cref="ICnmCache{TKey,TValue}.PurgeExpired"/> try to remove all elements that are expired.
257 /// </para>
258 /// <para>
259 /// To disable time limit in cache, set <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> to <see cref="DateTime.MaxValue"/>.
260 /// </para>
261 /// </remarks>
262 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
263 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
264 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
265 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
266 /// <seealso cref="ICnmCache{TKey,TValue}.Count"/>
267 /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
268 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
269 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
270 public TimeSpan ExpirationTime
271 {
272 get
273 {
274 lock( m_syncRoot )
275 {
276 return m_cache.ExpirationTime;
277 }
278 }
279
280 set
281 {
282 lock( m_syncRoot )
283 {
284 m_cache.ExpirationTime = value;
285 }
286 }
287 }
288
289 /// <summary>
290 /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting count of elements.
291 /// </summary>
292 /// <value>
293 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> count of elements is limited;
294 /// otherwise, <see langword="false"/>.
295 /// </value>
296 /// <remarks>
297 /// <para>
298 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
299 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
300 /// </para>
301 /// </remarks>
302 /// <seealso cref="ICnmCache{TKey,TValue}.Count"/>
303 /// <seealso cref="ICnmCache{TKey,TValue}.MaxCount"/>
304 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
305 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
306 public bool IsCountLimited
307 {
308 get
309 {
310 lock( m_syncRoot )
311 {
312 return m_cache.IsCountLimited;
313 }
314 }
315 }
316
317 /// <summary>
318 /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting size of elements.
319 /// </summary>
320 /// <value>
321 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> total size of elements is limited;
322 /// otherwise, <see langword="false"/>.
323 /// </value>
324 /// <remarks>
325 /// <para>
326 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
327 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
328 /// </para>
329 /// </remarks>
330 /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
331 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
332 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
333 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
334 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
335 public bool IsSizeLimited
336 {
337 get
338 {
339 lock( m_syncRoot )
340 {
341 return m_cache.IsSizeLimited;
342 }
343 }
344 }
345
346 /// <summary>
347 /// Gets a value indicating whether or not access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe).
348 /// </summary>
349 /// <value>
350 /// <see langword="true"/> if access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe);
351 /// otherwise, <see langword="false"/>.
352 /// </value>
353 /// <remarks>
354 /// <para>
355 /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/> object, use
356 /// <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> in <see cref="CnmSynchronizedCache{TKey,TValue}"/> class
357 /// to retrieve synchronized wrapper for <see cref="ICnmCache{TKey,TValue}"/> object.
358 /// </para>
359 /// </remarks>
360 /// <seealso cref="ICnmCache{TKey,TValue}.SyncRoot"/>
361 /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
362 public bool IsSynchronized
363 {
364 get { return true; }
365 }
366
367 /// <summary>
368 /// Gets a value indicating whether elements stored to <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time.
369 /// </summary>
370 /// <value>
371 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> has a fixed total size of elements;
372 /// otherwise, <see langword="false"/>.
373 /// </value>
374 /// <remarks>
375 /// If <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time and element is not accessed through <see cref="ICnmCache{TKey,TValue}.Set"/>
376 /// or <see cref="ICnmCache{TKey,TValue}.TryGetValue"/> methods in <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> , then element is automatically removed from
377 /// the cache. Depending on implementation of the <see cref="ICnmCache{TKey,TValue}"/>, some of the elements may
378 /// stay longer in cache.
379 /// </remarks>
380 /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
381 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
382 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
383 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
384 public bool IsTimeLimited
385 {
386 get
387 {
388 lock( m_syncRoot )
389 {
390 return m_cache.IsTimeLimited;
391 }
392 }
393 }
394
395 /// <summary>
396 /// Gets or sets maximal allowed count of elements that can be stored to <see cref="ICnmCache{TKey,TValue}"/>.
397 /// </summary>
398 /// <value>
399 /// <see cref="int.MaxValue"/>, if <see cref="ICnmCache{TKey,TValue}"/> is not limited by count of elements;
400 /// otherwise maximal allowed count of elements.
401 /// </value>
402 /// <remarks>
403 /// <para>
404 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
405 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
406 /// </para>
407 /// </remarks>
408 public int MaxCount
409 {
410 get
411 {
412 lock( m_syncRoot )
413 {
414 return m_cache.MaxCount;
415 }
416 }
417
418 set
419 {
420 lock( m_syncRoot )
421 {
422 m_cache.MaxCount = value;
423 }
424 }
425 }
426
427 /// <summary>
428 /// <para>Gets maximal allowed element size.</para>
429 /// </summary>
430 /// <value>
431 /// Maximal allowed element size.
432 /// </value>
433 /// <remarks>
434 /// <para>
435 /// If element's size is larger than <see cref="ICnmCache{TKey,TValue}.MaxElementSize"/>, then element is
436 /// not added to the <see cref="ICnmCache{TKey,TValue}"/>.
437 /// </para>
438 /// </remarks>
439 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
440 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
441 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
442 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
443 public long MaxElementSize
444 {
445 get
446 {
447 lock( m_syncRoot )
448 {
449 return m_cache.MaxElementSize;
450 }
451 }
452 }
453
454 /// <summary>
455 /// Gets or sets maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
456 /// </summary>
457 /// <value>
458 /// Maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
459 /// </value>
460 /// <remarks>
461 /// <para>
462 /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure.
463 /// </para>
464 /// <para>
465 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
466 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
467 /// </para>
468 /// </remarks>
469 /// <exception cref="ArgumentOutOfRangeException">value is less than 0.</exception>
470 /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
471 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
472 /// <seealso cref="ICnmCache{TKey,TValue}.Size"/>
473 public long MaxSize
474 {
475 get
476 {
477 lock( m_syncRoot )
478 {
479 return m_cache.MaxSize;
480 }
481 }
482
483 set
484 {
485 lock( m_syncRoot )
486 {
487 m_cache.MaxSize = value;
488 }
489 }
490 }
491
492 /// <summary>
493 /// Gets total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
494 /// </summary>
495 /// <value>
496 /// Total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
497 /// </value>
498 /// <remarks>
499 /// <para>
500 /// Normally bytes, but can be any suitable unit of measure.
501 /// </para>
502 /// <para>
503 /// Element's size is given when element is added or replaced by <see cref="ICnmCache{TKey,TValue}.Set"/> method.
504 /// </para>
505 /// <para>
506 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
507 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
508 /// </para>
509 /// </remarks>
510 /// <seealso cref="ICnmCache{TKey,TValue}.MaxElementSize"/>
511 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
512 /// <seealso cref="ICnmCache{TKey,TValue}.MaxSize"/>
513 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
514 /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
515 public long Size
516 {
517 get
518 {
519 lock( m_syncRoot )
520 {
521 return m_cache.Size;
522 }
523 }
524 }
525
526 /// <summary>
527 /// Gets an object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
528 /// </summary>
529 /// <value>
530 /// An object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
531 /// </value>
532 /// <remarks>
533 /// <para>
534 /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/>, use <see cref="CnmSynchronizedCache{TKey,TValue}"/>
535 /// method <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> to retrieve synchronized wrapper interface to
536 /// <see cref="ICnmCache{TKey,TValue}"/>.
537 /// </para>
538 /// </remarks>
539 /// <seealso cref="ICnmCache{TKey,TValue}.IsSynchronized"/>
540 /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
541 public object SyncRoot
542 {
543 get { return m_syncRoot; }
544 }
545
546 /// <summary>
547 /// Removes all elements from the <see cref="ICnmCache{TKey,TValue}"/>.
548 /// </summary>
549 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
550 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
551 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
552 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
553 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
554 public void Clear()
555 {
556 lock( m_syncRoot )
557 {
558 m_cache.Clear();
559 }
560 }
561
562 /// <summary>
563 /// Returns an enumerator that iterates through the elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
564 /// </summary>
565 /// <returns>
566 /// A <see cref="IEnumerator{T}"/> that can be used to iterate through the collection.
567 /// </returns>
568 /// <filterpriority>1</filterpriority>
569 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
570 {
571 lock( m_syncRoot )
572 {
573 return new SynchronizedEnumerator( m_cache.GetEnumerator(), m_syncRoot );
574 }
575 }
576
577 /// <summary>
578 /// Purge expired elements from the <see cref="ICnmCache{TKey,TValue}"/>.
579 /// </summary>
580 /// <remarks>
581 /// <para>
582 /// Element becomes expired when last access time to it has been longer time than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/>.
583 /// </para>
584 /// <para>
585 /// Depending on <see cref="ICnmCache{TKey,TValue}"/> implementation, some of expired elements
586 /// may stay longer than <see cref="ICnmCache{TKey,TValue}.ExpirationTime"/> in the cache.
587 /// </para>
588 /// </remarks>
589 /// <seealso cref="ICnmCache{TKey,TValue}.IsTimeLimited"/>
590 /// <seealso cref="ICnmCache{TKey,TValue}.ExpirationTime"/>
591 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
592 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
593 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
594 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
595 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
596 public void PurgeExpired()
597 {
598 lock( m_syncRoot )
599 {
600 m_cache.PurgeExpired();
601 }
602 }
603
604 /// <summary>
605 /// Removes element associated with <paramref name="key"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
606 /// </summary>
607 /// <param name="key">
608 /// The key that is associated with element to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
609 /// </param>
610 /// <exception cref="ArgumentNullException">
611 /// <paramref name="key"/>is <see langword="null"/>.
612 /// </exception>
613 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
614 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
615 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
616 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
617 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
618 public void Remove( TKey key )
619 {
620 lock( m_syncRoot )
621 {
622 m_cache.Remove( key );
623 }
624 }
625
626 /// <summary>
627 /// Removes elements that are associated with one of <paramref name="keys"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
628 /// </summary>
629 /// <param name="keys">
630 /// The keys that are associated with elements to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
631 /// </param>
632 /// <exception cref="ArgumentNullException">
633 /// <paramref name="keys"/>is <see langword="null"/>.
634 /// </exception>
635 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
636 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
637 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
638 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
639 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
640 public void RemoveRange( IEnumerable<TKey> keys )
641 {
642 lock( m_syncRoot )
643 {
644 m_cache.RemoveRange( keys );
645 }
646 }
647
648 /// <summary>
649 /// Add or replace an element with the provided <paramref name="key"/>, <paramref name="value"/> and <paramref name="size"/> to
650 /// <see cref="ICnmCache{TKey,TValue}"/>.
651 /// </summary>
652 /// <param name="key">
653 /// The object used as the key of the element. Can't be <see langword="null"/> reference.
654 /// </param>
655 /// <param name="value">
656 /// The object used as the value of the element to add or replace. <see langword="null"/> is allowed.
657 /// </param>
658 /// <param name="size">
659 /// The element's size. Normally bytes, but can be any suitable unit of measure.
660 /// </param>
661 /// <returns>
662 /// <see langword="true"/>if element has been added successfully to the <see cref="ICnmCache{TKey,TValue}"/>;
663 /// otherwise <see langword="false"/>.
664 /// </returns>
665 /// <exception cref="ArgumentNullException">
666 /// <paramref name="key"/>is <see langword="null"/>.
667 /// </exception>
668 /// <exception cref="ArgumentOutOfRangeException">
669 /// The element's <paramref name="size"/> is less than 0.
670 /// </exception>
671 /// <remarks>
672 /// <para>
673 /// If element's <paramref name="size"/> is larger than <see cref="ICnmCache{TKey,TValue}.MaxElementSize"/>, then element is
674 /// not added to the <see cref="ICnmCache{TKey,TValue}"/>, however - possible older element is
675 /// removed from the <see cref="ICnmCache{TKey,TValue}"/>.
676 /// </para>
677 /// <para>
678 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
679 /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element.
680 /// </para>
681 /// <para>
682 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
683 /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element.
684 /// </para>
685 /// </remarks>
686 /// <seealso cref="ICnmCache{TKey,TValue}.IsSizeLimited"/>
687 /// <seealso cref="ICnmCache{TKey,TValue}.IsCountLimited"/>
688 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
689 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
690 /// <seealso cref="ICnmCache{TKey,TValue}.TryGetValue"/>
691 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
692 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
693 public bool Set( TKey key, TValue value, long size )
694 {
695 lock( m_syncRoot )
696 {
697 return m_cache.Set( key, value, size );
698 }
699 }
700
701 /// <summary>
702 /// Gets the <paramref name="value"/> associated with the specified <paramref name="key"/>.
703 /// </summary>
704 /// <returns>
705 /// <see langword="true"/>if the <see cref="ICnmCache{TKey,TValue}"/> contains an element with
706 /// the specified key; otherwise, <see langword="false"/>.
707 /// </returns>
708 /// <param name="key">
709 /// The key whose <paramref name="value"/> to get.
710 /// </param>
711 /// <param name="value">
712 /// When this method returns, the value associated with the specified <paramref name="key"/>,
713 /// if the <paramref name="key"/> is found; otherwise, the
714 /// default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.
715 /// </param>
716 /// <exception cref="ArgumentNullException">
717 /// <paramref name="key"/>is <see langword="null"/>.
718 /// </exception>
719 /// <seealso cref="ICnmCache{TKey,TValue}.Set"/>
720 /// <seealso cref="ICnmCache{TKey,TValue}.Remove"/>
721 /// <seealso cref="ICnmCache{TKey,TValue}.RemoveRange"/>
722 /// <seealso cref="ICnmCache{TKey,TValue}.Clear"/>
723 /// <seealso cref="ICnmCache{TKey,TValue}.PurgeExpired"/>
724 public bool TryGetValue( TKey key, out TValue value )
725 {
726 lock( m_syncRoot )
727 {
728 return m_cache.TryGetValue( key, out value );
729 }
730 }
731
732 /// <summary>
733 /// Returns an enumerator that iterates through the elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
734 /// </summary>
735 /// <returns>
736 /// A <see cref="IEnumerator"/> that can be used to iterate through the collection.
737 /// </returns>
738 /// <filterpriority>1</filterpriority>
739 IEnumerator IEnumerable.GetEnumerator()
740 {
741 return GetEnumerator();
742 }
743
744 #endregion
745 }
746}
diff --git a/OpenSim/Framework/ICnmCache.cs b/OpenSim/Framework/ICnmCache.cs
new file mode 100644
index 0000000..cba8a7f
--- /dev/null
+++ b/OpenSim/Framework/ICnmCache.cs
@@ -0,0 +1,441 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30
31namespace OpenSim.Framework
32{
33 /// <summary>
34 /// Represent generic cache to store key/value pairs (elements) limited by time, size and count of elements.
35 /// </summary>
36 /// <typeparam name="TKey">
37 /// The type of keys in the cache.
38 /// </typeparam>
39 /// <typeparam name="TValue">
40 /// The type of values in the cache.
41 /// </typeparam>
42 /// <remarks>
43 /// <para>
44 /// Cache store limitations:
45 /// </para>
46 /// <list type="table">
47 /// <listheader>
48 /// <term>Limitation</term>
49 /// <description>Description</description>
50 /// </listheader>
51 /// <item>
52 /// <term>Time</term>
53 /// <description>
54 /// Element that is not accessed through <see cref="TryGetValue"/> or <see cref="Set"/> in last <see cref="ExpirationTime"/> are
55 /// removed from the cache automatically. Depending on implementation of the cache some of elements may stay longer in cache.
56 /// <see cref="IsTimeLimited"/> returns <see langword="true"/>, if cache is limited by time.
57 /// </description>
58 /// </item>
59 /// <item>
60 /// <term>Count</term>
61 /// <description>
62 /// When adding an new element to cache that already have <see cref="MaxCount"/> of elements, cache will remove less recently
63 /// used element(s) from the cache, until element fits to cache.
64 /// <see cref="IsCountLimited"/> returns <see langword="true"/>, if cache is limiting element count.
65 /// </description>
66 /// </item>
67 /// <item>
68 /// <term>Size</term>
69 /// <description>
70 /// <description>
71 /// When adding an new element to cache that already have <see cref="MaxSize"/> of elements, cache will remove less recently
72 /// used element(s) from the cache, until element fits to cache.
73 /// <see cref="IsSizeLimited"/> returns <see langword="true"/>, if cache is limiting total size of elements.
74 /// Normally size is bytes used by element in the cache. But it can be any other suitable unit of measure.
75 /// </description>
76 /// </description>
77 /// </item>
78 /// </list>
79 /// </remarks>
80 public interface ICnmCache<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
81 {
82 /// <summary>
83 /// Gets current count of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
84 /// </summary>
85 /// <remarks>
86 /// <para>
87 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
88 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
89 /// </para>
90 /// </remarks>
91 /// <seealso cref="MaxCount"/>
92 /// <seealso cref="IsCountLimited"/>
93 /// <seealso cref="IsSizeLimited"/>
94 /// <seealso cref="IsTimeLimited"/>
95 int Count { get; }
96
97 /// <summary>
98 /// Gets or sets elements expiration time.
99 /// </summary>
100 /// <value>
101 /// Elements expiration time.
102 /// </value>
103 /// <remarks>
104 /// <para>
105 /// When element has been stored in <see cref="ICnmCache{TKey,TValue}"/> longer than <see cref="ExpirationTime"/>
106 /// and it is not accessed through <see cref="TryGetValue"/> method or element's value is
107 /// not replaced by <see cref="Set"/> method, then it is automatically removed from the
108 /// <see cref="ICnmCache{TKey,TValue}"/>.
109 /// </para>
110 /// <para>
111 /// It is possible that <see cref="ICnmCache{TKey,TValue}"/> implementation removes element before it's expiration time,
112 /// because total size or count of elements stored to cache is larger than <see cref="MaxSize"/> or <see cref="MaxCount"/>.
113 /// </para>
114 /// <para>
115 /// It is also possible that element stays in cache longer than <see cref="ExpirationTime"/>.
116 /// </para>
117 /// <para>
118 /// Calling <see cref="PurgeExpired"/> try to remove all elements that are expired.
119 /// </para>
120 /// <para>
121 /// To disable time limit in cache, set <see cref="ExpirationTime"/> to <see cref="DateTime.MaxValue"/>.
122 /// </para>
123 /// </remarks>
124 /// <seealso cref="IsTimeLimited"/>
125 /// <seealso cref="IsCountLimited"/>
126 /// <seealso cref="IsSizeLimited"/>
127 /// <seealso cref="PurgeExpired"/>
128 /// <seealso cref="Count"/>
129 /// <seealso cref="MaxCount"/>
130 /// <seealso cref="MaxSize"/>
131 /// <seealso cref="Size"/>
132 TimeSpan ExpirationTime { get; set; }
133
134 /// <summary>
135 /// Gets a value indicating whether or not access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe).
136 /// </summary>
137 /// <value>
138 /// <see langword="true"/> if access to the <see cref="ICnmCache{TKey,TValue}"/> is synchronized (thread safe);
139 /// otherwise, <see langword="false"/>.
140 /// </value>
141 /// <remarks>
142 /// <para>
143 /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/> object, use
144 /// <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> in <see cref="CnmSynchronizedCache{TKey,TValue}"/> class
145 /// to retrieve synchronized wrapper for <see cref="ICnmCache{TKey,TValue}"/> object.
146 /// </para>
147 /// </remarks>
148 /// <seealso cref="SyncRoot"/>
149 /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
150 bool IsSynchronized { get; }
151
152 /// <summary>
153 /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting count of elements.
154 /// </summary>
155 /// <value>
156 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> count of elements is limited;
157 /// otherwise, <see langword="false"/>.
158 /// </value>
159 /// <remarks>
160 /// <para>
161 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
162 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
163 /// </para>
164 /// </remarks>
165 /// <seealso cref="Count"/>
166 /// <seealso cref="MaxCount"/>
167 /// <seealso cref="IsSizeLimited"/>
168 /// <seealso cref="IsTimeLimited"/>
169 bool IsCountLimited { get; }
170
171 /// <summary>
172 /// Gets a value indicating whether <see cref="ICnmCache{TKey,TValue}"/> is limiting size of elements.
173 /// </summary>
174 /// <value>
175 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> total size of elements is limited;
176 /// otherwise, <see langword="false"/>.
177 /// </value>
178 /// <remarks>
179 /// <para>
180 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
181 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
182 /// </para>
183 /// </remarks>
184 /// <seealso cref="MaxElementSize"/>
185 /// <seealso cref="Size"/>
186 /// <seealso cref="MaxSize"/>
187 /// <seealso cref="IsCountLimited"/>
188 /// <seealso cref="IsTimeLimited"/>
189 bool IsSizeLimited { get; }
190
191 /// <summary>
192 /// Gets a value indicating whether elements stored to <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time.
193 /// </summary>
194 /// <value>
195 /// <see langword="true"/> if the <see cref="ICnmCache{TKey,TValue}"/> has a fixed total size of elements;
196 /// otherwise, <see langword="false"/>.
197 /// </value>
198 /// <remarks>
199 /// If <see cref="ICnmCache{TKey,TValue}"/> have limited inactivity time and element is not accessed through <see cref="Set"/>
200 /// or <see cref="TryGetValue"/> methods in <see cref="ExpirationTime"/> , then element is automatically removed from
201 /// the cache. Depending on implementation of the <see cref="ICnmCache{TKey,TValue}"/>, some of the elements may
202 /// stay longer in cache.
203 /// </remarks>
204 /// <seealso cref="ExpirationTime"/>
205 /// <seealso cref="PurgeExpired"/>
206 /// <seealso cref="IsCountLimited"/>
207 /// <seealso cref="IsSizeLimited"/>
208 bool IsTimeLimited { get; }
209
210 /// <summary>
211 /// Gets or sets maximal allowed count of elements that can be stored to <see cref="ICnmCache{TKey,TValue}"/>.
212 /// </summary>
213 /// <value>
214 /// <see cref="int.MaxValue"/>, if <see cref="ICnmCache{TKey,TValue}"/> is not limited by count of elements;
215 /// otherwise maximal allowed count of elements.
216 /// </value>
217 /// <remarks>
218 /// <para>
219 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
220 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
221 /// </para>
222 /// </remarks>
223 int MaxCount { get; set; }
224
225 /// <summary>
226 /// <para>Gets maximal allowed element size.</para>
227 /// </summary>
228 /// <value>
229 /// Maximal allowed element size.
230 /// </value>
231 /// <remarks>
232 /// <para>
233 /// If element's size is larger than <see cref="MaxElementSize"/>, then element is
234 /// not added to the <see cref="ICnmCache{TKey,TValue}"/>.
235 /// </para>
236 /// </remarks>
237 /// <seealso cref="Set"/>
238 /// <seealso cref="IsSizeLimited"/>
239 /// <seealso cref="Size"/>
240 /// <seealso cref="MaxSize"/>
241 long MaxElementSize { get; }
242
243 /// <summary>
244 /// Gets or sets maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
245 /// </summary>
246 /// <value>
247 /// Maximal allowed total size for elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
248 /// </value>
249 /// <remarks>
250 /// <para>
251 /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure.
252 /// </para>
253 /// <para>
254 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
255 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
256 /// </para>
257 /// </remarks>
258 /// <exception cref="ArgumentOutOfRangeException">value is less than 0.</exception>
259 /// <seealso cref="MaxElementSize"/>
260 /// <seealso cref="IsSizeLimited"/>
261 /// <seealso cref="Size"/>
262 long MaxSize { get; set; }
263
264 /// <summary>
265 /// Gets total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
266 /// </summary>
267 /// <value>
268 /// Total size of elements stored to <see cref="ICnmCache{TKey,TValue}"/>.
269 /// </value>
270 /// <remarks>
271 /// <para>
272 /// Normally bytes, but can be any suitable unit of measure.
273 /// </para>
274 /// <para>
275 /// Element's size is given when element is added or replaced by <see cref="Set"/> method.
276 /// </para>
277 /// <para>
278 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
279 /// <see cref="ICnmCache{TKey,TValue}"/> will remove less recently used elements until it can fit an new element.
280 /// </para>
281 /// </remarks>
282 /// <seealso cref="MaxElementSize"/>
283 /// <seealso cref="IsSizeLimited"/>
284 /// <seealso cref="MaxSize"/>
285 /// <seealso cref="IsCountLimited"/>
286 /// <seealso cref="ExpirationTime"/>
287 long Size { get; }
288
289 /// <summary>
290 /// Gets an object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
291 /// </summary>
292 /// <value>
293 /// An object that can be used to synchronize access to the <see cref="ICnmCache{TKey,TValue}"/>.
294 /// </value>
295 /// <remarks>
296 /// <para>
297 /// To get synchronized (thread safe) access to <see cref="ICnmCache{TKey,TValue}"/>, use <see cref="CnmSynchronizedCache{TKey,TValue}"/>
298 /// method <see cref="CnmSynchronizedCache{TKey,TValue}.Synchronized"/> to retrieve synchronized wrapper interface to
299 /// <see cref="ICnmCache{TKey,TValue}"/>.
300 /// </para>
301 /// </remarks>
302 /// <seealso cref="IsSynchronized"/>
303 /// <seealso cref="CnmSynchronizedCache{TKey,TValue}"/>
304 object SyncRoot { get; }
305
306 /// <summary>
307 /// Removes all elements from the <see cref="ICnmCache{TKey,TValue}"/>.
308 /// </summary>
309 /// <seealso cref="Set"/>
310 /// <seealso cref="Remove"/>
311 /// <seealso cref="RemoveRange"/>
312 /// <seealso cref="TryGetValue"/>
313 /// <seealso cref="PurgeExpired"/>
314 void Clear();
315
316 /// <summary>
317 /// Purge expired elements from the <see cref="ICnmCache{TKey,TValue}"/>.
318 /// </summary>
319 /// <remarks>
320 /// <para>
321 /// Element becomes expired when last access time to it has been longer time than <see cref="ExpirationTime"/>.
322 /// </para>
323 /// <para>
324 /// Depending on <see cref="ICnmCache{TKey,TValue}"/> implementation, some of expired elements
325 /// may stay longer than <see cref="ExpirationTime"/> in the cache.
326 /// </para>
327 /// </remarks>
328 /// <seealso cref="IsTimeLimited"/>
329 /// <seealso cref="ExpirationTime"/>
330 /// <seealso cref="Set"/>
331 /// <seealso cref="Remove"/>
332 /// <seealso cref="RemoveRange"/>
333 /// <seealso cref="TryGetValue"/>
334 /// <seealso cref="Clear"/>
335 void PurgeExpired();
336
337 /// <summary>
338 /// Removes element associated with <paramref name="key"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
339 /// </summary>
340 /// <param name="key">
341 /// The key that is associated with element to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
342 /// </param>
343 /// <exception cref="ArgumentNullException">
344 /// <paramref name="key"/> is <see langword="null"/>.
345 /// </exception>
346 /// <seealso cref="Set"/>
347 /// <seealso cref="RemoveRange"/>
348 /// <seealso cref="TryGetValue"/>
349 /// <seealso cref="Clear"/>
350 /// <seealso cref="PurgeExpired"/>
351 void Remove( TKey key );
352
353 /// <summary>
354 /// Removes elements that are associated with one of <paramref name="keys"/> from the <see cref="ICnmCache{TKey,TValue}"/>.
355 /// </summary>
356 /// <param name="keys">
357 /// The keys that are associated with elements to remove from the <see cref="ICnmCache{TKey,TValue}"/>.
358 /// </param>
359 /// <exception cref="ArgumentNullException">
360 /// <paramref name="keys"/> is <see langword="null"/>.
361 /// </exception>
362 /// <seealso cref="Set"/>
363 /// <seealso cref="Remove"/>
364 /// <seealso cref="TryGetValue"/>
365 /// <seealso cref="Clear"/>
366 /// <seealso cref="PurgeExpired"/>
367 void RemoveRange( IEnumerable<TKey> keys );
368
369 /// <summary>
370 /// Add or replace an element with the provided <paramref name="key"/>, <paramref name="value"/> and <paramref name="size"/> to
371 /// <see cref="ICnmCache{TKey,TValue}"/>.
372 /// </summary>
373 /// <param name="key">
374 /// The object used as the key of the element. Can't be <see langword="null"/> reference.
375 /// </param>
376 /// <param name="value">
377 /// The object used as the value of the element to add or replace. <see langword="null"/> is allowed.
378 /// </param>
379 /// <param name="size">
380 /// The element's size. Normally bytes, but can be any suitable unit of measure.
381 /// </param>
382 /// <returns>
383 /// <see langword="true"/> if element has been added successfully to the <see cref="ICnmCache{TKey,TValue}"/>;
384 /// otherwise <see langword="false"/>.
385 /// </returns>
386 /// <exception cref="ArgumentNullException">
387 /// <paramref name="key"/>is <see langword="null"/>.
388 /// </exception>
389 /// <exception cref="ArgumentOutOfRangeException">
390 /// The element's <paramref name="size"/> is less than 0.
391 /// </exception>
392 /// <remarks>
393 /// <para>
394 /// If element's <paramref name="size"/> is larger than <see cref="MaxElementSize"/>, then element is
395 /// not added to the <see cref="ICnmCache{TKey,TValue}"/>, however - possible older element is
396 /// removed from the <see cref="ICnmCache{TKey,TValue}"/>.
397 /// </para>
398 /// <para>
399 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting total size of elements,
400 /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element.
401 /// </para>
402 /// <para>
403 /// When adding an new element to <see cref="ICnmCache{TKey,TValue}"/> that is limiting element count,
404 /// <see cref="ICnmCache{TKey,TValue}"/>will remove less recently used elements until it can fit an new element.
405 /// </para>
406 /// </remarks>
407 /// <seealso cref="IsSizeLimited"/>
408 /// <seealso cref="IsCountLimited"/>
409 /// <seealso cref="Remove"/>
410 /// <seealso cref="RemoveRange"/>
411 /// <seealso cref="TryGetValue"/>
412 /// <seealso cref="Clear"/>
413 /// <seealso cref="PurgeExpired"/>
414 bool Set( TKey key, TValue value, long size );
415
416 /// <summary>
417 /// Gets the <paramref name="value"/> associated with the specified <paramref name="key"/>.
418 /// </summary>
419 /// <returns>
420 /// <see langword="true"/>if the <see cref="ICnmCache{TKey,TValue}"/> contains an element with
421 /// the specified key; otherwise, <see langword="false"/>.
422 /// </returns>
423 /// <param name="key">
424 /// The key whose <paramref name="value"/> to get.
425 /// </param>
426 /// <param name="value">
427 /// When this method returns, the value associated with the specified <paramref name="key"/>,
428 /// if the <paramref name="key"/> is found; otherwise, the
429 /// default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.
430 /// </param>
431 /// <exception cref="ArgumentNullException">
432 /// <paramref name="key"/>is <see langword="null"/>.
433 /// </exception>
434 /// <seealso cref="Set"/>
435 /// <seealso cref="Remove"/>
436 /// <seealso cref="RemoveRange"/>
437 /// <seealso cref="Clear"/>
438 /// <seealso cref="PurgeExpired"/>
439 bool TryGetValue( TKey key, out TValue value );
440 }
441}
diff --git a/OpenSim/Framework/PrimeNumberHelper.cs b/OpenSim/Framework/PrimeNumberHelper.cs
new file mode 100644
index 0000000..33b70c7
--- /dev/null
+++ b/OpenSim/Framework/PrimeNumberHelper.cs
@@ -0,0 +1,114 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28
29namespace OpenSim.Framework
30{
31 /// <summary>
32 /// Utility class that is used to find small prime numbers and test is number prime number.
33 /// </summary>
34 public static class PrimeNumberHelper
35 {
36 /// <summary>
37 /// Precalculated prime numbers.
38 /// </summary>
39 private static readonly int[] Primes = new[]
40 {
41 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239,
42 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333,
43 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
44 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431,
45 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449,
46 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
47 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559,
48 5999471, 7199369
49 };
50
51 /// <summary>
52 /// Get prime number that is equal or larger than <see cref="min"/>.
53 /// </summary>
54 /// <param name="min">
55 /// Minimal returned prime number.
56 /// </param>
57 /// <returns>
58 /// Primer number that is equal or larger than <see cref="min"/>. If <see cref="min"/> is too large, return -1.
59 /// </returns>
60 public static int GetPrime( int min )
61 {
62 if( min <= 2 )
63 return 2;
64
65 if( Primes[ Primes.Length - 1 ] < min )
66 {
67 for( var i = min | 1 ; i < 0x7FFFFFFF ; i += 2 )
68 {
69 if( IsPrime( i ) )
70 return i;
71 }
72
73 return -1;
74 }
75
76 for( var i = Primes.Length - 2 ; i >= 0 ; i-- )
77 {
78 if( min == Primes[ i ] )
79 return min;
80
81 if( min > Primes[ i ] )
82 return Primes[ i + 1 ];
83 }
84
85 return 2;
86 }
87
88 /// <summary>
89 /// Just basic Sieve of Eratosthenes prime number test.
90 /// </summary>
91 /// <param name="candinate">
92 /// Number that is tested.
93 /// </param>
94 /// <returns>
95 /// true, if <see cref="candinate"/> is prime number; otherwise false.
96 /// </returns>
97 public static bool IsPrime( int candinate )
98 {
99 if( (candinate & 1) == 0 )
100
101 // Even number - only prime if 2
102 return candinate == 2;
103
104 var upperBound = (int) Math.Sqrt( candinate );
105 for( var i = 3 ; i < upperBound ; i += 2 )
106 {
107 if( candinate % i == 0 )
108 return false;
109 }
110
111 return true;
112 }
113 }
114}
diff --git a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs
new file mode 100644
index 0000000..4a34ca0
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs
@@ -0,0 +1,381 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using log4net;
31using Nini.Config;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Interfaces;
34using OpenSim.Region.Framework.Scenes;
35
36namespace OpenSim.Region.CoreModules.Asset
37{
38 /// <summary>
39 /// Cenome memory asset cache.
40 /// </summary>
41 /// <remarks>
42 /// <para>
43 /// Cache is enabled by setting "AssetCaching" configuration to value "CenomeMemoryAssetCache".
44 /// When cache is successfully enable log should have message
45 /// "[ASSET CACHE]: Cenome asset cache enabled (MaxSize = XXX bytes, MaxCount = XXX, ExpirationTime = XXX)".
46 /// </para>
47 /// <para>
48 /// Cache's size is limited by two parameters:
49 /// maximal allowed size in bytes and maximal allowed asset count. When new asset
50 /// is added to cache that have achieved either size or count limitation, cache
51 /// will automatically remove less recently used assets from cache. Additionally
52 /// asset's lifetime is controlled by expiration time.
53 /// </para>
54 /// <para>
55 /// <list type="table">
56 /// <listheader>
57 /// <term>Configuration</term>
58 /// <description>Description</description>
59 /// </listheader>
60 /// <item>
61 /// <term>MaxSize</term>
62 /// <description>Maximal size of the cache in bytes. Default value: 128MB (134 217 728 bytes).</description>
63 /// </item>
64 /// <item>
65 /// <term>MaxCount</term>
66 /// <description>Maximal count of assets stored to cache. Default value: 4096 assets.</description>
67 /// </item>
68 /// <item>
69 /// <term>ExpirationTime</term>
70 /// <description>Asset's expiration time in minutes. Default value: 30 minutes.</description>
71 /// </item>
72 /// </list>
73 /// </para>
74 /// </remarks>
75 /// <example>
76 /// Enabling Cenome Asset Cache:
77 /// <code>
78 /// [Modules]
79 /// AssetCaching = "CenomeMemoryAssetCache"
80 /// </code>
81 /// Setting size and expiration time limitations:
82 /// <code>
83 /// [AssetService]
84 /// ; 256 MB (default: 134217728)
85 /// MaxSize = 268435456
86 /// ; How many assets it is possible to store cache (default: 4096)
87 /// MaxCount = 16384
88 /// ; Expiration time - 1 hour (default: 30 minutes)
89 /// ExpirationTime = 60
90 /// </code>
91 /// </example>
92 public class CenomeMemoryAssetCache : IImprovedAssetCache, ISharedRegionModule
93 {
94 /// <summary>
95 /// Cache's default maximal asset count.
96 /// </summary>
97 /// <remarks>
98 /// <para>
99 /// Assuming that average asset size is about 32768 bytes.
100 /// </para>
101 /// </remarks>
102 public const int DefaultMaxCount = 4096;
103
104 /// <summary>
105 /// Default maximal size of the cache in bytes
106 /// </summary>
107 /// <remarks>
108 /// <para>
109 /// 128MB = 128 * 1024^2 = 134 217 728 bytes.
110 /// </para>
111 /// </remarks>
112 public const long DefaultMaxSize = 134217728;
113
114 /// <summary>
115 /// Asset's default expiration time in the cache.
116 /// </summary>
117 public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes( 30.0 );
118
119 /// <summary>
120 /// Log manager instance.
121 /// </summary>
122 private static readonly ILog Log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType );
123
124 /// <summary>
125 /// Cache object.
126 /// </summary>
127 private ICnmCache<string, AssetBase> m_cache;
128
129 /// <summary>
130 /// Is Cenome asset cache enabled.
131 /// </summary>
132 private bool m_enabled;
133
134 /// <summary>
135 /// Count of get requests
136 /// </summary>
137 private int m_getCount;
138
139 /// <summary>
140 /// How many hits
141 /// </summary>
142 private int m_hitCount;
143
144 /// <summary>
145 /// Count of cache commands
146 /// </summary>
147 private int m_cachedCount;
148
149 /// <summary>
150 /// How many gets before dumping statistics
151 /// </summary>
152 /// <remarks>
153 /// If 0 or less, then disabled.
154 /// </remarks>
155 private int m_debugEpoch = 0;
156
157 /// <summary>
158 /// Initialize asset cache module with default parameters.
159 /// </summary>
160 public void Initialize()
161 {
162 Initialize( DefaultMaxSize, DefaultMaxCount, DefaultExpirationTime );
163 }
164
165 /// <summary>
166 /// Initialize asset cache module, with custom parameters.
167 /// </summary>
168 /// <param name="maximalSize">
169 /// Cache's maximal size in bytes.
170 /// </param>
171 /// <param name="maximalCount">
172 /// Cache's maximal count of assets.
173 /// </param>
174 /// <param name="expirationTime">
175 /// Asset's expiration time.
176 /// </param>
177 public void Initialize( long maximalSize, int maximalCount, TimeSpan expirationTime )
178 {
179 if( maximalSize <= 0 || maximalCount <= 0 || expirationTime <= TimeSpan.Zero )
180 {
181 Log.Info( "[ASSET CACHE]: Cenome asset cache is not enabled." );
182 m_enabled = false;
183 return;
184 }
185
186 // Create cache and add synchronization wrapper over it
187 m_cache =
188 CnmSynchronizedCache<string, AssetBase>.Synchronized( new CnmMemoryCache<string, AssetBase>(
189 maximalSize, maximalCount, expirationTime ) );
190 m_enabled = true;
191 Log.InfoFormat("[ASSET CACHE]: Cenome asset cache enabled (MaxSize = {0} bytes, MaxCount = {1}, ExpirationTime = {2})", maximalSize, maximalCount, expirationTime );
192 }
193
194 #region IImprovedAssetCache Members
195 /// <summary>
196 /// Cache asset.
197 /// </summary>
198 /// <param name="asset">
199 /// The asset that is being cached.
200 /// </param>
201 public void Cache( AssetBase asset )
202 {
203 long size = asset.Data != null ? asset.Data.Length : 1;
204 m_cache.Set( asset.ID, asset, size );
205 m_cachedCount++;
206 }
207
208 /// <summary>
209 /// Clear asset cache.
210 /// </summary>
211 public void Clear()
212 {
213 m_cache.Clear();
214 }
215
216 /// <summary>
217 /// Expire (remove) asset stored to cache.
218 /// </summary>
219 /// <param name="id">
220 /// The expired asset's id.
221 /// </param>
222 public void Expire( string id )
223 {
224 m_cache.Remove( id );
225 }
226
227 /// <summary>
228 /// Get asset stored
229 /// </summary>
230 /// <param name="id">
231 /// The asset's id.
232 /// </param>
233 /// <returns>
234 /// Asset if it is found from cache; otherwise <see langword="null"/>.
235 /// </returns>
236 /// <remarks>
237 /// <para>
238 /// Caller should always check that is return value <see langword="null"/>.
239 /// Cache doesn't guarantee in any situation that asset is stored to it.
240 /// </para>
241 /// </remarks>
242 public AssetBase Get( string id )
243 {
244 m_getCount++;
245 AssetBase assetBase;
246 if( m_cache.TryGetValue( id, out assetBase ) )
247 m_hitCount++;
248
249 if( m_getCount == m_debugEpoch )
250 {
251 Log.InfoFormat( "[ASSET CACHE]: Cached = {0}, Get = {1}, Hits = {2}%, Size = {3} bytes, Avg. A. Size = {4} bytes",
252 m_cachedCount, m_getCount, ( (double) m_hitCount / m_getCount ) * 100.0, m_cache.Size, m_cache.Size / m_cache.Count );
253 m_getCount = 0;
254 m_hitCount = 0;
255 m_cachedCount = 0;
256 }
257
258 return assetBase;
259 }
260 #endregion
261
262 #region ISharedRegionModule Members
263 /// <summary>
264 /// Gets region module's name.
265 /// </summary>
266 public string Name
267 {
268 get { return "CenomeMemoryAssetCache"; }
269 }
270
271 /// <summary>
272 /// New region is being added to server.
273 /// </summary>
274 /// <param name="scene">
275 /// Region's scene.
276 /// </param>
277 public void AddRegion( Scene scene )
278 {
279 if( m_enabled )
280 scene.RegisterModuleInterface<IImprovedAssetCache>( this );
281 }
282
283 /// <summary>
284 /// Close region module.
285 /// </summary>
286 public void Close()
287 {
288 m_enabled = false;
289 m_cache.Clear();
290 m_cache = null;
291 }
292
293 /// <summary>
294 /// Initialize region module.
295 /// </summary>
296 /// <param name="source">
297 /// Configuration source.
298 /// </param>
299 public void Initialise( IConfigSource source )
300 {
301 m_cache = null;
302 m_enabled = false;
303
304 var moduleConfig = source.Configs[ "Modules" ];
305 if( moduleConfig == null )
306 return;
307
308 var name = moduleConfig.GetString( "AssetCaching" );
309 Log.DebugFormat( "[XXX] name = {0} (this module's name: {1}", name, Name );
310
311 if( name != Name )
312 return;
313
314 // This module is used
315 var maxSize = DefaultMaxSize;
316 var maxCount = DefaultMaxCount;
317 var expirationTime = DefaultExpirationTime;
318
319 var assetConfig = source.Configs[ "AssetCache" ];
320 if( assetConfig != null )
321 {
322 // Get optional configurations
323 maxSize = assetConfig.GetLong( "MaxSize", DefaultMaxSize );
324 maxCount = assetConfig.GetInt( "MaxCount", DefaultMaxCount );
325 expirationTime =
326 TimeSpan.FromMinutes( assetConfig.GetInt( "ExpirationTime", (int) DefaultExpirationTime.TotalMinutes ) );
327
328 // Debugging purposes only
329 m_debugEpoch = assetConfig.GetInt( "DebugEpoch", 0 );
330 }
331
332 Initialize( maxSize, maxCount, expirationTime );
333 }
334
335 /// <summary>
336 /// Initialization post handling.
337 /// </summary>
338 /// <remarks>
339 /// <para>
340 /// Modules can use this to initialize connection with other modules.
341 /// </para>
342 /// </remarks>
343 public void PostInitialise()
344 {
345 }
346
347 /// <summary>
348 /// Region has been loaded.
349 /// </summary>
350 /// <param name="scene">
351 /// Region's scene.
352 /// </param>
353 /// <remarks>
354 /// <para>
355 /// This is needed for all module types. Modules will register
356 /// Interfaces with scene in AddScene, and will also need a means
357 /// to access interfaces registered by other modules. Without
358 /// this extra method, a module attempting to use another modules'
359 /// interface would be successful only depending on load order,
360 /// which can't be depended upon, or modules would need to resort
361 /// to ugly kludges to attempt to request interfaces when needed
362 /// and unnecessary caching logic repeated in all modules.
363 /// The extra function stub is just that much cleaner.
364 /// </para>
365 /// </remarks>
366 public void RegionLoaded( Scene scene )
367 {
368 }
369
370 /// <summary>
371 /// Region is being removed.
372 /// </summary>
373 /// <param name="scene">
374 /// Region scene that is being removed.
375 /// </param>
376 public void RemoveRegion( Scene scene )
377 {
378 }
379 #endregion
380 }
381}
diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
index 393f340..9969ebe 100644
--- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
+++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
@@ -19,6 +19,7 @@
19 <RegionModule id="HGAssetBroker" type="OpenSim.Region.CoreModules.ServiceConnectors.Asset.HGAssetBroker" /> 19 <RegionModule id="HGAssetBroker" type="OpenSim.Region.CoreModules.ServiceConnectors.Asset.HGAssetBroker" />
20 <RegionModule id="CoreAssetCache" type="OpenSim.Region.CoreModules.Asset.CoreAssetCache" /> 20 <RegionModule id="CoreAssetCache" type="OpenSim.Region.CoreModules.Asset.CoreAssetCache" />
21 <RegionModule id="CoreAssetCache" type="OpenSim.Region.CoreModules.Asset.GlynnTuckerAssetCache" /> 21 <RegionModule id="CoreAssetCache" type="OpenSim.Region.CoreModules.Asset.GlynnTuckerAssetCache" />
22 <RegionModule id="CenomeMemoryAssetCache" type="OpenSim.Region.CoreModules.Asset.CenomeMemoryAssetCache"/>
22 <RegionModule id="UrlModule" type="OpenSim.Region.CoreModules.Scripting.LSLHttp.UrlModule" /> 23 <RegionModule id="UrlModule" type="OpenSim.Region.CoreModules.Scripting.LSLHttp.UrlModule" />
23 <RegionModule id="Chat" type="OpenSim.Region.CoreModules.Avatar.Chat.ChatModule" /> 24 <RegionModule id="Chat" type="OpenSim.Region.CoreModules.Avatar.Chat.ChatModule" />
24 </Extension> 25 </Extension>
diff --git a/bin/config-include/GridCommon.ini.example b/bin/config-include/GridCommon.ini.example
index 8a95776..d0f63c8 100644
--- a/bin/config-include/GridCommon.ini.example
+++ b/bin/config-include/GridCommon.ini.example
@@ -1,6 +1,7 @@
1[Modules] 1[Modules]
2 ;AssetCaching = "CoreAssetCache" 2 ;AssetCaching = "CoreAssetCache"
3 AssetCaching = "GlynnTuckerAssetCache" 3 AssetCaching = "GlynnTuckerAssetCache"
4 ;AssetCaching = "CenomeMemoryAssetCache"
4 5
5[AssetCache] 6[AssetCache]
6 ; Number of buckets for assets 7 ; Number of buckets for assets
@@ -16,3 +17,11 @@
16 ; 17 ;
17 AssetServerURI = "http://myassetserver.com:8003" 18 AssetServerURI = "http://myassetserver.com:8003"
18 19
20
21 ; Optional configurations for CenomeMemoryAssetCache
22 ; Cache size 128 MB (default: 134217728)
23 ; MaxSize = 134217728
24 ; Maximal asset count
25 ; MaxCount = 4096
26 ; Asset's expiration time (minutes)
27 ; ExpirationTime = 30
diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example
index d607092..d772256 100644
--- a/bin/config-include/StandaloneCommon.ini.example
+++ b/bin/config-include/StandaloneCommon.ini.example
@@ -1,6 +1,7 @@
1[Modules] 1[Modules]
2 ;AssetCaching = "CoreAssetCache" 2 ;AssetCaching = "CoreAssetCache"
3 AssetCaching = "GlynnTuckerAssetCache" 3 AssetCaching = "GlynnTuckerAssetCache"
4 ;AssetCaching = "CenomeMemoryAssetCache"
4 5
5[AssetCache] 6[AssetCache]
6 ; Number of buckets for assets 7 ; Number of buckets for assets
@@ -20,3 +21,10 @@
20 AssetLoaderArgs = "assets/AssetSets.xml" 21 AssetLoaderArgs = "assets/AssetSets.xml"
21 22
22 23
24 ; Optional configurations for CenomeMemoryAssetCache
25 ; Cache size 128 MB (default: 134217728)
26 ; MaxSize = 134217728
27 ; Maximal asset count
28 ; MaxCount = 4096
29 ; Asset's expiration time (minutes)
30 ; ExpirationTime = 30 \ No newline at end of file