DisposablePool.cs
102 lines
| 3.6 KiB
| text/x-csharp
|
CSharpLexer
|
|
r152 | using System; | ||
| using Implab.Parallels; | ||||
| using System.Threading; | ||||
| using System.Diagnostics; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
|
|
r153 | namespace Implab.Components { | ||
| /// <summary> | ||||
|
|
r256 | /// The base class for implementing thread-safe pools of disposable objects. | ||
|
|
r153 | /// </summary> | ||
| /// <remarks> | ||||
|
|
r256 | /// <para>This class maintains a set of pre-created objects which are frequently allocated and released | ||
|
|
r153 | /// 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> | ||||
|
|
r152 | 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 { | ||||
|
|
r153 | var disposable = instance as IDisposable; | ||
| if (disposable != null) | ||||
| disposable.Dispose(); | ||||
|
|
r152 | } | ||
| } | ||||
| protected virtual void Dispose(bool disposing) { | ||||
| if (disposing) { | ||||
| m_disposed = true; | ||||
| T instance; | ||||
|
|
r153 | while (m_queue.TryDequeue(out instance)) { | ||
| var disposable = instance as IDisposable; | ||||
| if (disposable != null) | ||||
| disposable.Dispose(); | ||||
| } | ||||
|
|
r152 | } | ||
| } | ||||
| #region IDisposable implementation | ||||
| public void Dispose() { | ||||
| Dispose(true); | ||||
| GC.SuppressFinalize(this); | ||||
| } | ||||
| #endregion | ||||
| } | ||||
| } | ||||
