Skip to content

Instantly share code, notes, and snippets.

@shadeglare
Created December 2, 2013 12:38
Show Gist options
  • Save shadeglare/7748874 to your computer and use it in GitHub Desktop.
Save shadeglare/7748874 to your computer and use it in GitHub Desktop.
We call it the Pool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TravelAgency.Infrastructure.Pooling
{
public sealed class ResourcePool<T> : IDisposable
where T : ResourceWrapper<T>
{
public event Action<ResourceWrapper<T>, DateTime> ResourceLastUseChanged = (resource, lastUse) => { };
public event Action<ResourceWrapper<T>, Int32> ResourceOperationCountChanged = (resource, operationCount) => { };
private IFactory<IResourceDisposer<T>> ResourceDisposerFactory { get; set; }
private IFactory<T> ResourceWrapperFactory { get; set; }
private ResourceQueue<T> ResourceQueue { get; set; }
private TimeSpan _resourceTTL = TimeSpan.FromMinutes(90);
public TimeSpan ResourceTTL
{
get
{
return _resourceTTL;
}
set
{
if (value < MinResourceTTL)
{
throw new ArgumentException("Resource TTL can not be less than MinResourceTTL");
}
_resourceTTL = value;
}
}
public TimeSpan MinResourceTTL
{
get
{
return TimeSpan.FromMinutes(1);
}
}
private Int32 _poolSize;
public Int32 PoolSize
{
get
{
return _poolSize;
}
private set
{
if (value < 1)
{
throw new ArgumentException("Pool size can not be less than 1");
}
_poolSize = value;
}
}
private Int32 _resourceMaxOperationCount;
public Int32 ResourceMaxOperationCount
{
get
{
return _resourceMaxOperationCount;
}
private set
{
if (value < 1)
{
throw new ArgumentException("Resource max operation count can not be less than 1");
}
_resourceMaxOperationCount = value;
}
}
private Boolean Disposed { get; set; }
public void Dispose()
{
if (!Disposed)
{
Disposed = true;
using (var resourceDisposer = ResourceDisposerFactory.CreateInstance())
{
var resourceDisposing = default(Action<IResourceDisposer<T>, T>);
resourceDisposing = (disposer, item) =>
{
item.LastUseChanged -= ResourceWrapper_LastUseChanged;
item.OperationCountChanged -= ResourceWrapper_OperationCountChanged;
};
resourceDisposer.ResourceDisposing += resourceDisposing;
foreach (var item in ResourceQueue)
{
resourceDisposer.DisposeResource(item);
}
resourceDisposing -= resourceDisposing;
ResourceQueue.Enqueued -= ResourceQueue_Enqueued;
}
}
}
public ResourcePool(
ResourcePoolSettings settings,
IFactory<T> resourceWrapperFactory,
IFactory<IResourceDisposer<T>> resourceDisposerFactory)
{
PoolSize = settings.PoolSize;
ResourceTTL = settings.ResourceTTL;
ResourceMaxOperationCount = settings.ResourceMaxOperationCount;
ResourceWrapperFactory = resourceWrapperFactory;
ResourceDisposerFactory = resourceDisposerFactory;
InitializeResourceQueue();
}
private void InitializeResourceQueue()
{
ResourceQueue = new ResourceQueue<T>();
for (var i = 0; i < PoolSize; i++)
{
var resourceWrapper = ResourceWrapperFactory.CreateInstance();
ResourceQueue.Enqueue(resourceWrapper);
}
ResourceQueue.Enqueued += ResourceQueue_Enqueued;
}
private T CreateAndInitializeResourceWrapper()
{
var resourceWrapper = ResourceWrapperFactory.CreateInstance();
resourceWrapper.OperationCount = 0;
resourceWrapper.LastUse = DateTime.Now;
resourceWrapper.OperationCountChanged += ResourceWrapper_OperationCountChanged;
resourceWrapper.LastUseChanged += ResourceWrapper_LastUseChanged;
return resourceWrapper;
}
public async Task<T> GetResourceAsync(Int32 retryCount = 1, Int32 retryDelay = 100)
{
if (retryCount < 1)
{
throw new ArgumentException("retryCount can not be less than 1");
}
if (retryDelay < 100)
{
throw new ArgumentException("retryDelay can not be less than 100msec");
}
var attemptCount = retryCount;
var item = default(T);
var task = Task.Factory.StartNew<T>(() =>
{
var result = default(T);
do
{
if (!ResourceQueue.TryDequeue(out result))
{
Thread.Sleep(retryDelay);
attemptCount--;
}
else
{
if (result.OperationCount > ResourceMaxOperationCount ||
DateTime.Now - result.LastUse > ResourceTTL)
{
CleanResource(result);
result = ResourceWrapperFactory.CreateInstance();
}
}
} while (attemptCount > 0);
return result;
});
item = await task;
if (item == default(T))
{
throw new InvalidOperationException("Can not get free resource");
}
return item;
}
private void CleanResource(T resource)
{
resource.LastUseChanged -= ResourceWrapper_LastUseChanged;
resource.OperationCountChanged -= ResourceWrapper_OperationCountChanged;
}
public void PutResource(T resource, Boolean resurrect)
{
if (resurrect)
{
CleanResource(resource);
var newResouce = ResourceWrapperFactory.CreateInstance();
ResourceQueue.Enqueue(newResouce, true);
}
else
{
ResourceQueue.Enqueue(resource);
}
}
void ResourceQueue_Enqueued(IQueue<T> queue, T resource)
{
resource.LastUse = DateTime.Now;
resource.OperationCount++;
}
private void ResourceWrapper_LastUseChanged(ResourceWrapper<T> resource, DateTime lastUse)
{
ResourceLastUseChanged(resource, lastUse);
}
private void ResourceWrapper_OperationCountChanged(ResourceWrapper<T> resource, Int32 operationCount)
{
ResourceOperationCountChanged(resource, operationCount);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment