Skip to content

Commit

Permalink
Modified the cache to use a more efficient way to iterate through and…
Browse files Browse the repository at this point in the history
… check each weak reference
  • Loading branch information
GWBasic committed Mar 4, 2012
1 parent 1be0a59 commit d2eaf9a
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 275 deletions.
93 changes: 47 additions & 46 deletions Server/ObjectCloud.Common/Cache.cs
Expand Up @@ -71,18 +71,13 @@ public class Cache<TKey, TValue, TConstructorArg> : Cache


/// <summary> /// <summary>
/// The GC count that was present at the last cleanup, used to prevent duplicate cleanups /// The GC count that was present at the last cleanup, used to prevent duplicate cleanups
/// </summary> /// </summary>
private int _GCCountAtLastCleanup = GC.CollectionCount(GC.MaxGeneration); private int _GCCountAtLastCleanup = GC.CollectionCount(GC.MaxGeneration);


/// <summary> /// <summary>
/// A copy of all of the weak references, used to periodically check to see if a value is GCed /// All of the weak references as they are added. This queue is used to allow each operation to look at a weak refernence to eventually detect when the cache needs to be cleaned
/// </summary>
private WeakReference[] _WRsCopy = new WeakReference[0];

/// <summary>
/// The mod of the is the current weak reference to check on accessing the cache
/// </summary> /// </summary>
private int _WRCtr = 0; private LockFreeQueue<WeakReference> _ScanQueue = new LockFreeQueue<WeakReference>();


public Cache(CreateForCache<TKey, TValue, TConstructorArg> createForCache) public Cache(CreateForCache<TKey, TValue, TConstructorArg> createForCache)
{ {
Expand All @@ -106,41 +101,43 @@ public bool Enabled
/// </summary> /// </summary>
private void CheckForCleanup() private void CheckForCleanup()
{ {
if (0 == _WRsCopy.Length) WeakReference wr;
return; if (_ScanQueue.Dequeue(out wr))
{
// Keep re-queuing items that are still alive
if (wr.IsAlive)
_ScanQueue.Enqueue(wr);

else if (_GCCountAtLastCleanup != GC.CollectionCount(GC.MaxGeneration))
ThreadPool.QueueUserWorkItem(DoCleanup);
}
}


int wrCtr = Interlocked.Increment(ref _WRCtr) % _WRsCopy.Length; private void DoCleanup(object state)
{
Lock.EnterWriteLock();


if (!_WRsCopy[wrCtr].IsAlive) try
ThreadPool.QueueUserWorkItem(delegate(object state) {
// Keep scanning while GCs are running
while (_GCCountAtLastCleanup != GC.CollectionCount(GC.MaxGeneration))
{ {
Lock.EnterWriteLock(); _GCCountAtLastCleanup = GC.CollectionCount(GC.MaxGeneration);


try foreach (KeyValuePair<TKey, WeakReference> kvp in new Dictionary<TKey, WeakReference>(Dictionary))
{ {
// Keep scanning while GCs are running TKey key = kvp.Key;
while (_GCCountAtLastCleanup != GC.CollectionCount(GC.MaxGeneration)) WeakReference wr = kvp.Value;
{
_GCCountAtLastCleanup = GC.CollectionCount(GC.MaxGeneration);

foreach (KeyValuePair<TKey, WeakReference> kvp in new Dictionary<TKey, WeakReference>(Dictionary))
{
TKey key = kvp.Key;
WeakReference wr = kvp.Value;


if (!wr.IsAlive) if (!wr.IsAlive)
Dictionary.Remove(key); Dictionary.Remove(key);
}

_WRsCopy = new WeakReference[Dictionary.Count];
Dictionary.Values.CopyTo(_WRsCopy, 0);
}
}
finally
{
Lock.ExitWriteLock();
} }
}); }
}
finally
{
Lock.ExitWriteLock();
}
} }


/// <summary> /// <summary>
Expand Down Expand Up @@ -191,8 +188,7 @@ public TValue Get(TKey key, TConstructorArg constructorArg)
weakReference = new WeakReference(null); weakReference = new WeakReference(null);
Dictionary[key] = weakReference; Dictionary[key] = weakReference;


_WRsCopy = new WeakReference[Dictionary.Count]; _ScanQueue.Enqueue(weakReference);
Dictionary.Values.CopyTo(_WRsCopy, 0);
} }
} }
finally finally
Expand Down Expand Up @@ -245,10 +241,10 @@ public void Set(TKey key, TValue value)
weakReference.Target = value; weakReference.Target = value;
else else
{ {
Dictionary[key] = new WeakReference(value); weakReference = new WeakReference(value);
Dictionary[key] = weakReference;


_WRsCopy = new WeakReference[Dictionary.Count]; _ScanQueue.Enqueue(weakReference);
Dictionary.Values.CopyTo(_WRsCopy, 0);
} }
} }
finally finally
Expand Down Expand Up @@ -283,8 +279,7 @@ public bool Remove(TKey key)


Dictionary.Remove(key); Dictionary.Remove(key);


_WRsCopy = new WeakReference[Dictionary.Count]; // No effort is made to remove from _ScanQueue because it'll eventually be cleaned up
Dictionary.Values.CopyTo(_WRsCopy, 0);
} }
finally finally
{ {
Expand Down Expand Up @@ -323,7 +318,7 @@ public void Clear()


Dictionary.Clear(); Dictionary.Clear();


_WRsCopy = new WeakReference[0]; _ScanQueue = new LockFreeQueue<WeakReference>();
} }
finally finally
{ {
Expand All @@ -343,21 +338,27 @@ public IEnumerable<TValue> Values
Lock.EnterReadLock(); Lock.EnterReadLock();
try try
{ {
CheckForCleanup();

toEnumerate = Enumerable<WeakReference>.FastCopy(Dictionary.Values); toEnumerate = Enumerable<WeakReference>.FastCopy(Dictionary.Values);
} }
finally finally
{ {
Lock.ExitReadLock(); Lock.ExitReadLock();
} }


bool queuedCleanup = false;
foreach (WeakReference weakReference in toEnumerate) foreach (WeakReference weakReference in toEnumerate)
{ {
object toYield = weakReference.Target; object toYield = weakReference.Target;


if (null != toYield) if (null != toYield)
yield return (TValue)toYield; yield return (TValue)toYield;

// This is done instead of calling CheckForCleanup because each WR is checked
else if (!queuedCleanup)
{
queuedCleanup = true;
ThreadPool.QueueUserWorkItem(DoCleanup);
}
} }
} }
} }
Expand Down

0 comments on commit d2eaf9a

Please sign in to comment.