-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
ObjectPool.cs
242 lines (218 loc) · 7.96 KB
/
ObjectPool.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ObjectPool.cs">
// Copyright (c) by respective owners including Yahoo!, Microsoft, and
// individual contributors. All rights reserved. Released under a BSD
// license as described in the file LICENSE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Threading;
namespace VW
{
/// <summary>
/// Thread-safe object pool supporting versioned updates.
/// </summary>
/// <typeparam name="TSource">The disposable context needed to create objects of <typeparamref name="TObject"/>.</typeparam>
/// <typeparam name="TObject">The type of the objects to be created.</typeparam>
public class ObjectPool<TSource, TObject> : IDisposable
where TSource : class, IDisposable
where TObject : class, IDisposable
{
/// <summary>
/// Lock resources
/// </summary>
private readonly ReaderWriterLockSlim rwLockSlim;
/// <summary>
/// Version of the factory function.
/// </summary>
private int version;
/// <summary>
/// Used to create new pooled objects.
/// </summary>
private ObjectFactory<TSource, TObject> factory;
/// <summary>
/// The actual pool.
/// </summary>
/// <remarks>
/// To maximize reuse of previously cached items within the pooled objects.
/// (e.g. cached action dependent features)
/// </remarks>
private Stack<PooledObject<TSource, TObject>> pool;
/// <summary>
/// Initializes a new ObjectPool.
/// </summary>
/// <param name="factory">
/// An optional factory to create pooled objects on demand.
/// <see cref="GetOrCreate()"/> will throw if the factory is still null when called.
/// </param>
public ObjectPool(ObjectFactory<TSource, TObject> factory = null)
{
this.rwLockSlim = new ReaderWriterLockSlim();
this.pool = new Stack<PooledObject<TSource, TObject>>();
this.factory = factory;
}
/// <summary>
/// Updates the object factory in a thread-safe manner.
/// </summary>
/// <param name="factory">The new object factory to be used.</param>
public void UpdateFactory(ObjectFactory<TSource, TObject> factory)
{
Stack<PooledObject<TSource, TObject>> oldPool;
ObjectFactory<TSource, TObject> oldFactory;
this.rwLockSlim.EnterWriteLock();
try
{
if (this.pool == null)
{
throw new ObjectDisposedException("ObjectPool already disposed");
}
this.version++;
oldFactory = this.factory;
this.factory = factory;
oldPool = this.pool;
this.pool = new Stack<PooledObject<TSource, TObject>>();
}
finally
{
this.rwLockSlim.ExitWriteLock();
}
// dispose outdated items
foreach (var item in oldPool)
{
item.Value.Dispose();
}
// dispose factory
if (oldFactory != null)
{
oldFactory.Dispose();
}
}
/// <summary>
/// Returns an instance of TObject from the pool or creates a new instance using the objectFactory
/// if the pool is empty.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
public PooledObject<TSource, TObject> GetOrCreate()
{
int localVersion;
ObjectFactory<TSource, TObject> localFactory;
this.rwLockSlim.EnterUpgradeableReadLock();
try
{
if (this.pool == null)
{
throw new ObjectDisposedException("ObjectPool already disposed");
}
if (this.pool.Count == 0)
{
// create a consistent copy
localVersion = this.version;
localFactory = this.factory;
}
else
{
this.rwLockSlim.EnterWriteLock();
try
{
if (this.pool == null)
{
throw new ObjectDisposedException("ObjectPool already disposed");
}
return this.pool.Pop();
}
finally
{
this.rwLockSlim.ExitWriteLock();
}
}
}
finally
{
this.rwLockSlim.ExitUpgradeableReadLock();
}
if (localFactory == null)
{
throw new InvalidOperationException("Factory must be initialized before calling Get()");
}
// invoke the factory outside of the lock
return new PooledObject<TSource, TObject>(this, localVersion, localFactory.Create());
}
/// <summary>
/// Returns <paramref name="pooledObject"/> to the pool of objects, given the version is still the same.
/// Otherwise <paramref name="pooledObject"/> is disposed.
/// </summary>
/// <param name="pooledObject">The object to be returned.</param>
internal void ReturnObject(PooledObject<TSource, TObject> pooledObject)
{
Contract.Ensures(pooledObject != null);
this.rwLockSlim.EnterUpgradeableReadLock();
try
{
if (this.version == pooledObject.Version && this.pool != null)
{
this.rwLockSlim.EnterWriteLock();
try
{
// double check
if (this.version == pooledObject.Version && this.pool != null)
{
// it's the same version, return to pool
this.pool.Push(pooledObject);
return;
}
}
finally
{
this.rwLockSlim.ExitWriteLock();
}
}
}
finally
{
this.rwLockSlim.ExitUpgradeableReadLock();
}
// outdated
pooledObject.Value.Dispose();
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
this.rwLockSlim.EnterWriteLock();
try
{
// Dispose pool items
if (this.pool != null)
{
foreach (var item in this.pool)
{
item.Value.Dispose();
}
this.pool = null;
}
// Dispose factory
if (this.factory != null)
{
this.factory.Dispose();
this.factory = null;
}
}
finally
{
this.rwLockSlim.ExitWriteLock();
}
}
}
}
}