#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
namespace Nuclex.Support.Collections {
/// Pool that recycles objects in order to avoid garbage build-up
/// Type of objects being pooled
///
///
/// Use this class to recycle objects instead of letting them become garbage,
/// creating new instances each time. The Pool class is designed to either be
/// used on its own or as a building block for a static class that wraps it.
///
///
/// Special care has to be taken to revert the entire state of a recycled
/// object when it is returned to the pool. For example, events will need to
/// have their subscriber lists emptied to avoid sending out events to the
/// wrong subscribers and accumulating more and more subscribers each time
/// they are reused.
///
///
/// To simplify such cleanup, pooled objects can implement the IRecyclable
/// interface. When an object is returned to the pool, the pool will
/// automatically call its IRecyclable.Recycle() method.
///
///
public class Pool {
/// Default number of recyclable objects the pool will store
public const int DefaultPoolSize = 64;
/// Initializes a new pool using the default capacity
public Pool() : this(DefaultPoolSize, null, null) { }
/// Initializes a new pool using the default capacity
/// Delegate that will be used to create new items
public Pool(Func createNewDelegate) :
this(DefaultPoolSize, createNewDelegate, null) { }
/// Initializes a new pool using the default capacity
/// Delegate that will be used to create new items
/// Delegate that will be used to recycle items
public Pool(Func createNewDelegate, Action recycleDelegate) :
this(DefaultPoolSize, createNewDelegate, recycleDelegate) { }
/// Initializes a new pool using a user-specified capacity
/// Capacity of the pool
public Pool(int capacity) :
this(capacity, null, null) { }
/// Initializes a new pool using a user-specified capacity
/// Capacity of the pool
/// Delegate that will be used to create new items
public Pool(int capacity, Func createNewDelegate) :
this(capacity, createNewDelegate, null) { }
/// Initializes a new pool using a user-specified capacity
/// Capacity of the pool
/// Delegate that will be used to create new items
/// Delegate that will be used to recycle items
public Pool(int capacity, Func createNewDelegate, Action recycleDelegate) {
Capacity = capacity;
if(createNewDelegate == null) {
if(!typeof(TItem).HasDefaultConstructor()) {
throw new ArgumentException(
"Type " + typeof(TItem).Name + " has no default constructor and " +
"requires a custom 'create instance' delegate"
);
}
createNewDelegate = new Func(Activator.CreateInstance);
}
if(recycleDelegate == null) {
recycleDelegate = new Action(callRecycleIfSupported);
}
this.createNewDelegate = createNewDelegate;
this.recycleDelegate = recycleDelegate;
}
///
/// Returns a new or recycled instance of the types managed by the pool
///
/// A new or recycled instance
public TItem Get() {
lock(this) {
if(this.items.Count > 0) {
return this.items.Dequeue();
} else {
return this.createNewDelegate();
}
}
}
///
/// Redeems an instance that is no longer used to be recycled by the pool
///
/// The instance that will be redeemed
public void Redeem(TItem item) {
// Call Recycle() when the object is redeemed (instead of when it leaves
// the pool again) in order to eliminate any references the object may hold
// to other objects.
this.recycleDelegate(item);
lock(this) {
if(this.items.Count < this.capacity) {
this.items.Enqueue(item);
}
}
}
/// Number of objects the pool can retain
///
/// Changing this value causes the pool to be emtpied. It is recommended that
/// you only read the pool's capacity, never change it.
///
public int Capacity {
get { return this.capacity; }
set {
this.capacity = value;
this.items = new Queue(value);
}
}
///
/// Calls the Recycle() method on an objects if it implements
/// the IRecyclable interface
///
///
/// Object whose Recycle() method will be called if supported by the object
///
private static void callRecycleIfSupported(TItem item) {
IRecyclable recycleable = item as IRecyclable;
if(recycleable != null) {
recycleable.Recycle();
}
}
/// Objects being retained for recycling
private Queue items;
/// Capacity of the pool
///
/// Required because the Queue class doesn't allow this value to be retrieved
///
private int capacity;
/// Delegate used to create new instances of the pool's type
private Func createNewDelegate;
/// Delegate used to recycle instances
private Action recycleDelegate;
}
} // namespace Nuclex.Support.Collections