RegionInfoCache.cs 30 KB

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