aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Cache.cs
diff options
context:
space:
mode:
authorMelanie Thielker2008-08-07 16:40:50 +0000
committerMelanie Thielker2008-08-07 16:40:50 +0000
commitc4eac71e54544fb3dbf7283969a6e229225d9621 (patch)
tree7b8c34ceee4eed59ecf185be52e52b3e7e5251e7 /OpenSim/Framework/Cache.cs
parentPatch #9159 (diff)
downloadopensim-SC_OLD-c4eac71e54544fb3dbf7283969a6e229225d9621.zip
opensim-SC_OLD-c4eac71e54544fb3dbf7283969a6e229225d9621.tar.gz
opensim-SC_OLD-c4eac71e54544fb3dbf7283969a6e229225d9621.tar.bz2
opensim-SC_OLD-c4eac71e54544fb3dbf7283969a6e229225d9621.tar.xz
Committing first draft of the universal cache. This is by no means
finished, but it does work for memory caching items in aggressive mode. Supports several paramters, including TTL.
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Cache.cs467
1 files changed, 467 insertions, 0 deletions
diff --git a/OpenSim/Framework/Cache.cs b/OpenSim/Framework/Cache.cs
new file mode 100644
index 0000000..88b86a3
--- /dev/null
+++ b/OpenSim/Framework/Cache.cs
@@ -0,0 +1,467 @@
1using System;
2using System.Collections.Generic;
3using libsecondlife;
4
5namespace Opensim.Framework
6{
7 // The delegate we will use for performing fetch from backing store
8 //
9 public delegate Object FetchDelegate(LLUUID index);
10 public delegate bool ExpireDelegate(LLUUID index);
11
12 // Strategy
13 //
14 // Conservative = Minimize memory. Expire items quickly.
15 // Balanced = Expire items with few hits quickly.
16 // Aggressive = Keep cache full. Expire only when over 90% and adding
17 //
18 public enum CacheStrategy
19 {
20 Conservative = 0,
21 Balanced = 1,
22 Aggressive = 2
23 }
24
25 // Select classes to store data on different media
26 //
27 public enum CacheMedium
28 {
29 Memory = 0,
30 File = 1
31 }
32
33 public enum CacheFlags
34 {
35 CacheMissing = 1,
36 AllowUpdate = 2
37 }
38
39 // The base class of all cache objects. Implements comparison and sorting
40 // by the LLUUID member.
41 //
42 // This is not abstract because we need to instantiate it briefly as a
43 // method parameter
44 //
45 public class CacheItemBase : IEquatable<CacheItemBase>, IComparable<CacheItemBase>
46 {
47 public LLUUID uuid;
48 public DateTime entered;
49 public DateTime lastUsed;
50 public DateTime expires = new DateTime(0);
51 public int hits = 0;
52
53 public virtual Object Retrieve()
54 {
55 return null;
56 }
57
58 public virtual void Store(Object data)
59 {
60 }
61
62 public CacheItemBase(LLUUID index)
63 {
64 uuid = index;
65 entered = DateTime.Now;
66 lastUsed = entered;
67 }
68
69 public CacheItemBase(LLUUID index, DateTime ttl)
70 {
71 uuid = index;
72 entered = DateTime.Now;
73 lastUsed = entered;
74 expires = ttl;
75 }
76
77 public virtual bool Equals(CacheItemBase item)
78 {
79 return uuid == item.uuid;
80 }
81
82 public virtual int CompareTo(CacheItemBase item)
83 {
84 return uuid.CompareTo(item.uuid);
85 }
86
87 public virtual bool IsLocked()
88 {
89 return false;
90 }
91 }
92
93 // Simple in-memory storage. Boxes the object and stores it in a variable
94 //
95 public class MemoryCacheItem : CacheItemBase
96 {
97 private Object m_Data;
98
99 public MemoryCacheItem(LLUUID index) :
100 base(index)
101 {
102 }
103
104 public MemoryCacheItem(LLUUID index, DateTime ttl) :
105 base(index, ttl)
106 {
107 }
108
109 public MemoryCacheItem(LLUUID index, Object data) :
110 base(index)
111 {
112 Store(data);
113 }
114
115 public MemoryCacheItem(LLUUID index, DateTime ttl, Object data) :
116 base(index, ttl)
117 {
118 Store(data);
119 }
120
121 public override Object Retrieve()
122 {
123 return m_Data;
124 }
125
126 public override void Store(Object data)
127 {
128 m_Data = data;
129 }
130 }
131
132 // Simple persistent file storage
133 //
134 public class FileCacheItem : CacheItemBase
135 {
136 public FileCacheItem(LLUUID index) :
137 base(index)
138 {
139 }
140
141 public FileCacheItem(LLUUID index, DateTime ttl) :
142 base(index, ttl)
143 {
144 }
145
146 public FileCacheItem(LLUUID index, Object data) :
147 base(index)
148 {
149 Store(data);
150 }
151
152 public FileCacheItem(LLUUID index, DateTime ttl, Object data) :
153 base(index, ttl)
154 {
155 Store(data);
156 }
157
158 public override Object Retrieve()
159 {
160 //TODO: Add file access code
161 return null;
162 }
163
164 public override void Store(Object data)
165 {
166 //TODO: Add file access code
167 }
168 }
169
170 // The main cache class. This is the class you instantiate to create
171 // a cache
172 //
173 public class Cache
174 {
175 private List<CacheItemBase> m_Index = new List<CacheItemBase>();
176
177 private CacheStrategy m_Strategy;
178 private CacheMedium m_Medium;
179 private CacheFlags m_Flags = 0;
180 private int m_Size = 1024;
181 private TimeSpan m_DefaultTTL = new TimeSpan(0);
182 public ExpireDelegate OnExpire;
183
184 // Comparison interfaces
185 //
186 private class SortLRU : IComparer<CacheItemBase>
187 {
188 public int Compare(CacheItemBase a, CacheItemBase b)
189 {
190 if(a == null && b == null)
191 return 0;
192 if(a == null)
193 return -1;
194 if(b == null)
195 return 1;
196
197 return(a.lastUsed.CompareTo(b.lastUsed));
198 }
199 }
200
201 // Convenience constructors
202 //
203 public Cache()
204 {
205 m_Strategy = CacheStrategy.Balanced;
206 m_Medium = CacheMedium.Memory;
207 m_Flags = 0;
208 }
209
210 public Cache(CacheMedium medium) :
211 this(medium, CacheStrategy.Balanced)
212 {
213 }
214
215 public Cache(CacheMedium medium, CacheFlags flags) :
216 this(medium, CacheStrategy.Balanced, flags)
217 {
218 }
219
220 public Cache(CacheMedium medium, CacheStrategy strategy) :
221 this(medium, strategy, 0)
222 {
223 }
224
225 public Cache(CacheStrategy strategy, CacheFlags flags) :
226 this(CacheMedium.Memory, strategy, flags)
227 {
228 }
229
230 public Cache(CacheFlags flags) :
231 this(CacheMedium.Memory, CacheStrategy.Balanced, flags)
232 {
233 }
234
235 public Cache(CacheMedium medium, CacheStrategy strategy,
236 CacheFlags flags)
237 {
238 m_Strategy = strategy;
239 m_Medium = medium;
240 m_Flags = flags;
241 }
242
243 // Count of the items currently in cache
244 //
245 public int Count
246 {
247 get { lock(m_Index) { return m_Index.Count; } }
248 }
249
250 // Maximum number of items this cache will hold
251 //
252 public int Size
253 {
254 get { return m_Size; }
255 set { SetSize(value); }
256 }
257
258 private void SetSize(int newSize)
259 {
260 lock(m_Index)
261 {
262 if(Count <= Size)
263 return;
264
265 m_Index.Sort(new SortLRU());
266 m_Index.Reverse();
267
268 m_Index.RemoveRange(newSize, Count - newSize);
269 m_Size = newSize;
270 }
271 }
272
273 public TimeSpan DefaultTTL
274 {
275 get { return m_DefaultTTL; }
276 set { m_DefaultTTL = value; }
277 }
278
279 // Get an item from cache. Return the raw item, not it's data
280 //
281 protected virtual CacheItemBase GetItem(LLUUID index)
282 {
283 CacheItemBase item = null;
284
285 lock(m_Index)
286 {
287 item = m_Index.Find(delegate(CacheItemBase i)
288 {
289 if(i.uuid == index)
290 return true;
291 return false;
292 });
293 }
294
295 if(item == null)
296 {
297 Expire(true);
298 return null;
299 }
300
301 item.hits++;
302 item.lastUsed = DateTime.Now;
303
304 Expire(true);
305
306 return item;
307 }
308
309 // Get an item from cache. Do not try to fetch from source if not
310 // present. Just return null
311 //
312 public virtual Object Get(LLUUID index)
313 {
314 CacheItemBase item = GetItem(index);
315
316 if(item == null)
317 return null;
318
319 return item.Retrieve();
320 }
321
322 // Fetch an object from backing store if not cached, serve from
323 // cache if it is.
324 //
325 public virtual Object Get(LLUUID index, FetchDelegate fetch)
326 {
327 Object item = Get(index);
328 if(item != null)
329 return item;
330
331 Object data = fetch(index);
332 if(data == null)
333 {
334 if((m_Flags & CacheFlags.CacheMissing) != 0)
335 {
336 lock(m_Index)
337 {
338 CacheItemBase missing = new CacheItemBase(index);
339 if(!m_Index.Contains(missing))
340 m_Index.Add(missing);
341 }
342 }
343 return null;
344 }
345
346 Store(index, data);
347
348 return data;
349 }
350
351
352 public virtual void Store(LLUUID index, Object data)
353 {
354 Type container;
355
356 switch(m_Medium)
357 {
358 case CacheMedium.Memory:
359 container = typeof(MemoryCacheItem);
360 break;
361 case CacheMedium.File:
362 return;
363 default:
364 return;
365 }
366
367 Store(index, data, container);
368 }
369
370 public virtual void Store(LLUUID index, Object data, Type container)
371 {
372 Store(index, data, container, new Object[] { index });
373 }
374
375 public virtual void Store(LLUUID index, Object data, Type container,
376 Object[] parameters)
377 {
378 Expire(false);
379
380 CacheItemBase item;
381
382 lock(m_Index)
383 {
384 if(m_Index.Contains(new CacheItemBase(index)))
385 {
386 if((m_Flags & CacheFlags.AllowUpdate) != 0)
387 {
388 item = GetItem(index);
389
390 item.hits++;
391 item.lastUsed = DateTime.Now;
392 if(m_DefaultTTL.Ticks != 0)
393 item.expires = DateTime.Now + m_DefaultTTL;
394
395 item.Store(data);
396 }
397 return;
398 }
399
400 item = (CacheItemBase)Activator.CreateInstance(container,
401 parameters);
402
403 if(m_DefaultTTL.Ticks != 0)
404 item.expires = DateTime.Now + m_DefaultTTL;
405
406 m_Index.Add(item);
407 }
408 item.Store(data);
409 }
410
411 protected virtual void Expire(bool getting)
412 {
413 if(getting && (m_Strategy == CacheStrategy.Aggressive))
414 return;
415
416 if(m_DefaultTTL.Ticks != 0)
417 {
418 DateTime now= System.DateTime.Now;
419
420 foreach (CacheItemBase item in new List<CacheItemBase>(m_Index))
421 {
422 if(item.expires.Ticks == 0 ||
423 item.expires <= now)
424 m_Index.Remove(item);
425 }
426 }
427
428 switch (m_Strategy)
429 {
430 case CacheStrategy.Aggressive:
431 if(Count < Size)
432 return;
433
434 lock(m_Index)
435 {
436 m_Index.Sort(new SortLRU());
437 m_Index.Reverse();
438
439 int target = (int)((float)Size * 0.9);
440 if(target == Count) // Cover ridiculous cache sizes
441 return;
442
443 ExpireDelegate doExpire = OnExpire;
444
445 if(doExpire != null)
446 {
447 List<CacheItemBase> candidates =
448 m_Index.GetRange(target, Count - target);
449
450 foreach (CacheItemBase i in candidates)
451 {
452 if(doExpire(i.uuid))
453 m_Index.Remove(i);
454 }
455 }
456 else
457 {
458 m_Index.RemoveRange(target, Count - target);
459 }
460 }
461 break;
462 default:
463 break;
464 }
465 }
466 }
467}