|
|
using System;
|
|
|
using Implab.Parallels;
|
|
|
using System.Threading;
|
|
|
using System.Diagnostics;
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
|
namespace Implab.Components {
|
|
|
/// <summary>
|
|
|
/// The base class for implementing thread-safe pools of disposable objects.
|
|
|
/// </summary>
|
|
|
/// <remarks>
|
|
|
/// <para>This class maintains a set of pre-created objects which are frequently allocated and released
|
|
|
/// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed,
|
|
|
/// if the pool is empty it will create new objects on demand.</para>
|
|
|
/// <para>Instances of this class are thread-safe.</para>
|
|
|
/// </remarks>
|
|
|
public abstract class DisposablePool<T> : IDisposable {
|
|
|
readonly int m_size;
|
|
|
readonly AsyncQueue<T> m_queue = new AsyncQueue<T>();
|
|
|
|
|
|
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
|
|
|
static readonly bool _isValueType = typeof(T).IsValueType;
|
|
|
|
|
|
bool m_disposed;
|
|
|
|
|
|
int m_count;
|
|
|
|
|
|
protected DisposablePool(int size) {
|
|
|
m_size = size;
|
|
|
}
|
|
|
|
|
|
protected DisposablePool() : this(Environment.ProcessorCount+1) {
|
|
|
}
|
|
|
|
|
|
public T Allocate() {
|
|
|
if (m_disposed)
|
|
|
throw new ObjectDisposedException(ToString());
|
|
|
|
|
|
T instance;
|
|
|
if (m_queue.TryDequeue(out instance)) {
|
|
|
Interlocked.Decrement(ref m_count);
|
|
|
} else {
|
|
|
instance = CreateInstance();
|
|
|
Debug.Assert(!Object.Equals(instance, default(T)) || _isValueType);
|
|
|
}
|
|
|
return instance;
|
|
|
}
|
|
|
|
|
|
protected abstract T CreateInstance();
|
|
|
|
|
|
protected virtual void CleanupInstance(T instance) {
|
|
|
}
|
|
|
|
|
|
public void Release(T instance) {
|
|
|
if ( Object.Equals(instance,default(T)) && !_isValueType)
|
|
|
return;
|
|
|
|
|
|
Thread.MemoryBarrier();
|
|
|
if (m_count < m_size && !m_disposed) {
|
|
|
Interlocked.Increment(ref m_count);
|
|
|
|
|
|
CleanupInstance(instance);
|
|
|
|
|
|
m_queue.Enqueue(instance);
|
|
|
|
|
|
// пока элемент возвращался в кеш, была начата операция освобождения всего кеша
|
|
|
// и возможно уже законцена, в таком случае следует извлечь элемент обратно и
|
|
|
// освободить его. Если операция освобождения кеша еще не заврешилась, то будет
|
|
|
// изъят и освобожден произвольный элемен, что не повлияет на ход всего процесса.
|
|
|
if (m_disposed && m_queue.TryDequeue(out instance) && instance is IDisposable)
|
|
|
((IDisposable)instance).Dispose() ;
|
|
|
|
|
|
} else {
|
|
|
var disposable = instance as IDisposable;
|
|
|
if (disposable != null)
|
|
|
disposable.Dispose();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected virtual void Dispose(bool disposing) {
|
|
|
if (disposing) {
|
|
|
m_disposed = true;
|
|
|
T instance;
|
|
|
while (m_queue.TryDequeue(out instance)) {
|
|
|
var disposable = instance as IDisposable;
|
|
|
if (disposable != null)
|
|
|
disposable.Dispose();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#region IDisposable implementation
|
|
|
|
|
|
public void Dispose() {
|
|
|
Dispose(true);
|
|
|
GC.SuppressFinalize(this);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|