Monday, December 10, 2007

Short-lived object manager

I don't know if I'm re-inventing the wheel here, but I refactored a couple of server-side objects to arrive at this base class:




using System;
using System.Collections.Generic;
using System.Threading;

class ShortLived : IDisposable
{
Guid key;
DateTime lastAccess = DateTime.Now;
int disposed = 0;
static Timer gcThread = null;
static Dictionary<Guid, ShortLived> objects = new Dictionary<Guid, ShortLived>();
static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5);
static readonly TimeSpan GcInterval = TimeSpan.FromMinutes(1);
static object syncRoot = new object();

public Guid Key
{
get
{
return key;
}
}

public static object SyncRoot
{
get
{
return syncRoot;
}
}

public static T GetObject<T>(Guid key) where T : ShortLived
{
ShortLived result = null;
if (objects.TryGetValue(key, out result))
return result as T;
return default(T);
}

public static IEnumerable<T> GetObjects<T>() where T : ShortLived
{
List<T> results = new List<T>();
lock (SyncRoot)
{
foreach (ShortLived obj in objects.Values)
{
if (obj is T)
results.Add((T)obj);
}
}
return results;
}

void IDisposable.Dispose()
{
Dispose(Interlocked.CompareExchange(ref disposed, 1, 0) == 1);
}

protected virtual void Dispose(bool disposed)
{
}

protected ShortLived()
{
key = Guid.NewGuid();
Init();
}

protected ShortLived(Guid key)
{
this.key = key;
Init();
}

private void Init()
{
lock (SyncRoot)
objects.Add(key, this);
Timer newCollector = new Timer(GarbageCollect, null, GcInterval, GcInterval);
if (Interlocked.CompareExchange(ref gcThread, newCollector, null) == null)
gcThread.Change(GcInterval, GcInterval);
else
newCollector.Dispose();
}

protected void KeepAlive()
{
lastAccess = DateTime.Now;
}

private static void GarbageCollect(object context)
{
List<Guid> expired = new List<Guid>();
DateTime now = DateTime.Now;
lock (SyncRoot)
{
foreach (KeyValuePair<Guid, ShortLived> kv in objects)
{
if (now - kv.Value.lastAccess > Lifetime)
expired.Add(kv.Key);
}
foreach (Guid key in expired)
{
IDisposable obj = objects[key];
objects.Remove(key);
obj.Dispose();
}
if (objects.Count == 0)
{
gcThread.Change(Timeout.Infinite, Timeout.Infinite);
Interlocked.Exchange(ref gcThread, null);
}
}
}
}

To use, just inherit from ShortLived, and call KeepAlive whenever the object is accessed. Don't store references to your object; store a reference to its Key property, and use that to look it up when you want it again.

0 Comments:

Post a Comment

<< Home