diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | 371 |
1 files changed, 294 insertions, 77 deletions
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 17646fb..c1bf544 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | |||
@@ -25,7 +25,7 @@ | |||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | // Uncomment to make asset Get requests for existing | 28 | // Uncomment to make asset Get requests for existing |
29 | // #define WAIT_ON_INPROGRESS_REQUESTS | 29 | // #define WAIT_ON_INPROGRESS_REQUESTS |
30 | 30 | ||
31 | using System; | 31 | using System; |
@@ -55,16 +55,18 @@ using OpenSim.Services.Interfaces; | |||
55 | namespace OpenSim.Region.CoreModules.Asset | 55 | namespace OpenSim.Region.CoreModules.Asset |
56 | { | 56 | { |
57 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FlotsamAssetCache")] | 57 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FlotsamAssetCache")] |
58 | public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache, IAssetService | 58 | public class FlotsamAssetCache : ISharedRegionModule, IAssetCache, IAssetService |
59 | { | 59 | { |
60 | private static readonly ILog m_log = | 60 | private static readonly ILog m_log = |
61 | LogManager.GetLogger( | 61 | LogManager.GetLogger( |
62 | MethodBase.GetCurrentMethod().DeclaringType); | 62 | MethodBase.GetCurrentMethod().DeclaringType); |
63 | 63 | ||
64 | private bool m_Enabled; | 64 | private bool m_Enabled; |
65 | private bool m_timerRunning; | ||
66 | private bool m_cleanupRunning; | ||
65 | 67 | ||
66 | private const string m_ModuleName = "FlotsamAssetCache"; | 68 | private const string m_ModuleName = "FlotsamAssetCache"; |
67 | private const string m_DefaultCacheDirectory = "../caches/assetcache"; | 69 | private const string m_DefaultCacheDirectory = "./assetcache"; |
68 | private string m_CacheDirectory = m_DefaultCacheDirectory; | 70 | private string m_CacheDirectory = m_DefaultCacheDirectory; |
69 | 71 | ||
70 | private readonly List<char> m_InvalidChars = new List<char>(); | 72 | private readonly List<char> m_InvalidChars = new List<char>(); |
@@ -76,6 +78,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
76 | private static ulong m_RequestsForInprogress; | 78 | private static ulong m_RequestsForInprogress; |
77 | private static ulong m_DiskHits; | 79 | private static ulong m_DiskHits; |
78 | private static ulong m_MemoryHits; | 80 | private static ulong m_MemoryHits; |
81 | private static ulong m_weakRefHits; | ||
79 | 82 | ||
80 | #if WAIT_ON_INPROGRESS_REQUESTS | 83 | #if WAIT_ON_INPROGRESS_REQUESTS |
81 | private Dictionary<string, ManualResetEvent> m_CurrentlyWriting = new Dictionary<string, ManualResetEvent>(); | 84 | private Dictionary<string, ManualResetEvent> m_CurrentlyWriting = new Dictionary<string, ManualResetEvent>(); |
@@ -89,12 +92,17 @@ namespace OpenSim.Region.CoreModules.Asset | |||
89 | private ExpiringCache<string, AssetBase> m_MemoryCache; | 92 | private ExpiringCache<string, AssetBase> m_MemoryCache; |
90 | private bool m_MemoryCacheEnabled = false; | 93 | private bool m_MemoryCacheEnabled = false; |
91 | 94 | ||
95 | private ExpiringCache<string, object> m_negativeCache; | ||
96 | private bool m_negativeCacheEnabled = true; | ||
97 | private bool m_negativeCacheSliding = false; | ||
98 | |||
92 | // Expiration is expressed in hours. | 99 | // Expiration is expressed in hours. |
93 | private const double m_DefaultMemoryExpiration = 2; | 100 | private double m_MemoryExpiration = 0.016; |
94 | private const double m_DefaultFileExpiration = 48; | 101 | private const double m_DefaultFileExpiration = 48; |
95 | private TimeSpan m_MemoryExpiration = TimeSpan.FromHours(m_DefaultMemoryExpiration); | 102 | // Negative cache is in seconds |
103 | private int m_negativeExpiration = 120; | ||
96 | private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration); | 104 | private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration); |
97 | private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(0.166); | 105 | private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(1.0); |
98 | 106 | ||
99 | private static int m_CacheDirectoryTiers = 1; | 107 | private static int m_CacheDirectoryTiers = 1; |
100 | private static int m_CacheDirectoryTierLen = 3; | 108 | private static int m_CacheDirectoryTierLen = 3; |
@@ -104,6 +112,11 @@ namespace OpenSim.Region.CoreModules.Asset | |||
104 | 112 | ||
105 | private IAssetService m_AssetService; | 113 | private IAssetService m_AssetService; |
106 | private List<Scene> m_Scenes = new List<Scene>(); | 114 | private List<Scene> m_Scenes = new List<Scene>(); |
115 | private object timerLock = new object(); | ||
116 | |||
117 | private Dictionary<string,WeakReference> weakAssetReferences = new Dictionary<string, WeakReference>(); | ||
118 | private object weakAssetReferencesLock = new object(); | ||
119 | private bool m_updateFileTimeOnCacheHit = false; | ||
107 | 120 | ||
108 | public FlotsamAssetCache() | 121 | public FlotsamAssetCache() |
109 | { | 122 | { |
@@ -111,7 +124,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
111 | m_InvalidChars.AddRange(Path.GetInvalidFileNameChars()); | 124 | m_InvalidChars.AddRange(Path.GetInvalidFileNameChars()); |
112 | } | 125 | } |
113 | 126 | ||
114 | public Type ReplaceableInterface | 127 | public Type ReplaceableInterface |
115 | { | 128 | { |
116 | get { return null; } | 129 | get { return null; } |
117 | } | 130 | } |
@@ -124,7 +137,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
124 | public void Initialise(IConfigSource source) | 137 | public void Initialise(IConfigSource source) |
125 | { | 138 | { |
126 | IConfig moduleConfig = source.Configs["Modules"]; | 139 | IConfig moduleConfig = source.Configs["Modules"]; |
127 | 140 | ||
128 | if (moduleConfig != null) | 141 | if (moduleConfig != null) |
129 | { | 142 | { |
130 | string name = moduleConfig.GetString("AssetCaching", String.Empty); | 143 | string name = moduleConfig.GetString("AssetCaching", String.Empty); |
@@ -132,6 +145,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
132 | if (name == Name) | 145 | if (name == Name) |
133 | { | 146 | { |
134 | m_MemoryCache = new ExpiringCache<string, AssetBase>(); | 147 | m_MemoryCache = new ExpiringCache<string, AssetBase>(); |
148 | m_negativeCache = new ExpiringCache<string, object>(); | ||
135 | m_Enabled = true; | 149 | m_Enabled = true; |
136 | 150 | ||
137 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name); | 151 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name); |
@@ -148,12 +162,18 @@ namespace OpenSim.Region.CoreModules.Asset | |||
148 | m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); | 162 | m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); |
149 | 163 | ||
150 | m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); | 164 | m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); |
151 | m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); | 165 | m_MemoryExpiration = assetConfig.GetDouble("MemoryCacheTimeout", m_MemoryExpiration); |
152 | 166 | m_MemoryExpiration *= 3600.0; // config in hours to seconds | |
167 | |||
168 | m_negativeCacheEnabled = assetConfig.GetBoolean("NegativeCacheEnabled", m_negativeCacheEnabled); | ||
169 | m_negativeExpiration = assetConfig.GetInt("NegativeCacheTimeout", m_negativeExpiration); | ||
170 | m_negativeCacheSliding = assetConfig.GetBoolean("NegativeCacheSliding", m_negativeCacheSliding); | ||
171 | m_updateFileTimeOnCacheHit = assetConfig.GetBoolean("UpdateFileTimeOnCacheHit", m_updateFileTimeOnCacheHit); | ||
172 | |||
153 | #if WAIT_ON_INPROGRESS_REQUESTS | 173 | #if WAIT_ON_INPROGRESS_REQUESTS |
154 | m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000); | 174 | m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000); |
155 | #endif | 175 | #endif |
156 | 176 | ||
157 | m_LogLevel = assetConfig.GetInt("LogLevel", m_LogLevel); | 177 | m_LogLevel = assetConfig.GetInt("LogLevel", m_LogLevel); |
158 | m_HitRateDisplay = (ulong)assetConfig.GetLong("HitRateDisplay", (long)m_HitRateDisplay); | 178 | m_HitRateDisplay = (ulong)assetConfig.GetLong("HitRateDisplay", (long)m_HitRateDisplay); |
159 | 179 | ||
@@ -170,14 +190,6 @@ namespace OpenSim.Region.CoreModules.Asset | |||
170 | 190 | ||
171 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); | 191 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); |
172 | 192 | ||
173 | if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) | ||
174 | { | ||
175 | m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds); | ||
176 | m_CacheCleanTimer.AutoReset = true; | ||
177 | m_CacheCleanTimer.Elapsed += CleanupExpiredFiles; | ||
178 | lock (m_CacheCleanTimer) | ||
179 | m_CacheCleanTimer.Start(); | ||
180 | } | ||
181 | 193 | ||
182 | if (m_CacheDirectoryTiers < 1) | 194 | if (m_CacheDirectoryTiers < 1) |
183 | { | 195 | { |
@@ -217,9 +229,8 @@ namespace OpenSim.Region.CoreModules.Asset | |||
217 | { | 229 | { |
218 | if (m_Enabled) | 230 | if (m_Enabled) |
219 | { | 231 | { |
220 | scene.RegisterModuleInterface<IImprovedAssetCache>(this); | 232 | scene.RegisterModuleInterface<IAssetCache>(this); |
221 | m_Scenes.Add(scene); | 233 | m_Scenes.Add(scene); |
222 | |||
223 | } | 234 | } |
224 | } | 235 | } |
225 | 236 | ||
@@ -227,23 +238,61 @@ namespace OpenSim.Region.CoreModules.Asset | |||
227 | { | 238 | { |
228 | if (m_Enabled) | 239 | if (m_Enabled) |
229 | { | 240 | { |
230 | scene.UnregisterModuleInterface<IImprovedAssetCache>(this); | 241 | scene.UnregisterModuleInterface<IAssetCache>(this); |
231 | m_Scenes.Remove(scene); | 242 | m_Scenes.Remove(scene); |
243 | lock(timerLock) | ||
244 | { | ||
245 | if(m_timerRunning && m_Scenes.Count <= 0) | ||
246 | { | ||
247 | m_timerRunning = false; | ||
248 | m_CacheCleanTimer.Stop(); | ||
249 | m_CacheCleanTimer.Close(); | ||
250 | } | ||
251 | } | ||
232 | } | 252 | } |
233 | } | 253 | } |
234 | 254 | ||
235 | public void RegionLoaded(Scene scene) | 255 | public void RegionLoaded(Scene scene) |
236 | { | 256 | { |
237 | if (m_Enabled && m_AssetService == null) | 257 | if (m_Enabled) |
238 | m_AssetService = scene.RequestModuleInterface<IAssetService>(); | 258 | { |
259 | if(m_AssetService == null) | ||
260 | m_AssetService = scene.RequestModuleInterface<IAssetService>(); | ||
261 | lock(timerLock) | ||
262 | { | ||
263 | if(!m_timerRunning) | ||
264 | { | ||
265 | if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) | ||
266 | { | ||
267 | m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds); | ||
268 | m_CacheCleanTimer.AutoReset = false; | ||
269 | m_CacheCleanTimer.Elapsed += CleanupExpiredFiles; | ||
270 | m_CacheCleanTimer.Start(); | ||
271 | m_timerRunning = true; | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | if (m_MemoryCacheEnabled) | ||
276 | m_MemoryCache = new ExpiringCache<string, AssetBase>(); | ||
277 | |||
278 | lock(weakAssetReferencesLock) | ||
279 | weakAssetReferences = new Dictionary<string, WeakReference>(); | ||
280 | } | ||
239 | } | 281 | } |
240 | 282 | ||
241 | //////////////////////////////////////////////////////////// | 283 | //////////////////////////////////////////////////////////// |
242 | // IImprovedAssetCache | 284 | // IAssetCache |
243 | // | 285 | // |
286 | private void UpdateWeakReference(string key, AssetBase asset) | ||
287 | { | ||
288 | WeakReference aref = new WeakReference(asset); | ||
289 | lock(weakAssetReferencesLock) | ||
290 | weakAssetReferences[key] = aref; | ||
291 | } | ||
244 | 292 | ||
245 | private void UpdateMemoryCache(string key, AssetBase asset) | 293 | private void UpdateMemoryCache(string key, AssetBase asset) |
246 | { | 294 | { |
295 | // NOTE DO NOT USE SLIDEEXPIRE option on current libomv | ||
247 | m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); | 296 | m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); |
248 | } | 297 | } |
249 | 298 | ||
@@ -257,11 +306,11 @@ namespace OpenSim.Region.CoreModules.Asset | |||
257 | if (File.Exists(filename)) | 306 | if (File.Exists(filename)) |
258 | { | 307 | { |
259 | UpdateFileLastAccessTime(filename); | 308 | UpdateFileLastAccessTime(filename); |
260 | } | 309 | } |
261 | else | 310 | else |
262 | { | 311 | { |
263 | // Once we start writing, make sure we flag that we're writing | 312 | // Once we start writing, make sure we flag that we're writing |
264 | // that object to the cache so that we don't try to write the | 313 | // that object to the cache so that we don't try to write the |
265 | // same file multiple times. | 314 | // same file multiple times. |
266 | lock (m_CurrentlyWriting) | 315 | lock (m_CurrentlyWriting) |
267 | { | 316 | { |
@@ -306,6 +355,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
306 | if (asset != null) | 355 | if (asset != null) |
307 | { | 356 | { |
308 | //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID); | 357 | //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID); |
358 | UpdateWeakReference(asset.ID, asset); | ||
309 | 359 | ||
310 | if (m_MemoryCacheEnabled) | 360 | if (m_MemoryCacheEnabled) |
311 | UpdateMemoryCache(asset.ID, asset); | 361 | UpdateMemoryCache(asset.ID, asset); |
@@ -315,6 +365,17 @@ namespace OpenSim.Region.CoreModules.Asset | |||
315 | } | 365 | } |
316 | } | 366 | } |
317 | 367 | ||
368 | public void CacheNegative(string id) | ||
369 | { | ||
370 | if (m_negativeCacheEnabled) | ||
371 | { | ||
372 | if (m_negativeCacheSliding) | ||
373 | m_negativeCache.AddOrUpdate(id, null, TimeSpan.FromSeconds(m_negativeExpiration)); | ||
374 | else | ||
375 | m_negativeCache.AddOrUpdate(id, null, m_negativeExpiration); | ||
376 | } | ||
377 | } | ||
378 | |||
318 | /// <summary> | 379 | /// <summary> |
319 | /// Updates the cached file with the current time. | 380 | /// Updates the cached file with the current time. |
320 | /// </summary> | 381 | /// </summary> |
@@ -333,6 +394,25 @@ namespace OpenSim.Region.CoreModules.Asset | |||
333 | } | 394 | } |
334 | } | 395 | } |
335 | 396 | ||
397 | private AssetBase GetFromWeakReference(string id) | ||
398 | { | ||
399 | AssetBase asset = null; | ||
400 | WeakReference aref; | ||
401 | |||
402 | lock(weakAssetReferencesLock) | ||
403 | { | ||
404 | if (weakAssetReferences.TryGetValue(id, out aref)) | ||
405 | { | ||
406 | asset = aref.Target as AssetBase; | ||
407 | if(asset == null) | ||
408 | weakAssetReferences.Remove(id); | ||
409 | else | ||
410 | m_weakRefHits++; | ||
411 | } | ||
412 | } | ||
413 | return asset; | ||
414 | } | ||
415 | |||
336 | /// <summary> | 416 | /// <summary> |
337 | /// Try to get an asset from the in-memory cache. | 417 | /// Try to get an asset from the in-memory cache. |
338 | /// </summary> | 418 | /// </summary> |
@@ -359,7 +439,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
359 | /// <param name="id"></param> | 439 | /// <param name="id"></param> |
360 | /// <returns>An asset retrieved from the file cache. null if there was a problem retrieving an asset.</returns> | 440 | /// <returns>An asset retrieved from the file cache. null if there was a problem retrieving an asset.</returns> |
361 | private AssetBase GetFromFileCache(string id) | 441 | private AssetBase GetFromFileCache(string id) |
362 | { | 442 | { |
363 | string filename = GetFileName(id); | 443 | string filename = GetFileName(id); |
364 | 444 | ||
365 | #if WAIT_ON_INPROGRESS_REQUESTS | 445 | #if WAIT_ON_INPROGRESS_REQUESTS |
@@ -394,6 +474,8 @@ namespace OpenSim.Region.CoreModules.Asset | |||
394 | { | 474 | { |
395 | using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) | 475 | using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) |
396 | { | 476 | { |
477 | if (stream.Length == 0) // Empty file will trigger exception below | ||
478 | return null; | ||
397 | BinaryFormatter bformatter = new BinaryFormatter(); | 479 | BinaryFormatter bformatter = new BinaryFormatter(); |
398 | 480 | ||
399 | asset = (AssetBase)bformatter.Deserialize(stream); | 481 | asset = (AssetBase)bformatter.Deserialize(stream); |
@@ -451,23 +533,57 @@ namespace OpenSim.Region.CoreModules.Asset | |||
451 | return found; | 533 | return found; |
452 | } | 534 | } |
453 | 535 | ||
536 | // For IAssetService | ||
454 | public AssetBase Get(string id) | 537 | public AssetBase Get(string id) |
455 | { | 538 | { |
539 | AssetBase asset; | ||
540 | Get(id, out asset); | ||
541 | return asset; | ||
542 | } | ||
543 | |||
544 | public bool Get(string id, out AssetBase asset) | ||
545 | { | ||
546 | asset = null; | ||
547 | |||
456 | m_Requests++; | 548 | m_Requests++; |
457 | 549 | ||
458 | AssetBase asset = null; | 550 | object dummy; |
551 | if (m_negativeCache.TryGetValue(id, out dummy)) | ||
552 | { | ||
553 | return false; | ||
554 | } | ||
459 | 555 | ||
460 | if (m_MemoryCacheEnabled) | 556 | asset = GetFromWeakReference(id); |
557 | if (asset != null && m_updateFileTimeOnCacheHit) | ||
558 | { | ||
559 | string filename = GetFileName(id); | ||
560 | UpdateFileLastAccessTime(filename); | ||
561 | } | ||
562 | |||
563 | if (m_MemoryCacheEnabled && asset == null) | ||
564 | { | ||
461 | asset = GetFromMemoryCache(id); | 565 | asset = GetFromMemoryCache(id); |
566 | if(asset != null) | ||
567 | { | ||
568 | UpdateWeakReference(id,asset); | ||
569 | if (m_updateFileTimeOnCacheHit) | ||
570 | { | ||
571 | string filename = GetFileName(id); | ||
572 | UpdateFileLastAccessTime(filename); | ||
573 | } | ||
574 | } | ||
575 | } | ||
462 | 576 | ||
463 | if (asset == null && m_FileCacheEnabled) | 577 | if (asset == null && m_FileCacheEnabled) |
464 | { | 578 | { |
465 | asset = GetFromFileCache(id); | 579 | asset = GetFromFileCache(id); |
466 | 580 | if(asset != null) | |
467 | if (m_MemoryCacheEnabled && asset != null) | 581 | UpdateWeakReference(id,asset); |
468 | UpdateMemoryCache(id, asset); | ||
469 | } | 582 | } |
470 | 583 | ||
584 | if (m_MemoryCacheEnabled && asset != null) | ||
585 | UpdateMemoryCache(id, asset); | ||
586 | |||
471 | if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) | 587 | if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) |
472 | { | 588 | { |
473 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); | 589 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); |
@@ -475,7 +591,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
475 | GenerateCacheHitReport().ForEach(l => m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0}", l)); | 591 | GenerateCacheHitReport().ForEach(l => m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0}", l)); |
476 | } | 592 | } |
477 | 593 | ||
478 | return asset; | 594 | return true; |
479 | } | 595 | } |
480 | 596 | ||
481 | public bool Check(string id) | 597 | public bool Check(string id) |
@@ -490,7 +606,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
490 | 606 | ||
491 | public AssetBase GetCached(string id) | 607 | public AssetBase GetCached(string id) |
492 | { | 608 | { |
493 | return Get(id); | 609 | AssetBase asset; |
610 | Get(id, out asset); | ||
611 | return asset; | ||
494 | } | 612 | } |
495 | 613 | ||
496 | public void Expire(string id) | 614 | public void Expire(string id) |
@@ -511,6 +629,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
511 | 629 | ||
512 | if (m_MemoryCacheEnabled) | 630 | if (m_MemoryCacheEnabled) |
513 | m_MemoryCache.Remove(id); | 631 | m_MemoryCache.Remove(id); |
632 | |||
633 | lock(weakAssetReferencesLock) | ||
634 | weakAssetReferences.Remove(id); | ||
514 | } | 635 | } |
515 | catch (Exception e) | 636 | catch (Exception e) |
516 | { | 637 | { |
@@ -525,7 +646,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
525 | if (m_LogLevel >= 2) | 646 | if (m_LogLevel >= 2) |
526 | m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches."); | 647 | m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches."); |
527 | 648 | ||
528 | if (m_FileCacheEnabled) | 649 | if (m_FileCacheEnabled && Directory.Exists(m_CacheDirectory)) |
529 | { | 650 | { |
530 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | 651 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) |
531 | { | 652 | { |
@@ -534,7 +655,12 @@ namespace OpenSim.Region.CoreModules.Asset | |||
534 | } | 655 | } |
535 | 656 | ||
536 | if (m_MemoryCacheEnabled) | 657 | if (m_MemoryCacheEnabled) |
537 | m_MemoryCache.Clear(); | 658 | m_MemoryCache = new ExpiringCache<string, AssetBase>(); |
659 | if (m_negativeCacheEnabled) | ||
660 | m_negativeCache = new ExpiringCache<string, object>(); | ||
661 | |||
662 | lock(weakAssetReferencesLock) | ||
663 | weakAssetReferences = new Dictionary<string, WeakReference>(); | ||
538 | } | 664 | } |
539 | 665 | ||
540 | private void CleanupExpiredFiles(object source, ElapsedEventArgs e) | 666 | private void CleanupExpiredFiles(object source, ElapsedEventArgs e) |
@@ -542,6 +668,12 @@ namespace OpenSim.Region.CoreModules.Asset | |||
542 | if (m_LogLevel >= 2) | 668 | if (m_LogLevel >= 2) |
543 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration); | 669 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration); |
544 | 670 | ||
671 | lock(timerLock) | ||
672 | { | ||
673 | if(!m_timerRunning || m_cleanupRunning) | ||
674 | return; | ||
675 | m_cleanupRunning = true; | ||
676 | } | ||
545 | // Purge all files last accessed prior to this point | 677 | // Purge all files last accessed prior to this point |
546 | DateTime purgeLine = DateTime.Now - m_FileExpiration; | 678 | DateTime purgeLine = DateTime.Now - m_FileExpiration; |
547 | 679 | ||
@@ -549,27 +681,34 @@ namespace OpenSim.Region.CoreModules.Asset | |||
549 | // before cleaning up expired files we must scan the objects in the scene to make sure that we retain | 681 | // before cleaning up expired files we must scan the objects in the scene to make sure that we retain |
550 | // such local assets if they have not been recently accessed. | 682 | // such local assets if they have not been recently accessed. |
551 | TouchAllSceneAssets(false); | 683 | TouchAllSceneAssets(false); |
684 | if(Directory.Exists(m_CacheDirectory)) | ||
685 | { | ||
686 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | ||
687 | CleanExpiredFiles(dir, purgeLine); | ||
688 | } | ||
552 | 689 | ||
553 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | 690 | lock(timerLock) |
554 | { | 691 | { |
555 | CleanExpiredFiles(dir, purgeLine); | 692 | if(m_timerRunning) |
693 | m_CacheCleanTimer.Start(); | ||
694 | m_cleanupRunning = false; | ||
556 | } | 695 | } |
557 | } | 696 | } |
558 | 697 | ||
559 | /// <summary> | 698 | /// <summary> |
560 | /// Recurses through specified directory checking for asset files last | 699 | /// Recurses through specified directory checking for asset files last |
561 | /// accessed prior to the specified purge line and deletes them. Also | 700 | /// accessed prior to the specified purge line and deletes them. Also |
562 | /// removes empty tier directories. | 701 | /// removes empty tier directories. |
563 | /// </summary> | 702 | /// </summary> |
564 | /// <param name="dir"></param> | 703 | /// <param name="dir"></param> |
565 | /// <param name="purgeLine"></param> | 704 | /// <param name="purgeLine"></param> |
566 | private void CleanExpiredFiles(string dir, DateTime purgeLine) | 705 | private void CleanExpiredFiles(string dir, DateTime purgeLine) |
567 | { | 706 | { |
568 | // Yet another "directory we are trying to remove doesn't exist, so don't complain" idiocy fix. | ||
569 | if (!Directory.Exists(dir)) | ||
570 | return; | ||
571 | try | 707 | try |
572 | { | 708 | { |
709 | if(!Directory.Exists(dir)) | ||
710 | return; | ||
711 | |||
573 | foreach (string file in Directory.GetFiles(dir)) | 712 | foreach (string file in Directory.GetFiles(dir)) |
574 | { | 713 | { |
575 | if (File.GetLastAccessTime(file) < purgeLine) | 714 | if (File.GetLastAccessTime(file) < purgeLine) |
@@ -593,14 +732,19 @@ namespace OpenSim.Region.CoreModules.Asset | |||
593 | else if (dirSize >= m_CacheWarnAt) | 732 | else if (dirSize >= m_CacheWarnAt) |
594 | { | 733 | { |
595 | m_log.WarnFormat( | 734 | m_log.WarnFormat( |
596 | "[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", | 735 | "[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", |
597 | dir, dirSize); | 736 | dir, dirSize); |
598 | } | 737 | } |
599 | } | 738 | } |
739 | catch (DirectoryNotFoundException) | ||
740 | { | ||
741 | // If we get here, another node on the same box has | ||
742 | // already removed the directory. Continue with next. | ||
743 | } | ||
600 | catch (Exception e) | 744 | catch (Exception e) |
601 | { | 745 | { |
602 | //// TODO - I'm almost sure this is another case of failing to delete something that doesn't actually exist. | 746 | m_log.Warn( |
603 | //// m_log.Warn(string.Format("[FLOTSAM ASSET CACHE]: Could not complete clean of expired files in {0}", dir)); | 747 | string.Format("[FLOTSAM ASSET CACHE]: Could not complete clean of expired files in {0}, exception ", dir), e); |
604 | } | 748 | } |
605 | } | 749 | } |
606 | 750 | ||
@@ -628,7 +772,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
628 | } | 772 | } |
629 | 773 | ||
630 | /// <summary> | 774 | /// <summary> |
631 | /// Writes a file to the file cache, creating any nessesary | 775 | /// Writes a file to the file cache, creating any nessesary |
632 | /// tier directories along the way | 776 | /// tier directories along the way |
633 | /// </summary> | 777 | /// </summary> |
634 | /// <param name="filename"></param> | 778 | /// <param name="filename"></param> |
@@ -640,7 +784,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
640 | // Make sure the target cache directory exists | 784 | // Make sure the target cache directory exists |
641 | string directory = Path.GetDirectoryName(filename); | 785 | string directory = Path.GetDirectoryName(filename); |
642 | 786 | ||
643 | // Write file first to a temp name, so that it doesn't look | 787 | // Write file first to a temp name, so that it doesn't look |
644 | // like it's already cached while it's still writing. | 788 | // like it's already cached while it's still writing. |
645 | string tempname = Path.Combine(directory, Path.GetRandomFileName()); | 789 | string tempname = Path.Combine(directory, Path.GetRandomFileName()); |
646 | 790 | ||
@@ -652,7 +796,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
652 | { | 796 | { |
653 | Directory.CreateDirectory(directory); | 797 | Directory.CreateDirectory(directory); |
654 | } | 798 | } |
655 | 799 | ||
656 | stream = File.Open(tempname, FileMode.Create); | 800 | stream = File.Open(tempname, FileMode.Create); |
657 | BinaryFormatter bformatter = new BinaryFormatter(); | 801 | BinaryFormatter bformatter = new BinaryFormatter(); |
658 | bformatter.Serialize(stream, asset); | 802 | bformatter.Serialize(stream, asset); |
@@ -665,6 +809,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
665 | 809 | ||
666 | return; | 810 | return; |
667 | } | 811 | } |
812 | catch (UnauthorizedAccessException) | ||
813 | { | ||
814 | } | ||
668 | finally | 815 | finally |
669 | { | 816 | { |
670 | if (stream != null) | 817 | if (stream != null) |
@@ -686,7 +833,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
686 | // This situation occurs fairly rarely anyway. We assume in this that moves are atomic on the | 833 | // This situation occurs fairly rarely anyway. We assume in this that moves are atomic on the |
687 | // filesystem. | 834 | // filesystem. |
688 | File.Move(tempname, filename); | 835 | File.Move(tempname, filename); |
689 | 836 | ||
690 | if (m_LogLevel >= 2) | 837 | if (m_LogLevel >= 2) |
691 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Cache Stored :: {0}", asset.ID); | 838 | m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Cache Stored :: {0}", asset.ID); |
692 | } | 839 | } |
@@ -725,6 +872,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
725 | /// <returns></returns> | 872 | /// <returns></returns> |
726 | private int GetFileCacheCount(string dir) | 873 | private int GetFileCacheCount(string dir) |
727 | { | 874 | { |
875 | if(!Directory.Exists(dir)) | ||
876 | return 0; | ||
877 | |||
728 | int count = Directory.GetFiles(dir).Length; | 878 | int count = Directory.GetFiles(dir).Length; |
729 | 879 | ||
730 | foreach (string subdir in Directory.GetDirectories(dir)) | 880 | foreach (string subdir in Directory.GetDirectories(dir)) |
@@ -743,7 +893,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
743 | { | 893 | { |
744 | string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + regionID.ToString() + ".fac"); | 894 | string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + regionID.ToString() + ".fac"); |
745 | 895 | ||
746 | try | 896 | try |
747 | { | 897 | { |
748 | if (File.Exists(RegionCacheStatusFile)) | 898 | if (File.Exists(RegionCacheStatusFile)) |
749 | { | 899 | { |
@@ -752,7 +902,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
752 | else | 902 | else |
753 | { | 903 | { |
754 | File.WriteAllText( | 904 | File.WriteAllText( |
755 | RegionCacheStatusFile, | 905 | RegionCacheStatusFile, |
756 | "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache."); | 906 | "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache."); |
757 | } | 907 | } |
758 | } | 908 | } |
@@ -760,14 +910,14 @@ namespace OpenSim.Region.CoreModules.Asset | |||
760 | { | 910 | { |
761 | m_log.Warn( | 911 | m_log.Warn( |
762 | string.Format( | 912 | string.Format( |
763 | "[FLOTSAM ASSET CACHE]: Could not stamp region status file for region {0}. Exception ", | 913 | "[FLOTSAM ASSET CACHE]: Could not stamp region status file for region {0}. Exception ", |
764 | regionID), | 914 | regionID), |
765 | e); | 915 | e); |
766 | } | 916 | } |
767 | } | 917 | } |
768 | 918 | ||
769 | /// <summary> | 919 | /// <summary> |
770 | /// Iterates through all Scenes, doing a deep scan through assets | 920 | /// Iterates through all Scenes, doing a deep scan through assets |
771 | /// to update the access time of all assets present in the scene or referenced by assets | 921 | /// to update the access time of all assets present in the scene or referenced by assets |
772 | /// in the scene. | 922 | /// in the scene. |
773 | /// </summary> | 923 | /// </summary> |
@@ -786,10 +936,16 @@ namespace OpenSim.Region.CoreModules.Asset | |||
786 | StampRegionStatusFile(s.RegionInfo.RegionID); | 936 | StampRegionStatusFile(s.RegionInfo.RegionID); |
787 | 937 | ||
788 | s.ForEachSOG(delegate(SceneObjectGroup e) | 938 | s.ForEachSOG(delegate(SceneObjectGroup e) |
789 | { | 939 | { |
940 | if(!m_timerRunning && !storeUncached) | ||
941 | return; | ||
942 | |||
790 | gatherer.AddForInspection(e); | 943 | gatherer.AddForInspection(e); |
791 | gatherer.GatherAll(); | 944 | gatherer.GatherAll(); |
792 | 945 | ||
946 | if(!m_timerRunning && !storeUncached) | ||
947 | return; | ||
948 | |||
793 | foreach (UUID assetID in gatherer.GatheredUuids.Keys) | 949 | foreach (UUID assetID in gatherer.GatheredUuids.Keys) |
794 | { | 950 | { |
795 | if (!assetsFound.ContainsKey(assetID)) | 951 | if (!assetsFound.ContainsKey(assetID)) |
@@ -799,6 +955,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
799 | if (File.Exists(filename)) | 955 | if (File.Exists(filename)) |
800 | { | 956 | { |
801 | UpdateFileLastAccessTime(filename); | 957 | UpdateFileLastAccessTime(filename); |
958 | assetsFound[assetID] = true; | ||
802 | } | 959 | } |
803 | else if (storeUncached) | 960 | else if (storeUncached) |
804 | { | 961 | { |
@@ -818,7 +975,14 @@ namespace OpenSim.Region.CoreModules.Asset | |||
818 | } | 975 | } |
819 | 976 | ||
820 | gatherer.GatheredUuids.Clear(); | 977 | gatherer.GatheredUuids.Clear(); |
978 | if(!m_timerRunning && !storeUncached) | ||
979 | return; | ||
980 | |||
981 | if(!storeUncached) | ||
982 | Thread.Sleep(50); | ||
821 | }); | 983 | }); |
984 | if(!m_timerRunning && !storeUncached) | ||
985 | break; | ||
822 | } | 986 | } |
823 | 987 | ||
824 | return assetsFound.Count; | 988 | return assetsFound.Count; |
@@ -829,6 +993,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
829 | /// </summary> | 993 | /// </summary> |
830 | private void ClearFileCache() | 994 | private void ClearFileCache() |
831 | { | 995 | { |
996 | if(!Directory.Exists(m_CacheDirectory)) | ||
997 | return; | ||
998 | |||
832 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) | 999 | foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) |
833 | { | 1000 | { |
834 | try | 1001 | try |
@@ -862,22 +1029,35 @@ namespace OpenSim.Region.CoreModules.Asset | |||
862 | { | 1029 | { |
863 | List<string> outputLines = new List<string>(); | 1030 | List<string> outputLines = new List<string>(); |
864 | 1031 | ||
865 | double fileHitRate = (double)m_DiskHits / m_Requests * 100.0; | 1032 | double invReq = 100.0 / m_Requests; |
1033 | |||
1034 | double weakHitRate = m_weakRefHits * invReq; | ||
1035 | int weakEntries = weakAssetReferences.Count; | ||
1036 | |||
1037 | double fileHitRate = m_DiskHits * invReq; | ||
1038 | double TotalHitRate = weakHitRate + fileHitRate; | ||
1039 | |||
1040 | outputLines.Add( | ||
1041 | string.Format("Total requests: {0}", m_Requests)); | ||
866 | outputLines.Add( | 1042 | outputLines.Add( |
867 | string.Format("File Hit Rate: {0}% for {1} requests", fileHitRate.ToString("0.00"), m_Requests)); | 1043 | string.Format("unCollected Hit Rate: {0}% ({1} entries)", weakHitRate.ToString("0.00"),weakEntries)); |
1044 | outputLines.Add( | ||
1045 | string.Format("File Hit Rate: {0}%", fileHitRate.ToString("0.00"))); | ||
868 | 1046 | ||
869 | if (m_MemoryCacheEnabled) | 1047 | if (m_MemoryCacheEnabled) |
870 | { | 1048 | { |
871 | double memHitRate = (double)m_MemoryHits / m_Requests * 100.0; | 1049 | double HitRate = m_MemoryHits * invReq; |
872 | |||
873 | outputLines.Add( | 1050 | outputLines.Add( |
874 | string.Format("Memory Hit Rate: {0}% for {1} requests", memHitRate.ToString("0.00"), m_Requests)); | 1051 | string.Format("Memory Hit Rate: {0}%", HitRate.ToString("0.00"))); |
1052 | |||
1053 | TotalHitRate += HitRate; | ||
875 | } | 1054 | } |
1055 | outputLines.Add( | ||
1056 | string.Format("Total Hit Rate: {0}%", TotalHitRate.ToString("0.00"))); | ||
876 | 1057 | ||
877 | outputLines.Add( | 1058 | outputLines.Add( |
878 | string.Format( | 1059 | string.Format( |
879 | "Unnecessary requests due to requests for assets that are currently downloading: {0}", | 1060 | "Requests overlap during file writing: {0}", m_RequestsForInprogress)); |
880 | m_RequestsForInprogress)); | ||
881 | 1061 | ||
882 | return outputLines; | 1062 | return outputLines; |
883 | } | 1063 | } |
@@ -914,9 +1094,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
914 | if (m_FileCacheEnabled) | 1094 | if (m_FileCacheEnabled) |
915 | { | 1095 | { |
916 | con.Output("Deep scans have previously been performed on the following regions:"); | 1096 | con.Output("Deep scans have previously been performed on the following regions:"); |
917 | 1097 | ||
918 | foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) | 1098 | foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) |
919 | { | 1099 | { |
920 | string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); | 1100 | string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); |
921 | DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); | 1101 | DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); |
922 | con.OutputFormat("Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); | 1102 | con.OutputFormat("Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); |
@@ -959,7 +1139,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
959 | con.Output("Memory cache not enabled."); | 1139 | con.Output("Memory cache not enabled."); |
960 | } | 1140 | } |
961 | } | 1141 | } |
962 | 1142 | ||
963 | if (clearFile) | 1143 | if (clearFile) |
964 | { | 1144 | { |
965 | if (m_FileCacheEnabled) | 1145 | if (m_FileCacheEnabled) |
@@ -976,13 +1156,44 @@ namespace OpenSim.Region.CoreModules.Asset | |||
976 | break; | 1156 | break; |
977 | 1157 | ||
978 | case "assets": | 1158 | case "assets": |
979 | con.Output("Ensuring assets are cached for all scenes."); | 1159 | lock(timerLock) |
1160 | { | ||
1161 | if(m_cleanupRunning) | ||
1162 | { | ||
1163 | con.OutputFormat("FloatSam assets check already running"); | ||
1164 | return; | ||
1165 | } | ||
1166 | m_cleanupRunning = true; | ||
1167 | } | ||
980 | 1168 | ||
981 | WorkManager.RunInThread(delegate | 1169 | con.Output("FloatSam Ensuring assets are cached for all scenes."); |
1170 | |||
1171 | WorkManager.RunInThreadPool(delegate | ||
982 | { | 1172 | { |
1173 | bool wasRunning= false; | ||
1174 | lock(timerLock) | ||
1175 | { | ||
1176 | if(m_timerRunning) | ||
1177 | { | ||
1178 | m_CacheCleanTimer.Stop(); | ||
1179 | m_timerRunning = false; | ||
1180 | wasRunning = true; | ||
1181 | Thread.Sleep(100); | ||
1182 | } | ||
1183 | } | ||
983 | int assetReferenceTotal = TouchAllSceneAssets(true); | 1184 | int assetReferenceTotal = TouchAllSceneAssets(true); |
1185 | GC.Collect(); | ||
1186 | lock(timerLock) | ||
1187 | { | ||
1188 | if(wasRunning) | ||
1189 | { | ||
1190 | m_CacheCleanTimer.Start(); | ||
1191 | m_timerRunning = true; | ||
1192 | } | ||
1193 | m_cleanupRunning = false; | ||
1194 | } | ||
984 | con.OutputFormat("Completed check with {0} assets.", assetReferenceTotal); | 1195 | con.OutputFormat("Completed check with {0} assets.", assetReferenceTotal); |
985 | }, null, "TouchAllSceneAssets"); | 1196 | }, null, "TouchAllSceneAssets", false); |
986 | 1197 | ||
987 | break; | 1198 | break; |
988 | 1199 | ||
@@ -1037,19 +1248,23 @@ namespace OpenSim.Region.CoreModules.Asset | |||
1037 | 1248 | ||
1038 | public AssetMetadata GetMetadata(string id) | 1249 | public AssetMetadata GetMetadata(string id) |
1039 | { | 1250 | { |
1040 | AssetBase asset = Get(id); | 1251 | AssetBase asset; |
1252 | Get(id, out asset); | ||
1041 | return asset.Metadata; | 1253 | return asset.Metadata; |
1042 | } | 1254 | } |
1043 | 1255 | ||
1044 | public byte[] GetData(string id) | 1256 | public byte[] GetData(string id) |
1045 | { | 1257 | { |
1046 | AssetBase asset = Get(id); | 1258 | AssetBase asset; |
1259 | Get(id, out asset); | ||
1047 | return asset.Data; | 1260 | return asset.Data; |
1048 | } | 1261 | } |
1049 | 1262 | ||
1050 | public bool Get(string id, object sender, AssetRetrieved handler) | 1263 | public bool Get(string id, object sender, AssetRetrieved handler) |
1051 | { | 1264 | { |
1052 | AssetBase asset = Get(id); | 1265 | AssetBase asset; |
1266 | if (!Get(id, out asset)) | ||
1267 | return false; | ||
1053 | handler(id, sender, asset); | 1268 | handler(id, sender, asset); |
1054 | return true; | 1269 | return true; |
1055 | } | 1270 | } |
@@ -1057,12 +1272,12 @@ namespace OpenSim.Region.CoreModules.Asset | |||
1057 | public bool[] AssetsExist(string[] ids) | 1272 | public bool[] AssetsExist(string[] ids) |
1058 | { | 1273 | { |
1059 | bool[] exist = new bool[ids.Length]; | 1274 | bool[] exist = new bool[ids.Length]; |
1060 | 1275 | ||
1061 | for (int i = 0; i < ids.Length; i++) | 1276 | for (int i = 0; i < ids.Length; i++) |
1062 | { | 1277 | { |
1063 | exist[i] = Check(ids[i]); | 1278 | exist[i] = Check(ids[i]); |
1064 | } | 1279 | } |
1065 | 1280 | ||
1066 | return exist; | 1281 | return exist; |
1067 | } | 1282 | } |
1068 | 1283 | ||
@@ -1080,7 +1295,9 @@ namespace OpenSim.Region.CoreModules.Asset | |||
1080 | 1295 | ||
1081 | public bool UpdateContent(string id, byte[] data) | 1296 | public bool UpdateContent(string id, byte[] data) |
1082 | { | 1297 | { |
1083 | AssetBase asset = Get(id); | 1298 | AssetBase asset; |
1299 | if (!Get(id, out asset)) | ||
1300 | return false; | ||
1084 | asset.Data = data; | 1301 | asset.Data = data; |
1085 | Cache(asset); | 1302 | Cache(asset); |
1086 | return true; | 1303 | return true; |