aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework')
-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}