diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionInfoCache.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionInfoCache.cs | 965 |
1 files changed, 897 insertions, 68 deletions
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionInfoCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionInfoCache.cs index be8a9a2..f6fff58 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionInfoCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RegionInfoCache.cs | |||
@@ -26,6 +26,8 @@ | |||
26 | */ | 26 | */ |
27 | using System; | 27 | using System; |
28 | using System.Reflection; | 28 | using System.Reflection; |
29 | using System.Threading; | ||
30 | using System.Runtime.InteropServices; | ||
29 | using System.Collections.Generic; | 31 | using System.Collections.Generic; |
30 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
31 | using OpenSim.Services.Interfaces; | 33 | using OpenSim.Services.Interfaces; |
@@ -37,82 +39,68 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid | |||
37 | { | 39 | { |
38 | public class RegionInfoCache | 40 | public class RegionInfoCache |
39 | { | 41 | { |
40 | private const double CACHE_EXPIRATION_SECONDS = 300.0; // 5 minutes | 42 | private const float CACHE_EXPIRATION_SECONDS = 120; // 2 minutes opensim regions change a lot |
41 | 43 | ||
42 | // private static readonly ILog m_log = | 44 | // private static readonly ILog m_log = |
43 | // LogManager.GetLogger( | 45 | // LogManager.GetLogger( |
44 | // MethodBase.GetCurrentMethod().DeclaringType); | 46 | // MethodBase.GetCurrentMethod().DeclaringType); |
45 | 47 | ||
46 | internal struct ScopedRegionUUID | 48 | private static RegionsExpiringCache m_Cache; |
49 | private int numberInstances; | ||
50 | |||
51 | public RegionInfoCache() | ||
47 | { | 52 | { |
48 | public UUID m_scopeID; | 53 | if(m_Cache == null) |
49 | public UUID m_regionID; | 54 | m_Cache = new RegionsExpiringCache(); |
50 | public ScopedRegionUUID(UUID scopeID, UUID regionID) | 55 | numberInstances++; |
51 | { | ||
52 | m_scopeID = scopeID; | ||
53 | m_regionID = regionID; | ||
54 | } | ||
55 | } | 56 | } |
56 | 57 | ||
57 | internal struct ScopedRegionName | 58 | public void Cache(GridRegion rinfo) |
58 | { | 59 | { |
59 | public UUID m_scopeID; | 60 | if (rinfo != null) |
60 | public string m_name; | 61 | this.Cache(rinfo.ScopeID, rinfo); |
61 | public ScopedRegionName(UUID scopeID, string name) | ||
62 | { | ||
63 | m_scopeID = scopeID; | ||
64 | m_name = name; | ||
65 | } | ||
66 | } | 62 | } |
67 | 63 | ||
68 | internal struct ScopedRegionPosition | 64 | public void Cache(UUID scopeID, GridRegion rinfo) |
69 | { | 65 | { |
70 | public UUID m_scopeID; | 66 | if (rinfo == null) |
71 | public ulong m_regionHandle; | 67 | return; |
72 | public ScopedRegionPosition(UUID scopeID, ulong handle) | ||
73 | { | ||
74 | m_scopeID = scopeID; | ||
75 | m_regionHandle = handle; | ||
76 | } | ||
77 | } | ||
78 | 68 | ||
79 | private ExpiringCache<ScopedRegionUUID, GridRegion> m_UUIDCache; | 69 | m_Cache.AddOrUpdate(scopeID, rinfo, CACHE_EXPIRATION_SECONDS); |
80 | private ExpiringCache<ScopedRegionName, ScopedRegionUUID> m_NameCache; | 70 | } |
81 | private ExpiringCache<ScopedRegionPosition, GridRegion> m_PositionCache; | ||
82 | 71 | ||
83 | public RegionInfoCache() | 72 | public void CacheLocal(GridRegion rinfo) |
84 | { | 73 | { |
85 | m_UUIDCache = new ExpiringCache<ScopedRegionUUID, GridRegion>(); | 74 | if (rinfo == null) |
86 | m_NameCache = new ExpiringCache<ScopedRegionName, ScopedRegionUUID>(); | 75 | return; |
87 | m_PositionCache = new ExpiringCache<ScopedRegionPosition, GridRegion>(); | 76 | |
77 | m_Cache.AddOrUpdate(rinfo.ScopeID, rinfo, 1e7f); | ||
88 | } | 78 | } |
89 | 79 | ||
90 | public void Cache(GridRegion rinfo) | 80 | public void CacheNearNeighbour(UUID scopeID, GridRegion rinfo) |
91 | { | 81 | { |
92 | if (rinfo != null) | 82 | if (rinfo == null) |
93 | this.Cache(rinfo.ScopeID,rinfo.RegionID,rinfo); | 83 | return; |
84 | |||
85 | m_Cache.AddOrUpdate(scopeID, rinfo, CACHE_EXPIRATION_SECONDS); | ||
94 | } | 86 | } |
95 | 87 | ||
96 | public void Cache(UUID scopeID, UUID regionID, GridRegion rinfo) | 88 | public void Cache(UUID scopeID, GridRegion rinfo, float expireSeconds) |
97 | { | 89 | { |
98 | // for now, do not cache negative results; this is because | ||
99 | // we need to figure out how to handle regions coming online | ||
100 | // in a timely way | ||
101 | if (rinfo == null) | 90 | if (rinfo == null) |
102 | return; | 91 | return; |
103 | |||
104 | ScopedRegionUUID id = new ScopedRegionUUID(scopeID,regionID); | ||
105 | |||
106 | // Cache even null accounts | ||
107 | m_UUIDCache.AddOrUpdate(id, rinfo, CACHE_EXPIRATION_SECONDS); | ||
108 | if (rinfo != null) | ||
109 | { | ||
110 | ScopedRegionName name = new ScopedRegionName(scopeID,rinfo.RegionName); | ||
111 | m_NameCache.AddOrUpdate(name, id, CACHE_EXPIRATION_SECONDS); | ||
112 | 92 | ||
113 | ScopedRegionPosition pos = new ScopedRegionPosition(scopeID, rinfo.RegionHandle); | 93 | m_Cache.AddOrUpdate(scopeID, rinfo, expireSeconds); |
114 | m_PositionCache.AddOrUpdate(pos, rinfo, CACHE_EXPIRATION_SECONDS); | 94 | } |
115 | } | 95 | |
96 | public void Remove(UUID scopeID, GridRegion rinfo) | ||
97 | { | ||
98 | m_Cache.Remove(scopeID, rinfo); | ||
99 | } | ||
100 | |||
101 | public void Remove(UUID scopeID, ulong regionHandle) | ||
102 | { | ||
103 | m_Cache.Remove(scopeID, regionHandle); | ||
116 | } | 104 | } |
117 | 105 | ||
118 | public GridRegion Get(UUID scopeID, UUID regionID, out bool inCache) | 106 | public GridRegion Get(UUID scopeID, UUID regionID, out bool inCache) |
@@ -120,8 +108,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid | |||
120 | inCache = false; | 108 | inCache = false; |
121 | 109 | ||
122 | GridRegion rinfo = null; | 110 | GridRegion rinfo = null; |
123 | ScopedRegionUUID id = new ScopedRegionUUID(scopeID,regionID); | 111 | if (m_Cache.TryGetValue(scopeID, regionID, out rinfo)) |
124 | if (m_UUIDCache.TryGetValue(id, out rinfo)) | ||
125 | { | 112 | { |
126 | inCache = true; | 113 | inCache = true; |
127 | return rinfo; | 114 | return rinfo; |
@@ -135,8 +122,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid | |||
135 | inCache = false; | 122 | inCache = false; |
136 | 123 | ||
137 | GridRegion rinfo = null; | 124 | GridRegion rinfo = null; |
138 | ScopedRegionPosition pos = new ScopedRegionPosition(scopeID, handle); | 125 | if (m_Cache.TryGetValue(scopeID, handle, out rinfo)) |
139 | if (m_PositionCache.TryGetValue(pos, out rinfo)) | ||
140 | { | 126 | { |
141 | inCache = true; | 127 | inCache = true; |
142 | return rinfo; | 128 | return rinfo; |
@@ -145,25 +131,868 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid | |||
145 | return null; | 131 | return null; |
146 | } | 132 | } |
147 | 133 | ||
148 | |||
149 | public GridRegion Get(UUID scopeID, string name, out bool inCache) | 134 | public GridRegion Get(UUID scopeID, string name, out bool inCache) |
150 | { | 135 | { |
151 | inCache = false; | 136 | inCache = false; |
152 | 137 | ||
153 | ScopedRegionName sname = new ScopedRegionName(scopeID,name); | 138 | GridRegion rinfo = null; |
139 | if (m_Cache.TryGetValue(scopeID, name, out rinfo)) | ||
140 | { | ||
141 | inCache = true; | ||
142 | return rinfo; | ||
143 | } | ||
144 | |||
145 | return null; | ||
146 | } | ||
147 | |||
148 | public GridRegion Get(UUID scopeID, uint x, uint y, out bool inCache) | ||
149 | { | ||
150 | inCache = false; | ||
151 | |||
152 | GridRegion rinfo = null; | ||
153 | if (m_Cache.TryGetValue(scopeID, x, y, out rinfo)) | ||
154 | { | ||
155 | inCache = true; | ||
156 | return rinfo; | ||
157 | } | ||
158 | |||
159 | return null; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | // dont care about endianess | ||
164 | [StructLayout(LayoutKind.Explicit, Size = 8, Pack = 8)] | ||
165 | public class fastRegionHandle | ||
166 | { | ||
167 | [FieldOffset(0)] public ulong handle; | ||
168 | [FieldOffset(0)] public uint y; | ||
169 | [FieldOffset(4)] public uint x; | ||
170 | |||
171 | public fastRegionHandle(ulong h) | ||
172 | { | ||
173 | handle = h; | ||
174 | } | ||
175 | |||
176 | public fastRegionHandle(uint px, uint py) | ||
177 | { | ||
178 | y = py & 0xffffff00; | ||
179 | x = px & 0xffffff00; | ||
180 | } | ||
181 | // actually do care | ||
182 | public ulong toHandle() | ||
183 | { | ||
184 | if(BitConverter.IsLittleEndian) | ||
185 | return handle; | ||
186 | return (ulong) x << 32 | (ulong)y ; | ||
187 | } | ||
188 | |||
189 | public static bool operator ==(fastRegionHandle value1, fastRegionHandle value2) | ||
190 | { | ||
191 | return value1.handle == value2.handle; | ||
192 | } | ||
193 | public static bool operator !=(fastRegionHandle value1, fastRegionHandle value2) | ||
194 | { | ||
195 | return value1.handle != value2.handle; | ||
196 | } | ||
197 | public override int GetHashCode() | ||
198 | { | ||
199 | return handle.GetHashCode(); | ||
200 | } | ||
201 | public override bool Equals(Object obj) | ||
202 | { | ||
203 | if(obj == null) | ||
204 | return false; | ||
205 | fastRegionHandle p = obj as fastRegionHandle; | ||
206 | return p.handle == handle; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | [StructLayout(LayoutKind.Explicit, Size = 8, Pack = 8)] | ||
212 | public class regionHandle | ||
213 | { | ||
214 | [FieldOffset(0)] private ulong handle; | ||
215 | [FieldOffset(0)] public uint a; | ||
216 | [FieldOffset(4)] public uint b; | ||
217 | |||
218 | public regionHandle(ulong h) | ||
219 | { | ||
220 | handle = h; | ||
221 | } | ||
222 | |||
223 | public regionHandle(uint px, uint py) | ||
224 | { | ||
225 | if(BitConverter.IsLittleEndian) | ||
226 | { | ||
227 | a = py & 0xffffff00; | ||
228 | b = px & 0xffffff00; | ||
229 | } | ||
230 | else | ||
231 | { | ||
232 | a = px & 0xffffff00; | ||
233 | b = py & 0xffffff00; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | public uint x | ||
238 | { | ||
239 | get | ||
240 | { | ||
241 | if(BitConverter.IsLittleEndian) | ||
242 | return b; | ||
243 | return a; | ||
244 | } | ||
245 | set | ||
246 | { | ||
247 | if(BitConverter.IsLittleEndian) | ||
248 | b = value & 0xffffff00; | ||
249 | else | ||
250 | a = value & 0xffffff00; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | public uint y | ||
255 | { | ||
256 | get | ||
257 | { | ||
258 | if(BitConverter.IsLittleEndian) | ||
259 | return a; | ||
260 | return b; | ||
261 | } | ||
262 | set | ||
263 | { | ||
264 | if(BitConverter.IsLittleEndian) | ||
265 | a = value; | ||
266 | else | ||
267 | b = value; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | public static bool operator ==(regionHandle value1, regionHandle value2) | ||
272 | { | ||
273 | return value1.handle == value2.handle; | ||
274 | } | ||
275 | public static bool operator !=(regionHandle value1, regionHandle value2) | ||
276 | { | ||
277 | return value1.handle != value2.handle; | ||
278 | } | ||
279 | public override int GetHashCode() | ||
280 | { | ||
281 | return handle.GetHashCode(); | ||
282 | } | ||
283 | public override bool Equals(Object obj) | ||
284 | { | ||
285 | if(obj == null) | ||
286 | return false; | ||
287 | regionHandle p = obj as regionHandle; | ||
288 | return p.handle == handle; | ||
289 | } | ||
290 | } | ||
291 | */ | ||
292 | |||
293 | public class RegionInfoForScope | ||
294 | { | ||
295 | public const ulong HANDLEMASK = 0xffffff00ffffff00ul; | ||
296 | public const ulong HANDLECOORDMASK = 0xffffff00ul; | ||
297 | |||
298 | private Dictionary<ulong, GridRegion> storage; | ||
299 | private Dictionary<ulong, DateTime> expires; | ||
300 | private Dictionary<string, ulong> byname; | ||
301 | private Dictionary<UUID, ulong> byuuid; | ||
302 | // includes handles to the inside of large regions | ||
303 | private Dictionary<ulong, ulong> innerHandles = new Dictionary<ulong, ulong>(); | ||
304 | |||
305 | public RegionInfoForScope() | ||
306 | { | ||
307 | storage = new Dictionary<ulong, GridRegion>(); | ||
308 | expires = new Dictionary<ulong, DateTime>(); | ||
309 | byname = new Dictionary<string, ulong>(); | ||
310 | byuuid = new Dictionary<UUID, ulong>(); | ||
311 | } | ||
312 | |||
313 | public RegionInfoForScope(GridRegion region, DateTime expire) | ||
314 | { | ||
315 | storage = new Dictionary<ulong, GridRegion>(); | ||
316 | expires = new Dictionary<ulong, DateTime>(); | ||
317 | byname = new Dictionary<string, ulong>(); | ||
318 | byuuid = new Dictionary<UUID, ulong>(); | ||
319 | |||
320 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
321 | storage[handle] = region; | ||
322 | expires[handle] = expire; | ||
323 | byname[region.RegionName] = handle; | ||
324 | byuuid[region.RegionID] = handle; | ||
325 | addToInner(region); | ||
326 | } | ||
327 | |||
328 | public void Add(GridRegion region, DateTime expire) | ||
329 | { | ||
330 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
331 | |||
332 | if(storage != null && storage.ContainsKey(handle)) | ||
333 | return; | ||
334 | |||
335 | if(storage == null) | ||
336 | storage = new Dictionary<ulong, GridRegion>(); | ||
337 | if(expires == null) | ||
338 | expires = new Dictionary<ulong, DateTime>(); | ||
339 | if(byname == null) | ||
340 | byname = new Dictionary<string, ulong>(); | ||
341 | if(byuuid == null) | ||
342 | byuuid = new Dictionary<UUID, ulong>(); | ||
343 | |||
344 | storage[handle] = region; | ||
345 | expires[handle] = expire; | ||
346 | byname[region.RegionName] = handle; | ||
347 | byuuid[region.RegionID] = handle; | ||
348 | |||
349 | addToInner(region); | ||
350 | } | ||
351 | |||
352 | public void AddUpdate(GridRegion region, DateTime expire) | ||
353 | { | ||
354 | if(storage == null) | ||
355 | storage = new Dictionary<ulong, GridRegion>(); | ||
356 | if(expires == null) | ||
357 | expires = new Dictionary<ulong, DateTime>(); | ||
358 | if(byname == null) | ||
359 | byname = new Dictionary<string, ulong>(); | ||
360 | if(byuuid == null) | ||
361 | byuuid = new Dictionary<UUID, ulong>(); | ||
362 | |||
363 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
154 | 364 | ||
155 | ScopedRegionUUID id; | 365 | if(expires.ContainsKey(handle)) |
156 | if (m_NameCache.TryGetValue(sname, out id)) | ||
157 | { | 366 | { |
158 | GridRegion rinfo = null; | 367 | if(expires[handle] < expire) |
159 | if (m_UUIDCache.TryGetValue(id, out rinfo)) | 368 | expires[handle] = expire; |
369 | if(storage.ContainsKey(handle)) | ||
160 | { | 370 | { |
161 | inCache = true; | 371 | GridRegion oldr = storage[handle]; |
162 | return rinfo; | 372 | if (oldr.RegionSizeX != region.RegionSizeX |
373 | || oldr.RegionSizeY != region.RegionSizeY) | ||
374 | { | ||
375 | removeFromInner(oldr); | ||
376 | addToInner(region); | ||
377 | } | ||
163 | } | 378 | } |
164 | } | 379 | } |
165 | 380 | else | |
381 | { | ||
382 | expires[handle] = expire; | ||
383 | addToInner(region); | ||
384 | } | ||
385 | storage[handle] = region; | ||
386 | byname[region.RegionName] = handle; | ||
387 | byuuid[region.RegionID] = handle; | ||
388 | } | ||
389 | |||
390 | public void Remove(GridRegion region) | ||
391 | { | ||
392 | if(region == null) | ||
393 | return; | ||
394 | |||
395 | if(byname != null) | ||
396 | byname.Remove(region.RegionName); | ||
397 | if(byuuid != null) | ||
398 | byuuid.Remove(region.RegionID); | ||
399 | |||
400 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
401 | if(storage != null) | ||
402 | { | ||
403 | if(storage.ContainsKey(handle)) | ||
404 | { | ||
405 | storage[handle] = null; | ||
406 | storage.Remove(handle); | ||
407 | } | ||
408 | } | ||
409 | removeFromInner(region); | ||
410 | if(expires != null) | ||
411 | { | ||
412 | expires.Remove(handle); | ||
413 | if(expires.Count == 0) | ||
414 | Clear(); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | public void Remove(ulong handle) | ||
419 | { | ||
420 | handle &= HANDLEMASK; | ||
421 | |||
422 | if(storage != null) | ||
423 | { | ||
424 | if(storage.ContainsKey(handle)) | ||
425 | { | ||
426 | GridRegion r = storage[handle]; | ||
427 | if(byname != null) | ||
428 | byname.Remove(r.RegionName); | ||
429 | if(byuuid != null) | ||
430 | byuuid.Remove(r.RegionID); | ||
431 | removeFromInner(r); | ||
432 | storage[handle] = null; | ||
433 | } | ||
434 | storage.Remove(handle); | ||
435 | } | ||
436 | if(expires != null) | ||
437 | { | ||
438 | expires.Remove(handle); | ||
439 | if(expires.Count == 0) | ||
440 | Clear(); | ||
441 | } | ||
442 | } | ||
443 | |||
444 | public void Clear() | ||
445 | { | ||
446 | if(expires != null) | ||
447 | expires.Clear(); | ||
448 | if(storage != null) | ||
449 | storage.Clear(); | ||
450 | if(byname != null) | ||
451 | byname.Clear(); | ||
452 | if(byuuid != null) | ||
453 | byuuid.Clear(); | ||
454 | byname = null; | ||
455 | byuuid = null; | ||
456 | storage = null; | ||
457 | expires = null; | ||
458 | innerHandles.Clear(); | ||
459 | } | ||
460 | |||
461 | public bool Contains(GridRegion region) | ||
462 | { | ||
463 | if(storage == null) | ||
464 | return false; | ||
465 | if(region == null) | ||
466 | return false; | ||
467 | |||
468 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
469 | return storage.ContainsKey(handle); | ||
470 | } | ||
471 | |||
472 | public bool Contains(ulong handle) | ||
473 | { | ||
474 | if(storage == null) | ||
475 | return false; | ||
476 | |||
477 | handle &= HANDLEMASK; | ||
478 | return storage.ContainsKey(handle); | ||
479 | } | ||
480 | |||
481 | public GridRegion get(ulong handle) | ||
482 | { | ||
483 | if(storage == null) | ||
484 | return null; | ||
485 | |||
486 | handle &= HANDLEMASK; | ||
487 | if(storage.ContainsKey(handle)) | ||
488 | return storage[handle]; | ||
489 | |||
490 | if(!innerHandles.ContainsKey(handle)) | ||
491 | return null; | ||
492 | |||
493 | ulong rhandle = innerHandles[handle]; | ||
494 | if(storage.ContainsKey(rhandle)) | ||
495 | return storage[rhandle]; | ||
496 | |||
166 | return null; | 497 | return null; |
167 | } | 498 | } |
499 | |||
500 | public GridRegion get(string name) | ||
501 | { | ||
502 | if(byname == null || !byname.ContainsKey(name)) | ||
503 | return null; | ||
504 | |||
505 | ulong handle = byname[name]; | ||
506 | if(storage.ContainsKey(handle)) | ||
507 | return storage[handle]; | ||
508 | return null; | ||
509 | } | ||
510 | |||
511 | public GridRegion get(UUID id) | ||
512 | { | ||
513 | if(byuuid == null || !byuuid.ContainsKey(id)) | ||
514 | return null; | ||
515 | |||
516 | ulong handle = byuuid[id]; | ||
517 | if(storage.ContainsKey(handle)) | ||
518 | return storage[handle]; | ||
519 | return null; | ||
520 | } | ||
521 | |||
522 | public GridRegion get(uint x, uint y) | ||
523 | { | ||
524 | if(storage == null) | ||
525 | return null; | ||
526 | |||
527 | // look for a handle first this should find normal size regions | ||
528 | ulong handle = (ulong)x & HANDLECOORDMASK; | ||
529 | handle <<= 32; | ||
530 | handle |= ((ulong)y & HANDLECOORDMASK); | ||
531 | |||
532 | if(storage.ContainsKey(handle)) | ||
533 | return storage[handle]; | ||
534 | |||
535 | if(!innerHandles.ContainsKey(handle)) | ||
536 | return null; | ||
537 | |||
538 | ulong rhandle = innerHandles[handle]; | ||
539 | if(!storage.ContainsKey(rhandle)) | ||
540 | return null; | ||
541 | |||
542 | GridRegion r = storage[rhandle]; | ||
543 | if(r == null) | ||
544 | return null; | ||
545 | |||
546 | // extra check, possible redundant | ||
547 | |||
548 | int test = r.RegionLocX; | ||
549 | if(x < test) | ||
550 | return null; | ||
551 | test += r.RegionSizeX; | ||
552 | if(x >= test) | ||
553 | return null; | ||
554 | test = r.RegionLocY; | ||
555 | if (y < test) | ||
556 | return null; | ||
557 | test += r.RegionSizeY; | ||
558 | if (y < test) | ||
559 | return r; | ||
560 | |||
561 | /* | ||
562 | // next do the harder work | ||
563 | foreach(KeyValuePair<ulong, GridRegion> kvp in storage) | ||
564 | { | ||
565 | GridRegion r = kvp.Value; | ||
566 | if(r == null) // ?? | ||
567 | continue; | ||
568 | |||
569 | int test = r.RegionLocX; | ||
570 | if(x < test) | ||
571 | continue; | ||
572 | test += r.RegionSizeX; | ||
573 | if(x >= test) | ||
574 | continue; | ||
575 | test = r.RegionLocY; | ||
576 | if (y < test) | ||
577 | continue; | ||
578 | test += r.RegionSizeY; | ||
579 | if (y < test) | ||
580 | return r; | ||
581 | } | ||
582 | */ | ||
583 | return null; | ||
584 | } | ||
585 | |||
586 | public int expire(DateTime now ) | ||
587 | { | ||
588 | if(expires == null || expires.Count == 0) | ||
589 | return 0; | ||
590 | |||
591 | int expiresCount = expires.Count; | ||
592 | List<ulong> toexpire = new List<ulong>(); | ||
593 | |||
594 | foreach(KeyValuePair<ulong, DateTime> kvp in expires) | ||
595 | { | ||
596 | if(kvp.Value < now) | ||
597 | toexpire.Add(kvp.Key); | ||
598 | } | ||
599 | |||
600 | int toexpireCount = toexpire.Count; | ||
601 | if(toexpireCount == 0) | ||
602 | return expiresCount; | ||
603 | |||
604 | if(toexpireCount == expiresCount) | ||
605 | { | ||
606 | Clear(); | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | if(storage != null) | ||
611 | { | ||
612 | ulong h; | ||
613 | for(int i = 0; i < toexpireCount; i++) | ||
614 | { | ||
615 | h = toexpire[i]; | ||
616 | if(storage.ContainsKey(h)) | ||
617 | { | ||
618 | GridRegion r = storage[h]; | ||
619 | if(byname != null) | ||
620 | byname.Remove(r.RegionName); | ||
621 | if(byuuid != null) | ||
622 | byuuid.Remove(r.RegionID); | ||
623 | removeFromInner(r); | ||
624 | |||
625 | storage[h] = null; | ||
626 | storage.Remove(h); | ||
627 | } | ||
628 | if(expires != null) | ||
629 | expires.Remove(h); | ||
630 | } | ||
631 | } | ||
632 | else | ||
633 | { | ||
634 | Clear(); | ||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | expiresCount = expires.Count; | ||
639 | if(expiresCount == 0) | ||
640 | { | ||
641 | byname = null; | ||
642 | byuuid = null; | ||
643 | storage = null; | ||
644 | expires = null; | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | return expiresCount; | ||
649 | } | ||
650 | |||
651 | public int Count() | ||
652 | { | ||
653 | if(byname == null) | ||
654 | return 0; | ||
655 | else | ||
656 | return byname.Count; | ||
657 | } | ||
658 | |||
659 | private void addToInner(GridRegion region) | ||
660 | { | ||
661 | int rsx = region.RegionSizeX; | ||
662 | int rsy = region.RegionSizeY; | ||
663 | |||
664 | if(rsx < 512 && rsy < 512) | ||
665 | return; | ||
666 | |||
667 | rsx >>= 8; | ||
668 | rsy >>= 8; | ||
669 | |||
670 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
671 | fastRegionHandle fh = new fastRegionHandle(handle); | ||
672 | uint startY = fh.y; | ||
673 | for(int i = 0; i < rsx; i++) | ||
674 | { | ||
675 | for(int j = 0; j < rsy ; j++) | ||
676 | { | ||
677 | innerHandles[fh.toHandle()] = handle; | ||
678 | fh.y += 256; | ||
679 | } | ||
680 | |||
681 | fh.y = startY; | ||
682 | fh.x += 256; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | private void removeFromInner(GridRegion region) | ||
687 | { | ||
688 | int rsx = region.RegionSizeX; | ||
689 | int rsy = region.RegionSizeY; | ||
690 | |||
691 | if(rsx < 512 && rsy < 512) | ||
692 | return; | ||
693 | |||
694 | rsx >>= 8; | ||
695 | rsy >>= 8; | ||
696 | ulong handle = region.RegionHandle & HANDLEMASK; | ||
697 | fastRegionHandle fh = new fastRegionHandle(handle); | ||
698 | uint startY = fh.y; | ||
699 | for(int i = 0; i < rsx; i++) | ||
700 | { | ||
701 | for(int j = 0; j < rsy ; j++) | ||
702 | { | ||
703 | innerHandles.Remove(fh.toHandle()); | ||
704 | fh.y += 256; | ||
705 | } | ||
706 | |||
707 | fh.y = startY; | ||
708 | fh.x += 256; | ||
709 | } | ||
710 | } | ||
711 | } | ||
712 | |||
713 | public class RegionsExpiringCache | ||
714 | { | ||
715 | const double CACHE_PURGE_TIME = 60000; // milliseconds | ||
716 | const int MAX_LOCK_WAIT = 10000; // milliseconds | ||
717 | |||
718 | /// <summary>For thread safety</summary> | ||
719 | object syncRoot = new object(); | ||
720 | /// <summary>For thread safety</summary> | ||
721 | object isPurging = new object(); | ||
722 | |||
723 | Dictionary<UUID, RegionInfoForScope> InfobyScope = new Dictionary<UUID, RegionInfoForScope>(); | ||
724 | private System.Timers.Timer timer = new System.Timers.Timer(CACHE_PURGE_TIME); | ||
725 | |||
726 | public RegionsExpiringCache() | ||
727 | { | ||
728 | timer.Elapsed += PurgeCache; | ||
729 | timer.Start(); | ||
730 | } | ||
731 | |||
732 | public bool AddOrUpdate(UUID scope, GridRegion region, float expirationSeconds) | ||
733 | { | ||
734 | if(region == null) | ||
735 | return false; | ||
736 | |||
737 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
738 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
739 | |||
740 | try | ||
741 | { | ||
742 | DateTime expire = DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds); | ||
743 | |||
744 | RegionInfoForScope ris = null; | ||
745 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
746 | { | ||
747 | ris = new RegionInfoForScope(region, expire); | ||
748 | InfobyScope[scope] = ris; | ||
749 | } | ||
750 | else | ||
751 | ris.AddUpdate(region, expire); | ||
752 | |||
753 | return true; | ||
754 | } | ||
755 | finally { Monitor.Exit(syncRoot); } | ||
756 | } | ||
757 | |||
758 | public void Clear() | ||
759 | { | ||
760 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
761 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
762 | try | ||
763 | { | ||
764 | foreach(RegionInfoForScope ris in InfobyScope.Values) | ||
765 | ris.Clear(); | ||
766 | InfobyScope.Clear(); | ||
767 | } | ||
768 | finally { Monitor.Exit(syncRoot); } | ||
769 | } | ||
770 | |||
771 | public bool Contains(UUID scope, GridRegion region) | ||
772 | { | ||
773 | if(region == null) | ||
774 | return false; | ||
775 | |||
776 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
777 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
778 | |||
779 | try | ||
780 | { | ||
781 | RegionInfoForScope ris = null; | ||
782 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
783 | return false; | ||
784 | |||
785 | return ris.Contains(region); | ||
786 | } | ||
787 | finally { Monitor.Exit(syncRoot); } | ||
788 | } | ||
789 | |||
790 | public bool Contains(UUID scope, ulong handle) | ||
791 | { | ||
792 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
793 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
794 | |||
795 | try | ||
796 | { | ||
797 | RegionInfoForScope ris = null; | ||
798 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
799 | return false; | ||
800 | |||
801 | return ris.Contains(handle); | ||
802 | } | ||
803 | finally { Monitor.Exit(syncRoot); } | ||
804 | } | ||
805 | |||
806 | public int Count() | ||
807 | { | ||
808 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
809 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
810 | |||
811 | try | ||
812 | { | ||
813 | int count = 0; | ||
814 | foreach(RegionInfoForScope ris in InfobyScope.Values) | ||
815 | count += ris.Count(); | ||
816 | return count; | ||
817 | } | ||
818 | finally { Monitor.Exit(syncRoot); } | ||
819 | } | ||
820 | |||
821 | public bool Remove(UUID scope, ulong handle) | ||
822 | { | ||
823 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
824 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
825 | try | ||
826 | { | ||
827 | RegionInfoForScope ris = null; | ||
828 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
829 | return false; | ||
830 | |||
831 | ris.Remove(handle); | ||
832 | if(ris.Count() == 0) | ||
833 | InfobyScope.Remove(scope); | ||
834 | return true; | ||
835 | } | ||
836 | finally { Monitor.Exit(syncRoot); } | ||
837 | } | ||
838 | |||
839 | public bool Remove(UUID scope, GridRegion region) | ||
840 | { | ||
841 | if(region == null) | ||
842 | return false; | ||
843 | |||
844 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
845 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
846 | try | ||
847 | { | ||
848 | RegionInfoForScope ris = null; | ||
849 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
850 | return false; | ||
851 | |||
852 | ris.Remove(region); | ||
853 | if(ris.Count() == 0) | ||
854 | InfobyScope.Remove(scope); | ||
855 | return true; | ||
856 | } | ||
857 | finally { Monitor.Exit(syncRoot); } | ||
858 | } | ||
859 | |||
860 | public bool TryGetValue(UUID scope, ulong handle, out GridRegion value) | ||
861 | { | ||
862 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
863 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
864 | |||
865 | value = null; | ||
866 | try | ||
867 | { | ||
868 | RegionInfoForScope ris = null; | ||
869 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
870 | return false; | ||
871 | value = ris.get(handle); | ||
872 | } | ||
873 | finally { Monitor.Exit(syncRoot); } | ||
874 | |||
875 | return value != null; | ||
876 | } | ||
877 | |||
878 | public bool TryGetValue(UUID scope, string name, out GridRegion value) | ||
879 | { | ||
880 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
881 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
882 | |||
883 | value = null; | ||
884 | try | ||
885 | { | ||
886 | RegionInfoForScope ris = null; | ||
887 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
888 | return false; | ||
889 | value = ris.get(name); | ||
890 | } | ||
891 | finally { Monitor.Exit(syncRoot); } | ||
892 | |||
893 | return value != null; | ||
894 | } | ||
895 | |||
896 | public bool TryGetValue(UUID scope, UUID id, out GridRegion value) | ||
897 | { | ||
898 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
899 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
900 | |||
901 | value = null; | ||
902 | try | ||
903 | { | ||
904 | RegionInfoForScope ris = null; | ||
905 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
906 | return false; | ||
907 | value = ris.get(id); | ||
908 | } | ||
909 | finally { Monitor.Exit(syncRoot); } | ||
910 | |||
911 | return value != null; | ||
912 | } | ||
913 | |||
914 | // gets a region that contains world position (x,y) | ||
915 | // hopefull will not take ages | ||
916 | public bool TryGetValue(UUID scope, uint x, uint y, out GridRegion value) | ||
917 | { | ||
918 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
919 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
920 | |||
921 | value = null; | ||
922 | try | ||
923 | { | ||
924 | RegionInfoForScope ris = null; | ||
925 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
926 | return false; | ||
927 | |||
928 | value = ris.get(x, y); | ||
929 | } | ||
930 | finally { Monitor.Exit(syncRoot); } | ||
931 | |||
932 | return value != null; | ||
933 | } | ||
934 | |||
935 | public bool Update(UUID scope, GridRegion region, double expirationSeconds) | ||
936 | { | ||
937 | if(region == null) | ||
938 | return false; | ||
939 | |||
940 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
941 | throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); | ||
942 | |||
943 | try | ||
944 | { | ||
945 | RegionInfoForScope ris = null; | ||
946 | if(!InfobyScope.TryGetValue(scope, out ris) || ris == null) | ||
947 | return false; | ||
948 | |||
949 | DateTime expire = DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds); | ||
950 | ris.AddUpdate(region,expire); | ||
951 | return true; | ||
952 | } | ||
953 | finally { Monitor.Exit(syncRoot); } | ||
954 | } | ||
955 | |||
956 | /// <summary> | ||
957 | /// Purges expired objects from the cache. Called automatically by the purge timer. | ||
958 | /// </summary> | ||
959 | private void PurgeCache(object sender, System.Timers.ElapsedEventArgs e) | ||
960 | { | ||
961 | // Only let one thread purge at once - a buildup could cause a crash | ||
962 | // This could cause the purge to be delayed while there are lots of read/write ops | ||
963 | // happening on the cache | ||
964 | if (!Monitor.TryEnter(isPurging)) | ||
965 | return; | ||
966 | |||
967 | DateTime now = DateTime.UtcNow; | ||
968 | |||
969 | try | ||
970 | { | ||
971 | // If we fail to acquire a lock on the synchronization root after MAX_LOCK_WAIT, skip this purge cycle | ||
972 | if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) | ||
973 | return; | ||
974 | try | ||
975 | { | ||
976 | List<UUID> expiredscopes = new List<UUID>(); | ||
977 | |||
978 | foreach (KeyValuePair<UUID, RegionInfoForScope> kvp in InfobyScope) | ||
979 | { | ||
980 | if (kvp.Value.expire(now) == 0) | ||
981 | expiredscopes.Add(kvp.Key); | ||
982 | } | ||
983 | |||
984 | if (expiredscopes.Count > 0) | ||
985 | { | ||
986 | foreach (UUID sid in expiredscopes) | ||
987 | { | ||
988 | InfobyScope[sid] = null; | ||
989 | InfobyScope.Remove(sid); | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | finally { Monitor.Exit(syncRoot); } | ||
994 | } | ||
995 | finally { Monitor.Exit(isPurging); } | ||
996 | } | ||
168 | } | 997 | } |
169 | } | 998 | } |