OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
Cache.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections.Generic;
30 using OpenMetaverse;
31 
32 namespace OpenSim.Framework
33 {
34  // The delegate we will use for performing fetch from backing store
35  //
36  public delegate Object FetchDelegate(string index);
37  public delegate bool ExpireDelegate(string index);
38 
39  // Strategy
40  //
41  // Conservative = Minimize memory. Expire items quickly.
42  // Balanced = Expire items with few hits quickly.
43  // Aggressive = Keep cache full. Expire only when over 90% and adding
44  //
45  public enum CacheStrategy
46  {
47  Conservative = 0,
48  Balanced = 1,
49  Aggressive = 2
50  }
51 
52  // Select classes to store data on different media
53  //
54  public enum CacheMedium
55  {
56  Memory = 0,
57  File = 1
58  }
59 
60  public enum CacheFlags
61  {
62  CacheMissing = 1,
63  AllowUpdate = 2
64  }
65 
66  // The base class of all cache objects. Implements comparison and sorting
67  // by the string member.
68  //
69  // This is not abstract because we need to instantiate it briefly as a
70  // method parameter
71  //
72  public class CacheItemBase : IEquatable<CacheItemBase>, IComparable<CacheItemBase>
73  {
74  public string uuid;
75  public DateTime entered;
76  public DateTime lastUsed;
77  public DateTime expires = new DateTime(0);
78  public int hits = 0;
79 
80  public virtual Object Retrieve()
81  {
82  return null;
83  }
84 
85  public virtual void Store(Object data)
86  {
87  }
88 
89  public CacheItemBase(string index)
90  {
91  uuid = index;
92  entered = DateTime.Now;
93  lastUsed = entered;
94  }
95 
96  public CacheItemBase(string index, DateTime ttl)
97  {
98  uuid = index;
99  entered = DateTime.Now;
100  lastUsed = entered;
101  expires = ttl;
102  }
103 
104  public virtual bool Equals(CacheItemBase item)
105  {
106  return uuid == item.uuid;
107  }
108 
109  public virtual int CompareTo(CacheItemBase item)
110  {
111  return uuid.CompareTo(item.uuid);
112  }
113 
114  public virtual bool IsLocked()
115  {
116  return false;
117  }
118  }
119 
120  // Simple in-memory storage. Boxes the object and stores it in a variable
121  //
123  {
124  private Object m_Data;
125 
126  public MemoryCacheItem(string index) :
127  base(index)
128  {
129  }
130 
131  public MemoryCacheItem(string index, DateTime ttl) :
132  base(index, ttl)
133  {
134  }
135 
136  public MemoryCacheItem(string index, Object data) :
137  base(index)
138  {
139  Store(data);
140  }
141 
142  public MemoryCacheItem(string index, DateTime ttl, Object data) :
143  base(index, ttl)
144  {
145  Store(data);
146  }
147 
148  public override Object Retrieve()
149  {
150  return m_Data;
151  }
152 
153  public override void Store(Object data)
154  {
155  m_Data = data;
156  }
157  }
158 
159  // Simple persistent file storage
160  //
162  {
163  public FileCacheItem(string index) :
164  base(index)
165  {
166  }
167 
168  public FileCacheItem(string index, DateTime ttl) :
169  base(index, ttl)
170  {
171  }
172 
173  public FileCacheItem(string index, Object data) :
174  base(index)
175  {
176  Store(data);
177  }
178 
179  public FileCacheItem(string index, DateTime ttl, Object data) :
180  base(index, ttl)
181  {
182  Store(data);
183  }
184 
185  public override Object Retrieve()
186  {
187  //TODO: Add file access code
188  return null;
189  }
190 
191  public override void Store(Object data)
192  {
193  //TODO: Add file access code
194  }
195  }
196 
197  // The main cache class. This is the class you instantiate to create
198  // a cache
199  //
200  public class Cache
201  {
205  private List<CacheItemBase> m_Index = new List<CacheItemBase>();
206 
210  private Dictionary<string, CacheItemBase> m_Lookup =
211  new Dictionary<string, CacheItemBase>();
212 
213  private CacheStrategy m_Strategy;
214  private CacheMedium m_Medium;
215  private CacheFlags m_Flags = 0;
216  private int m_Size = 1024;
217  private TimeSpan m_DefaultTTL = new TimeSpan(0);
219 
220  // Comparison interfaces
221  //
222  private class SortLRU : IComparer<CacheItemBase>
223  {
224  public int Compare(CacheItemBase a, CacheItemBase b)
225  {
226  if (a == null && b == null)
227  return 0;
228  if (a == null)
229  return -1;
230  if (b == null)
231  return 1;
232 
233  return(a.lastUsed.CompareTo(b.lastUsed));
234  }
235  }
236 
237  // Convenience constructors
238  //
239  public Cache()
240  {
241  m_Strategy = CacheStrategy.Balanced;
242  m_Medium = CacheMedium.Memory;
243  m_Flags = 0;
244  }
245 
246  public Cache(CacheMedium medium) :
247  this(medium, CacheStrategy.Balanced)
248  {
249  }
250 
251  public Cache(CacheMedium medium, CacheFlags flags) :
252  this(medium, CacheStrategy.Balanced, flags)
253  {
254  }
255 
256  public Cache(CacheMedium medium, CacheStrategy strategy) :
257  this(medium, strategy, 0)
258  {
259  }
260 
261  public Cache(CacheStrategy strategy, CacheFlags flags) :
262  this(CacheMedium.Memory, strategy, flags)
263  {
264  }
265 
266  public Cache(CacheFlags flags) :
268  {
269  }
270 
271  public Cache(CacheMedium medium, CacheStrategy strategy,
272  CacheFlags flags)
273  {
274  m_Strategy = strategy;
275  m_Medium = medium;
276  m_Flags = flags;
277  }
278 
279  // Count of the items currently in cache
280  //
281  public int Count
282  {
283  get { lock (m_Index) { return m_Index.Count; } }
284  }
285 
286  // Maximum number of items this cache will hold
287  //
288  public int Size
289  {
290  get { return m_Size; }
291  set { SetSize(value); }
292  }
293 
294  private void SetSize(int newSize)
295  {
296  lock (m_Index)
297  {
298  if (Count <= Size)
299  return;
300 
301  m_Index.Sort(new SortLRU());
302  m_Index.Reverse();
303 
304  m_Index.RemoveRange(newSize, Count - newSize);
305  m_Size = newSize;
306 
307  m_Lookup.Clear();
308 
309  foreach (CacheItemBase item in m_Index)
310  m_Lookup[item.uuid] = item;
311  }
312  }
313 
314  public TimeSpan DefaultTTL
315  {
316  get { return m_DefaultTTL; }
317  set { m_DefaultTTL = value; }
318  }
319 
320  // Get an item from cache. Return the raw item, not it's data
321  //
322  protected virtual CacheItemBase GetItem(string index)
323  {
324  CacheItemBase item = null;
325 
326  lock (m_Index)
327  {
328  if (m_Lookup.ContainsKey(index))
329  item = m_Lookup[index];
330 
331  if (item == null)
332  {
333  Expire(true);
334  return null;
335  }
336 
337  item.hits++;
338  item.lastUsed = DateTime.Now;
339 
340  Expire(true);
341  }
342 
343  return item;
344  }
345 
346  // Get an item from cache. Do not try to fetch from source if not
347  // present. Just return null
348  //
349  public virtual Object Get(string index)
350  {
351  CacheItemBase item = GetItem(index);
352 
353  if (item == null)
354  return null;
355 
356  return item.Retrieve();
357  }
358 
359  // Fetch an object from backing store if not cached, serve from
360  // cache if it is.
361  //
362  public virtual Object Get(string index, FetchDelegate fetch)
363  {
364  Object item = Get(index);
365  if (item != null)
366  return item;
367 
368  Object data = fetch(index);
369  if (data == null)
370  {
371  if ((m_Flags & CacheFlags.CacheMissing) != 0)
372  {
373  lock (m_Index)
374  {
375  CacheItemBase missing = new CacheItemBase(index);
376  if (!m_Index.Contains(missing))
377  {
378  m_Index.Add(missing);
379  m_Lookup[index] = missing;
380  }
381  }
382  }
383  return null;
384  }
385 
386  Store(index, data);
387 
388  return data;
389  }
390 
391  // Find an object in cache by delegate.
392  //
393  public Object Find(Predicate<CacheItemBase> d)
394  {
395  CacheItemBase item;
396 
397  lock (m_Index)
398  item = m_Index.Find(d);
399 
400  if (item == null)
401  return null;
402 
403  return item.Retrieve();
404  }
405 
406  public virtual void Store(string index, Object data)
407  {
408  Type container;
409 
410  switch (m_Medium)
411  {
412  case CacheMedium.Memory:
413  container = typeof(MemoryCacheItem);
414  break;
415  case CacheMedium.File:
416  return;
417  default:
418  return;
419  }
420 
421  Store(index, data, container);
422  }
423 
424  public virtual void Store(string index, Object data, Type container)
425  {
426  Store(index, data, container, new Object[] { index });
427  }
428 
429  public virtual void Store(string index, Object data, Type container,
430  Object[] parameters)
431  {
432  CacheItemBase item;
433 
434  lock (m_Index)
435  {
436  Expire(false);
437 
438  if (m_Index.Contains(new CacheItemBase(index)))
439  {
440  if ((m_Flags & CacheFlags.AllowUpdate) != 0)
441  {
442  item = GetItem(index);
443 
444  item.hits++;
445  item.lastUsed = DateTime.Now;
446  if (m_DefaultTTL.Ticks != 0)
447  item.expires = DateTime.Now + m_DefaultTTL;
448 
449  item.Store(data);
450  }
451  return;
452  }
453 
454  item = (CacheItemBase)Activator.CreateInstance(container,
455  parameters);
456 
457  if (m_DefaultTTL.Ticks != 0)
458  item.expires = DateTime.Now + m_DefaultTTL;
459 
460  m_Index.Add(item);
461  m_Lookup[index] = item;
462  }
463 
464  item.Store(data);
465  }
466 
474  protected virtual void Expire(bool getting)
475  {
476  if (getting && (m_Strategy == CacheStrategy.Aggressive))
477  return;
478 
479  if (m_DefaultTTL.Ticks != 0)
480  {
481  DateTime now= DateTime.Now;
482 
483  foreach (CacheItemBase item in new List<CacheItemBase>(m_Index))
484  {
485  if (item.expires.Ticks == 0 ||
486  item.expires <= now)
487  {
488  m_Index.Remove(item);
489  m_Lookup.Remove(item.uuid);
490  }
491  }
492  }
493 
494  switch (m_Strategy)
495  {
496  case CacheStrategy.Aggressive:
497  if (Count < Size)
498  return;
499 
500  m_Index.Sort(new SortLRU());
501  m_Index.Reverse();
502 
503  int target = (int)((float)Size * 0.9);
504  if (target == Count) // Cover ridiculous cache sizes
505  return;
506 
507  ExpireDelegate doExpire = OnExpire;
508 
509  if (doExpire != null)
510  {
511  List<CacheItemBase> candidates =
512  m_Index.GetRange(target, Count - target);
513 
514  foreach (CacheItemBase i in candidates)
515  {
516  if (doExpire(i.uuid))
517  {
518  m_Index.Remove(i);
519  m_Lookup.Remove(i.uuid);
520  }
521  }
522  }
523  else
524  {
525  m_Index.RemoveRange(target, Count - target);
526 
527  m_Lookup.Clear();
528 
529  foreach (CacheItemBase item in m_Index)
530  m_Lookup[item.uuid] = item;
531  }
532 
533  break;
534 
535  default:
536  break;
537  }
538  }
539 
540  public void Invalidate(string uuid)
541  {
542  lock (m_Index)
543  {
544  if (!m_Lookup.ContainsKey(uuid))
545  return;
546 
547  CacheItemBase item = m_Lookup[uuid];
548  m_Lookup.Remove(uuid);
549  m_Index.Remove(item);
550  }
551  }
552 
553  public void Clear()
554  {
555  lock (m_Index)
556  {
557  m_Index.Clear();
558  m_Lookup.Clear();
559  }
560  }
561  }
562 }
virtual void Store(string index, Object data, Type container, Object[] parameters)
Definition: Cache.cs:429
virtual void Store(string index, Object data)
Definition: Cache.cs:406
override Object Retrieve()
Definition: Cache.cs:148
delegate Object FetchDelegate(string index)
void Invalidate(string uuid)
Definition: Cache.cs:540
MemoryCacheItem(string index)
Definition: Cache.cs:126
override Object Retrieve()
Definition: Cache.cs:185
FileCacheItem(string index, DateTime ttl, Object data)
Definition: Cache.cs:179
override void Store(Object data)
Definition: Cache.cs:191
virtual void Store(Object data)
Definition: Cache.cs:85
virtual Object Retrieve()
Definition: Cache.cs:80
FileCacheItem(string index, DateTime ttl)
Definition: Cache.cs:168
Object Find(Predicate< CacheItemBase > d)
Definition: Cache.cs:393
Cache(CacheMedium medium, CacheFlags flags)
Definition: Cache.cs:251
virtual bool Equals(CacheItemBase item)
Definition: Cache.cs:104
delegate bool ExpireDelegate(string index)
CacheItemBase(string index, DateTime ttl)
Definition: Cache.cs:96
virtual bool IsLocked()
Definition: Cache.cs:114
override void Store(Object data)
Definition: Cache.cs:153
virtual void Store(string index, Object data, Type container)
Definition: Cache.cs:424
MemoryCacheItem(string index, DateTime ttl)
Definition: Cache.cs:131
MemoryCacheItem(string index, Object data)
Definition: Cache.cs:136
FileCacheItem(string index)
Definition: Cache.cs:163
FileCacheItem(string index, Object data)
Definition: Cache.cs:173
ExpireDelegate OnExpire
Definition: Cache.cs:218
CacheItemBase(string index)
Definition: Cache.cs:89
virtual Object Get(string index)
Definition: Cache.cs:349
Cache(CacheFlags flags)
Definition: Cache.cs:266
Cache(CacheMedium medium, CacheStrategy strategy, CacheFlags flags)
Definition: Cache.cs:271
Cache(CacheMedium medium)
Definition: Cache.cs:246
MemoryCacheItem(string index, DateTime ttl, Object data)
Definition: Cache.cs:142
virtual Object Get(string index, FetchDelegate fetch)
Definition: Cache.cs:362
virtual int CompareTo(CacheItemBase item)
Definition: Cache.cs:109
Cache(CacheMedium medium, CacheStrategy strategy)
Definition: Cache.cs:256
virtual CacheItemBase GetItem(string index)
Definition: Cache.cs:322
virtual void Expire(bool getting)
Expire items as appropriate.
Definition: Cache.cs:474
Cache(CacheStrategy strategy, CacheFlags flags)
Definition: Cache.cs:261