152
|
1 using System;
|
|
2 using Implab.Parallels;
|
|
3 using System.Threading;
|
|
4 using System.Diagnostics;
|
|
5 using System.Diagnostics.CodeAnalysis;
|
|
6
|
153
|
7 namespace Implab.Components {
|
|
8 /// <summary>
|
256
|
9 /// The base class for implementing thread-safe pools of disposable objects.
|
153
|
10 /// </summary>
|
|
11 /// <remarks>
|
256
|
12 /// <para>This class maintains a set of pre-created objects which are frequently allocated and released
|
153
|
13 /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed,
|
|
14 /// if the pool is empty it will create new objects on demand.</para>
|
|
15 /// <para>Instances of this class are thread-safe.</para>
|
|
16 /// </remarks>
|
152
|
17 public abstract class DisposablePool<T> : IDisposable {
|
|
18 readonly int m_size;
|
|
19 readonly AsyncQueue<T> m_queue = new AsyncQueue<T>();
|
|
20
|
|
21 [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
|
|
22 static readonly bool _isValueType = typeof(T).IsValueType;
|
|
23
|
|
24 bool m_disposed;
|
|
25
|
|
26 int m_count;
|
|
27
|
|
28 protected DisposablePool(int size) {
|
|
29 m_size = size;
|
|
30 }
|
|
31
|
|
32 protected DisposablePool() : this(Environment.ProcessorCount+1) {
|
|
33 }
|
|
34
|
|
35 public T Allocate() {
|
|
36 if (m_disposed)
|
|
37 throw new ObjectDisposedException(ToString());
|
|
38
|
|
39 T instance;
|
|
40 if (m_queue.TryDequeue(out instance)) {
|
|
41 Interlocked.Decrement(ref m_count);
|
|
42 } else {
|
|
43 instance = CreateInstance();
|
|
44 Debug.Assert(!Object.Equals(instance, default(T)) || _isValueType);
|
|
45 }
|
|
46 return instance;
|
|
47 }
|
|
48
|
|
49 protected abstract T CreateInstance();
|
|
50
|
|
51 protected virtual void CleanupInstance(T instance) {
|
|
52 }
|
|
53
|
|
54 public void Release(T instance) {
|
|
55 if ( Object.Equals(instance,default(T)) && !_isValueType)
|
|
56 return;
|
|
57
|
|
58 Thread.MemoryBarrier();
|
|
59 if (m_count < m_size && !m_disposed) {
|
|
60 Interlocked.Increment(ref m_count);
|
|
61
|
|
62 CleanupInstance(instance);
|
|
63
|
|
64 m_queue.Enqueue(instance);
|
|
65
|
|
66 // пока элемент возвращался в кеш, была начата операция освобождения всего кеша
|
|
67 // и возможно уже законцена, в таком случае следует извлечь элемент обратно и
|
|
68 // освободить его. Если операция освобождения кеша еще не заврешилась, то будет
|
|
69 // изъят и освобожден произвольный элемен, что не повлияет на ход всего процесса.
|
|
70 if (m_disposed && m_queue.TryDequeue(out instance) && instance is IDisposable)
|
|
71 ((IDisposable)instance).Dispose() ;
|
|
72
|
|
73 } else {
|
153
|
74 var disposable = instance as IDisposable;
|
|
75 if (disposable != null)
|
|
76 disposable.Dispose();
|
152
|
77 }
|
|
78 }
|
|
79
|
|
80 protected virtual void Dispose(bool disposing) {
|
|
81 if (disposing) {
|
|
82 m_disposed = true;
|
|
83 T instance;
|
153
|
84 while (m_queue.TryDequeue(out instance)) {
|
|
85 var disposable = instance as IDisposable;
|
|
86 if (disposable != null)
|
|
87 disposable.Dispose();
|
|
88 }
|
152
|
89 }
|
|
90 }
|
|
91
|
|
92 #region IDisposable implementation
|
|
93
|
|
94 public void Dispose() {
|
|
95 Dispose(true);
|
|
96 GC.SuppressFinalize(this);
|
|
97 }
|
|
98
|
|
99 #endregion
|
|
100 }
|
|
101 }
|
|
102
|