diff --git a/OpenMetaverseTypes/ExpiringCache.cs b/OpenMetaverseTypes/ExpiringCache.cs index b7f449d4..5ec31825 100644 --- a/OpenMetaverseTypes/ExpiringCache.cs +++ b/OpenMetaverseTypes/ExpiringCache.cs @@ -103,7 +103,7 @@ namespace OpenMetaverse #region Public methods - public bool Add(TKey key, TValue value, DateTime expiration) + public bool Add(TKey key, TValue value, double expirationSeconds) { if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); @@ -116,7 +116,7 @@ namespace OpenMetaverse } else { - TimedCacheKey internalKey = new TimedCacheKey(key, expiration); + TimedCacheKey internalKey = new TimedCacheKey(key, DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds)); timedStorage.Add(internalKey, value); timedStorageIndex.Add(key, internalKey); return true; @@ -147,7 +147,7 @@ namespace OpenMetaverse finally { Monitor.Exit(syncRoot); } } - public bool AddOrUpdate(TKey key, TValue value, DateTime expiration) + public bool AddOrUpdate(TKey key, TValue value, double expirationSeconds) { if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); @@ -155,12 +155,12 @@ namespace OpenMetaverse { if (Contains(key)) { - Update(key, value, expiration); + Update(key, value, expirationSeconds); return false; } else { - Add(key, value, expiration); + Add(key, value, expirationSeconds); return true; } } @@ -245,22 +245,6 @@ namespace OpenMetaverse } } - public TValue this[TKey key, DateTime expiration] - { - set - { - AddOrUpdate(key, value, expiration); - } - } - - public TValue this[TKey key, TimeSpan slidingExpiration] - { - set - { - AddOrUpdate(key, value, slidingExpiration); - } - } - public bool Remove(TKey key) { if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) @@ -327,7 +311,7 @@ namespace OpenMetaverse finally { Monitor.Exit(syncRoot); } } - public bool Update(TKey key, TValue value, DateTime expiration) + public bool Update(TKey key, TValue value, double expirationSeconds) { if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT)) throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms"); @@ -343,7 +327,7 @@ namespace OpenMetaverse return false; } - TimedCacheKey internalKey = new TimedCacheKey(key, expiration); + TimedCacheKey internalKey = new TimedCacheKey(key, DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds)); timedStorage.Add(internalKey, value); timedStorageIndex.Add(key, internalKey); return true; @@ -414,6 +398,9 @@ namespace OpenMetaverse // happening on the cache if (!Monitor.TryEnter(isPurging)) return; + + DateTime signalTime = DateTime.UtcNow; + try { // If we fail to acquire a lock on the synchronization root after MAX_LOCK_WAIT, skip this purge cycle @@ -421,14 +408,14 @@ namespace OpenMetaverse return; try { - List expiredItems = new List(); + Lazy> expiredItems = new Lazy>(); foreach (TimedCacheKey timedKey in timedStorage.Keys) { - if (timedKey.ExpirationDate < e.SignalTime) + if (timedKey.ExpirationDate < signalTime) { // Mark the object for purge - expiredItems.Add(timedKey.Key); + expiredItems.Value.Add(timedKey.Key); } else { @@ -436,11 +423,14 @@ namespace OpenMetaverse } } - foreach (TKey key in expiredItems) + if (expiredItems.IsValueCreated) { - TimedCacheKey timedKey = timedStorageIndex[key]; - timedStorageIndex.Remove(timedKey.Key); - timedStorage.Remove(timedKey); + foreach (TKey key in expiredItems.Value) + { + TimedCacheKey timedKey = timedStorageIndex[key]; + timedStorageIndex.Remove(timedKey.Key); + timedStorage.Remove(timedKey); + } } } finally { Monitor.Exit(syncRoot); } diff --git a/OpenMetaverseTypes/Lazy.cs b/OpenMetaverseTypes/Lazy.cs new file mode 100644 index 00000000..30ff0397 --- /dev/null +++ b/OpenMetaverseTypes/Lazy.cs @@ -0,0 +1,100 @@ +/* + * Copyright (c) Microsoft Corporation + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace OpenMetaverse +{ + public class Lazy + { + private T _value = default(T); + private volatile bool _isValueCreated = false; + private Func _valueFactory = null; + private object _lock; + + public bool IsValueCreated { get { return _isValueCreated; } } + + public Lazy() + : this(() => Activator.CreateInstance()) + { + } + + public Lazy(bool isThreadSafe) + : this(() => Activator.CreateInstance(), isThreadSafe) + { + } + + public Lazy(Func valueFactory) : + this(valueFactory, true) + { + } + + public Lazy(Func valueFactory, bool isThreadSafe) + { + if (isThreadSafe) + { + this._lock = new object(); + } + + this._valueFactory = valueFactory; + } + + + public T Value + { + get + { + if (!this._isValueCreated) + { + if (this._lock != null) + { + Monitor.Enter(this._lock); + } + + try + { + T value = this._valueFactory.Invoke(); + this._valueFactory = null; + Thread.MemoryBarrier(); + this._value = value; + this._isValueCreated = true; + } + finally + { + if (this._lock != null) + { + Monitor.Exit(this._lock); + } + } + } + return this._value; + } + } + } +}