History

Renamed folders and separated GSF.Media and GSF.Net into separate projects.

StephenCWills committed 14 years ago

Commit ac1ef661 parent 45bce55628 files · +733 −461Browse files

Showing the changes to Source/Libraries/GSF.Core/ReusableObjectPool.cs (1 of 628 files). Show all

Unified · Split

Source/Libraries/GSF.Core/ReusableObjectPool.cs+505−0
@@ -0,0 +1,505 @@
1//******************************************************************************************************
2// ReusableObjectPool.cs - Gbtc
3//
4// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
5//
6// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9// not use this file except in compliance with the License. You may obtain a copy of the License at:
10//
11// http://www.opensource.org/licenses/eclipse-1.0.php
12//
13// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15// License for the specific language governing permissions and limitations.
16//
17// Code Modification History:
18// ----------------------------------------------------------------------------------------------------
19// 11/23/2011 - J. Ritchie Carroll
20// Generated original version of source code.
21// 10/8/2012 - Danyelle Gilliam
22// Modified Header
23//
24//******************************************************************************************************
25
26
27
28
29#region [ Contributor License Agreements ]
30
31//******************************************************************************************************
32//
33// Copyright © 2011, Grid Protection Alliance. All Rights Reserved.
34//
35// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
36// not use this file except in compliance with the License. You may obtain a copy of the License at:
37//
38// http://www.opensource.org/licenses/eclipse-1.0.php
39//
40// Unless agreed to in writing, the subject software distributed under the License is distributed on an
41// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
42// License for the specific language governing permissions and limitations.
43//
44//******************************************************************************************************
45
46#endregion
47
48using System;
49using System.Collections.Concurrent;
50
51namespace GSF
52{
53 /// <summary>
54 /// Represents a reusable object pool that can be used by an application.
55 /// </summary>
56 /// <remarks>
57 /// <para>
58 /// Object pooling can offer a significant performance boost in some scenarios. It is most effective when the cost
59 /// of construction is high and/or the rate of instantiation is high.
60 /// </para>
61 /// <para>
62 /// This object pool is statically created at application startup, one per type, and is available for all components
63 /// and classes within an application domain. Every time you need to use an object, you take one from the pool, use
64 /// it, then return it to the pool when done. This process can be much faster than creating a new object every time
65 /// you need to use one.
66 /// </para>
67 /// <para>
68 /// When objects implement <see cref="ISupportLifecycle"/>, the <see cref="ReusableObjectPool{T}"/> will automatically
69 /// attach to the <see cref="ISupportLifecycle.Disposed"/> event and return the object to the pool when it's disposed.
70 /// When an object is taken from the pool, the <see cref="ISupportLifecycle.Initialize"/> method will be called to
71 /// reinstantiate any need member variables. If class tracks a disposed flag, it should be reset during call to the
72 /// <see cref="ISupportLifecycle.Initialize"/> method so that class will be "un-disposed". Also, typical dispose
73 /// implementations call <see cref="GC.SuppressFinalize"/> in the <see cref="IDisposable.Dispose"/> method, as such the
74 /// <see cref="GC.ReRegisterForFinalize"/> should be called during <see cref="ISupportLifecycle.Initialize"/> method to
75 /// make sure class will be disposed by finalizer in case <see cref="IDisposable.Dispose"/> method is never called.
76 /// <example>
77 /// Here is an example class that implements <see cref="ISupportLifecycle"/> such that it will be automatically returned
78 /// to the pool when the class is disposed and automatically initialized when taken from the pool:
79 /// <code>
80 /// /// &lt;summary&gt;
81 /// /// Class that shows example implementation of reusable &lt;see cref="ISupportLifecycle"/&gt;.
82 /// /// &lt;/summary&gt;
83 /// public class ReusableClass : ISupportLifecycle
84 /// {
85 /// #region [ Members ]
86 ///
87 /// // Events
88 ///
89 /// /// &lt;summary&gt;
90 /// /// Occurs when the &lt;see cref="ReusableClass"/&gt; is disposed.
91 /// /// &lt;/summary&gt;
92 /// public event EventHandler Disposed;
93 ///
94 /// // Fields
95 /// private System.Timers.Timer m_timer; // Example member variable that needs initialization and disposal
96 /// private bool m_enabled;
97 /// private bool m_disposed;
98 ///
99 /// #endregion
100 ///
101 /// #region [ Constructors ]
102 ///
103 /// /// &lt;summary&gt;
104 /// /// Releases the unmanaged resources before the &lt;see cref="ReusableClass"/&gt; object is reclaimed by &lt;see cref="GC"/&gt;.
105 /// /// &lt;/summary&gt;
106 /// ~ReusableClass()
107 /// {
108 /// Dispose(false);
109 /// }
110 ///
111 /// #endregion
112 ///
113 /// #region [ Properties ]
114 ///
115 /// /// &lt;summary&gt;
116 /// /// Gets or sets a boolean value that indicates whether the &lt;see cref="ReusableClass"/&gt; object is currently enabled.
117 /// /// &lt;/summary&gt;
118 /// public bool Enabled
119 /// {
120 /// get
121 /// {
122 /// return m_enabled;
123 /// }
124 /// set
125 /// {
126 /// m_enabled = value;
127 ///
128 /// if (m_timer != null)
129 /// m_timer.Enabled = m_enabled;
130 /// }
131 /// }
132 ///
133 /// #endregion
134 ///
135 /// #region [ Methods ]
136 ///
137 /// /// &lt;summary&gt;
138 /// /// Initializes the &lt;see cref="ReusableClass"/&gt; object.
139 /// /// &lt;/summary&gt;
140 /// public void Initialize()
141 /// {
142 /// // Undispose class instance if it is being reused
143 /// if (m_disposed)
144 /// {
145 /// m_disposed = false;
146 /// GC.ReRegisterForFinalize(this);
147 /// }
148 ///
149 /// m_enabled = true;
150 ///
151 /// // Initialize member variables
152 /// if (m_timer == null)
153 /// {
154 /// m_timer = new System.Timers.Timer(2000.0D);
155 /// m_timer.Elapsed += m_timer_Elapsed;
156 /// m_timer.Enabled = m_enabled;
157 /// }
158 /// }
159 ///
160 /// /// &lt;summary&gt;
161 /// /// Releases all the resources used by the &lt;see cref="ReusableClass"/&gt; object.
162 /// /// &lt;/summary&gt;
163 /// public void Dispose()
164 /// {
165 /// Dispose(true);
166 /// GC.SuppressFinalize(this);
167 /// }
168 ///
169 /// /// &lt;summary&gt;
170 /// /// Releases the unmanaged resources used by the &lt;see cref="ReusableClass"/&gt; object and optionally releases the managed resources.
171 /// /// &lt;/summary&gt;
172 /// /// &lt;param name="disposing"&gt;true to release both managed and unmanaged resources; false to release only unmanaged resources.&lt;/param&gt;
173 /// protected virtual void Dispose(bool disposing)
174 /// {
175 /// if (!m_disposed)
176 /// {
177 /// try
178 /// {
179 /// if (disposing)
180 /// {
181 /// // Dispose member variables
182 /// if (m_timer != null)
183 /// {
184 /// m_timer.Elapsed -= m_timer_Elapsed;
185 /// m_timer.Dispose();
186 /// }
187 /// m_timer = null;
188 ///
189 /// m_enabled = false;
190 /// }
191 /// }
192 /// finally
193 /// {
194 /// m_disposed = true;
195 ///
196 /// if (Disposed != null)
197 /// Disposed(this, EventArgs.Empty);
198 /// }
199 /// }
200 /// }
201 ///
202 /// // Handle timer event
203 /// private void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
204 /// {
205 /// // Do work here...
206 /// }
207 ///
208 /// #endregion
209 /// }
210 /// </code>
211 /// </example>
212 /// </para>
213 /// <para>
214 /// If the pool objects do not implement <see cref="ISupportLifecycle"/> you can still use the object pool, but it
215 /// will be very imporant to return the object to the pool when you are finished using it. If you are using an object
216 /// scoped within a method, make sure to use a try/finally so that you can take the object within the try and return
217 /// the object within the finally. If you are using an object as a member scoped class field, make sure you use the
218 /// standard dispose pattern and return the object during the <see cref="IDisposable.Dispose"/> method call. In this
219 /// mode you will also need to manually initialze your object after you get it from the pool to reset its state.
220 /// </para>
221 /// </remarks>
222 /// <typeparam name="T">Type of object to pool.</typeparam>
223 public class ReusableObjectPool<T> where T : class, new()
224 {
225 private ConcurrentQueue<T> m_objectPool = new ConcurrentQueue<T>();
226
227 /// <summary>
228 /// Gets an object from the pool, or creates a new one if no pool items are available.
229 /// </summary>
230 /// <returns>An available object from the pool, or a new one if no pool items are available.</returns>
231 /// <remarks>
232 /// If type of <typeparamref name="T"/> implements <see cref="ISupportLifecycle"/>, the pool will attach
233 /// to the item's <see cref="ISupportLifecycle.Disposed"/> event such that the object can be automatically
234 /// restored to the pool upon <see cref="IDisposable.Dispose"/>. It will be up to class implementors to
235 /// make sure <see cref="ISupportLifecycle.Initialize"/> makes the class ready for use as this method will
236 /// always be called for an object being taken from the pool.
237 /// </remarks>
238 public T TakeObject()
239 {
240 T item;
241 bool newItem = false;
242
243 // Attempt to provide user with a queued item
244 if (!m_objectPool.TryDequeue(out item))
245 {
246 // No items are available, create a new item for the object pool
247 item = FastObjectFactory<T>.CreateObjectFunction();
248 newItem = true;
249 }
250
251 // Automatically handle class life cycle if item implements support for this
252 ISupportLifecycle lifecycleItem = item as ISupportLifecycle;
253
254 if (lifecycleItem != null)
255 {
256 // Attach to dispose event so item can be automatically returned to the pool
257 if (newItem)
258 lifecycleItem.Disposed += LifecycleItem_Disposed;
259
260 // Initialize or reinitialize (i.e., un-dispose) the item
261 lifecycleItem.Initialize();
262 }
263
264 return item;
265 }
266
267 /// <summary>
268 /// Returns object to the pool.
269 /// </summary>
270 /// <param name="item">Reference to the object being returned.</param>
271 /// <remarks>
272 /// If type of <typeparamref name="T"/> implements <see cref="ISupportLifecycle"/>, the pool will automatically
273 /// return the item to the pool when the <see cref="ISupportLifecycle.Disposed"/> event is raised, usually when
274 /// the object's <see cref="IDisposable.Dispose"/> method is called.
275 /// </remarks>
276 public void ReturnObject(T item)
277 {
278 if (item != null)
279 m_objectPool.Enqueue(item);
280 }
281
282 /// <summary>
283 /// Releases all the objects currently cached in the pool.
284 /// </summary>
285 public void Clear()
286 {
287 T item;
288
289 while (!m_objectPool.IsEmpty)
290 {
291 m_objectPool.TryDequeue(out item);
292 }
293 }
294
295 /// <summary>
296 /// Allocates the pool to the desired <paramref name="size"/>.
297 /// </summary>
298 /// <param name="size">Desired pool size.</param>
299 /// <exception cref="ArgumentOutOfRangeException">Pool <paramref name="size"/> must at least be one.</exception>
300 public void SetPoolSize(int size)
301 {
302 if (size < 1)
303 throw new ArgumentOutOfRangeException("size", "pool size must at least be one");
304
305 for (int i = 0; i < size - m_objectPool.Count; i++)
306 {
307 T item = FastObjectFactory<T>.CreateObjectFunction();
308
309 // Automatically handle life cycle if item implements support for this
310 ISupportLifecycle lifecycleItem = item as ISupportLifecycle;
311
312 // Attach to dispose event so item can be automatically returned to the pool
313 if (lifecycleItem != null)
314 lifecycleItem.Disposed += LifecycleItem_Disposed;
315
316 m_objectPool.Enqueue(item);
317 }
318 }
319
320 /// <summary>
321 /// Gets the number of items currently contained in the pool.
322 /// </summary>
323 /// <returns>The size of the pool.</returns>
324 public int GetPoolSize()
325 {
326 return m_objectPool.Count;
327 }
328
329 // Handler to automatically to return items to the queue
330 private void LifecycleItem_Disposed(object sender, EventArgs e)
331 {
332 ReturnObject(sender as T);
333 }
334
335 /// <summary>
336 /// Default static instance which can be shared throughout the application.
337 /// </summary>
338 public static readonly ReusableObjectPool<T> Default = new ReusableObjectPool<T>();
339 }
340
341 /// <summary>
342 /// Represents a reusable object pool that can be used by an application.
343 /// </summary>
344 /// <remarks>
345 /// <para>
346 /// See <see cref="ReusableObjectPool{T}"/> for more details on using object pooling.
347 /// </para>
348 /// <para>
349 /// <see cref="ReusableObjectPool"/> should be used when you only have the <see cref="Type"/> of an object available (such as when you are
350 /// using reflection), otherwise you should use the generic <see cref="ReusableObjectPool{T}"/>.
351 /// </para>
352 /// </remarks>
353 public class ReusableObjectPool
354 {
355 private static ConcurrentDictionary<Type, ConcurrentQueue<object>> s_objectPools = new ConcurrentDictionary<Type, ConcurrentQueue<object>>();
356
357 /// <summary>
358 /// Gets an object from the pool, or creates a new one if no pool items are available.
359 /// </summary>
360 /// <param name="type">Type of object to get from pool.</param>
361 /// <returns>An available object from the pool, or a new one if no pool items are available.</returns>
362 /// <exception cref="InvalidOperationException"><paramref name="type"/> does not support parameterless public constructor.</exception>
363 /// <remarks>
364 /// If <paramref name="type"/> implements <see cref="ISupportLifecycle"/>, the pool will attach
365 /// to the item's <see cref="ISupportLifecycle.Disposed"/> event such that the object can be automatically
366 /// restored to the pool upon <see cref="IDisposable.Dispose"/>. It will be up to class implementors to
367 /// make sure <see cref="ISupportLifecycle.Initialize"/> makes the class ready for use as this method will
368 /// always be called for an object being taken from the pool.
369 /// </remarks>
370 public static object TakeObject(Type type)
371 {
372 return TakeObject<object>(type);
373 }
374
375 /// <summary>
376 /// Gets an object from the pool, or creates a new one if no pool items are available.
377 /// </summary>
378 /// <param name="type">Type of object to get from pool.</param>
379 /// <typeparam name="T">Type of returned object to get from pool.</typeparam>
380 /// <returns>An available object from the pool, or a new one if no pool items are available.</returns>
381 /// <exception cref="InvalidOperationException">
382 /// <paramref name="type"/> does not support parameterless public constructor -or-
383 /// <paramref name="type"/> is not a subclass or interface implementation of function type definition.
384 /// </exception>
385 /// <remarks>
386 /// <para>
387 /// This function will validate that <typeparamref name="T"/> is related to <paramref name="type"/>.
388 /// </para>
389 /// <para>
390 /// If <paramref name="type"/> implements <see cref="ISupportLifecycle"/>, the pool will attach
391 /// to the item's <see cref="ISupportLifecycle.Disposed"/> event such that the object can be automatically
392 /// restored to the pool upon <see cref="IDisposable.Dispose"/>. It will be up to class implementors to
393 /// make sure <see cref="ISupportLifecycle.Initialize"/> makes the class ready for use as this method will
394 /// always be called for an object being taken from the pool.
395 /// </para>
396 /// </remarks>
397 public static T TakeObject<T>(Type type)
398 {
399 object item;
400 bool newItem = false;
401
402 // Get or create object pool associated with specified type
403 ConcurrentQueue<object> objectPool = s_objectPools.GetOrAdd(type, (objType) => new ConcurrentQueue<object>());
404
405 // Attempt to provide user with a queued item
406 if (!objectPool.TryDequeue(out item))
407 {
408 // No items are available, create a new item for the object pool
409 item = FastObjectFactory.GetCreateObjectFunction<T>(type)();
410 newItem = true;
411 }
412
413 // Automatically handle class life cycle if item implements support for this
414 ISupportLifecycle lifecycleItem = item as ISupportLifecycle;
415
416 if (lifecycleItem != null)
417 {
418 // Attach to dispose event so item can be automatically returned to the pool
419 if (newItem)
420 lifecycleItem.Disposed += LifecycleItem_Disposed;
421
422 // Initialize or reinitialize (i.e., un-dispose) the item
423 lifecycleItem.Initialize();
424 }
425
426 return (T)item;
427 }
428
429 /// <summary>
430 /// Returns object to the pool.
431 /// </summary>
432 /// <param name="item">Reference to the object being returned.</param>
433 /// <remarks>
434 /// If type of <paramref name="item"/> implements <see cref="ISupportLifecycle"/>, the pool will automatically
435 /// return the item to the pool when the <see cref="ISupportLifecycle.Disposed"/> event is raised, usually when
436 /// the object's <see cref="IDisposable.Dispose"/> method is called.
437 /// </remarks>
438 public static void ReturnObject(object item)
439 {
440 if (item != null)
441 {
442 ConcurrentQueue<object> objectPool;
443
444 // Try to get object pool associated with the specified item's type
445 if (s_objectPools.TryGetValue(item.GetType(), out objectPool))
446 objectPool.Enqueue(item);
447 }
448 }
449
450 /// <summary>
451 /// Releases all the objects currently cached in the specified pool.
452 /// </summary>
453 /// <param name="type">Type of pool to clear.</param>
454 public static void Clear(Type type)
455 {
456 ConcurrentQueue<object> objectPool;
457
458 // Try to get object pool associated with the specified type
459 if (s_objectPools.TryGetValue(type, out objectPool))
460 {
461 object item;
462
463 while (!objectPool.IsEmpty)
464 {
465 objectPool.TryDequeue(out item);
466 }
467 }
468 }
469
470 /// <summary>
471 /// Allocates the pool to the desired <paramref name="size"/>.
472 /// </summary>
473 /// <param name="type">Type of pool to initialize.</param>
474 /// <param name="size">Desired pool size.</param>
475 /// <exception cref="ArgumentOutOfRangeException">Pool <paramref name="size"/> must at least be one.</exception>
476 public static void SetPoolSize(Type type, int size)
477 {
478 if (size < 1)
479 throw new ArgumentOutOfRangeException("size", "pool size must at least be one");
480
481 // Get or create object pool associated with specified type
482 ConcurrentQueue<object> objectPool = s_objectPools.GetOrAdd(type, (objType) => new ConcurrentQueue<object>());
483
484 for (int i = 0; i < size - objectPool.Count; i++)
485 {
486 object item = FastObjectFactory.GetCreateObjectFunction(type)();
487
488 // Automatically handle life cycle if item implements support for this
489 ISupportLifecycle lifecycleItem = item as ISupportLifecycle;
490
491 // Attach to dispose event so item can be automatically returned to the pool
492 if (lifecycleItem != null)
493 lifecycleItem.Disposed += LifecycleItem_Disposed;
494
495 objectPool.Enqueue(item);
496 }
497 }
498
499 // Handler to automatically to return items to the queue
500 private static void LifecycleItem_Disposed(object sender, EventArgs e)
501 {
502 ReturnObject(sender);
503 }
504 }
505}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t