From dfd53af1f3d73a584ebd6900ef201e494ce5de5b Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 13 Aug 2010 23:06:35 +0000 Subject: [PATCH] * Fixed the ExpiringCache collection to remove an ambiguity of whether to use local timezone expiration dates or UTC expiration dates. Expiration time is now passed in as the number of seconds relative to the current time * Minor performance improvement in ExpiringCache to avoid instantiating a List<> every time the cache purge timer runs git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@3421 52acb1d6-8a22-11de-b505-999d5b087335 --- OpenMetaverseTypes/ExpiringCache.cs | 50 ++++++-------- OpenMetaverseTypes/Lazy.cs | 100 ++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 30 deletions(-) create mode 100644 OpenMetaverseTypes/Lazy.cs 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; + } + } + } +}