Created
December 2, 2013 12:38
-
-
Save shadeglare/7748874 to your computer and use it in GitHub Desktop.
We call it the Pool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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