diff options
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling')
10 files changed, 768 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ArrayPool.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ArrayPool.cs new file mode 100644 index 0000000..f7b6300 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ArrayPool.cs @@ -0,0 +1,200 @@ +#if !UNITY_EDITOR +// Extra optimizations when not running in the editor, but less error checking +#define ASTAR_OPTIMIZE_POOLING +#endif + +using System; +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// <summary> + /// Lightweight Array Pool. + /// Handy class for pooling arrays of type T. + /// + /// Usage: + /// - Claim a new array using <code> SomeClass[] foo = ArrayPool<SomeClass>.Claim (capacity); </code> + /// - Use it and do stuff with it + /// - Release it with <code> ArrayPool<SomeClass>.Release (ref foo); </code> + /// + /// Warning: Arrays returned from the Claim method may contain arbitrary data. + /// You cannot rely on it being zeroed out. + /// + /// After you have released a array, you should never use it again, if you do use it + /// your code may modify it at the same time as some other code is using it which + /// will likely lead to bad results. + /// + /// Since: Version 3.8.6 + /// See: Pathfinding.Util.ListPool + /// </summary> + public static class ArrayPool<T> { +#if !ASTAR_NO_POOLING + /// <summary> + /// Maximum length of an array pooled using ClaimWithExactLength. + /// Arrays with lengths longer than this will silently not be pooled. + /// </summary> + const int MaximumExactArrayLength = 256; + + /// <summary> + /// Internal pool. + /// The arrays in each bucket have lengths of 2^i + /// </summary> + static readonly Stack<T[]>[] pool = new Stack<T[]>[31]; + static readonly Stack<T[]>[] exactPool = new Stack<T[]>[MaximumExactArrayLength+1]; +#if !ASTAR_OPTIMIZE_POOLING + static readonly HashSet<T[]> inPool = new HashSet<T[]>(); +#endif +#endif + + /// <summary> + /// Returns an array with at least the specified length. + /// Warning: Returned arrays may contain arbitrary data. + /// You cannot rely on it being zeroed out. + /// + /// The returned array will always be a power of two, or zero. + /// </summary> + public static T[] Claim (int minimumLength) { + if (minimumLength <= 0) { + return ClaimWithExactLength(0); + } + + int bucketIndex = 0; + while ((1 << bucketIndex) < minimumLength && bucketIndex < 30) { + bucketIndex++; + } + + if (bucketIndex == 30) + throw new System.ArgumentException("Too high minimum length"); + +#if !ASTAR_NO_POOLING + lock (pool) { + if (pool[bucketIndex] == null) { + pool[bucketIndex] = new Stack<T[]>(); + } + + if (pool[bucketIndex].Count > 0) { + var array = pool[bucketIndex].Pop(); +#if !ASTAR_OPTIMIZE_POOLING + inPool.Remove(array); +#endif + return array; + } + } +#endif + return new T[1 << bucketIndex]; + } + + /// <summary> + /// Returns an array with the specified length. + /// Use with caution as pooling too many arrays with different lengths that + /// are rarely being reused will lead to an effective memory leak. + /// + /// Use <see cref="Claim"/> if you just need an array that is at least as large as some value. + /// + /// Warning: Returned arrays may contain arbitrary data. + /// You cannot rely on it being zeroed out. + /// </summary> + public static T[] ClaimWithExactLength (int length) { +#if !ASTAR_NO_POOLING + bool isPowerOfTwo = length != 0 && (length & (length - 1)) == 0; + if (isPowerOfTwo) { + // Will return the correct array length + return Claim(length); + } + + if (length <= MaximumExactArrayLength) { + lock (pool) { + Stack<T[]> stack = exactPool[length]; + if (stack != null && stack.Count > 0) { + var array = stack.Pop(); +#if !ASTAR_OPTIMIZE_POOLING + inPool.Remove(array); +#endif + return array; + } + } + } +#endif + return new T[length]; + } + + /// <summary> + /// Pool an array. + /// If the array was got using the <see cref="ClaimWithExactLength"/> method then the allowNonPowerOfTwo parameter must be set to true. + /// The parameter exists to make sure that non power of two arrays are not pooled unintentionally which could lead to memory leaks. + /// </summary> + public static void Release (ref T[] array, bool allowNonPowerOfTwo = false) { + if (array == null) return; + if (array.GetType() != typeof(T[])) { + throw new System.ArgumentException("Expected array type " + typeof(T[]).Name + " but found " + array.GetType().Name + "\nAre you using the correct generic class?\n"); + } + +#if !ASTAR_NO_POOLING + bool isPowerOfTwo = array.Length != 0 && (array.Length & (array.Length - 1)) == 0; + if (!isPowerOfTwo && !allowNonPowerOfTwo && array.Length != 0) throw new System.ArgumentException("Length is not a power of 2"); + + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING + if (!inPool.Add(array)) { + throw new InvalidOperationException("You are trying to pool an array twice. Please make sure that you only pool it once."); + } +#endif + if (isPowerOfTwo) { + int bucketIndex = 0; + while ((1 << bucketIndex) < array.Length && bucketIndex < 30) { + bucketIndex++; + } + + if (pool[bucketIndex] == null) { + pool[bucketIndex] = new Stack<T[]>(); + } + + pool[bucketIndex].Push(array); + } else if (array.Length <= MaximumExactArrayLength) { + Stack<T[]> stack = exactPool[array.Length]; + if (stack == null) stack = exactPool[array.Length] = new Stack<T[]>(); + stack.Push(array); + } + } +#endif + array = null; + } + } + + /// <summary>Extension methods for List<T></summary> + public static class ListExtensions { + /// <summary> + /// Identical to ToArray but it uses ArrayPool<T> to avoid allocations if possible. + /// + /// Use with caution as pooling too many arrays with different lengths that + /// are rarely being reused will lead to an effective memory leak. + /// </summary> + public static T[] ToArrayFromPool<T>(this List<T> list) { + var arr = ArrayPool<T>.ClaimWithExactLength(list.Count); + + for (int i = 0; i < arr.Length; i++) { + arr[i] = list[i]; + } + return arr; + } + + /// <summary> + /// Clear a list faster than List<T>.Clear. + /// It turns out that the List<T>.Clear method will clear all elements in the underlaying array + /// not just the ones up to Count. If the list only has a few elements, but the capacity + /// is huge, this can cause performance problems. Using the RemoveRange method to remove + /// all elements in the list does not have this problem, however it is implemented in a + /// stupid way, so it will clear the elements twice (completely unnecessarily) so it will + /// only be faster than using the Clear method if the number of elements in the list is + /// less than half of the capacity of the list. + /// + /// Hopefully this method can be removed when Unity upgrades to a newer version of Mono. + /// </summary> + public static void ClearFast<T>(this List<T> list) { + if (list.Count*2 < list.Capacity) { + list.RemoveRange(0, list.Count); + } else { + list.Clear(); + } + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ArrayPool.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ArrayPool.cs.meta new file mode 100644 index 0000000..25e0d39 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ArrayPool.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 787a564bf2d894ee09284b775074864c +timeCreated: 1470483941 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ListPool.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ListPool.cs new file mode 100644 index 0000000..22ff246 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ListPool.cs @@ -0,0 +1,211 @@ +#if !UNITY_EDITOR +// Extra optimizations when not running in the editor, but less error checking +#define ASTAR_OPTIMIZE_POOLING +#endif + +using System; +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// <summary> + /// Lightweight List Pool. + /// Handy class for pooling lists of type T. + /// + /// Usage: + /// - Claim a new list using <code> List<SomeClass> foo = ListPool<SomeClass>.Claim (); </code> + /// - Use it and do stuff with it + /// - Release it with <code> ListPool<SomeClass>.Release (foo); </code> + /// + /// You do not need to clear the list before releasing it. + /// After you have released a list, you should never use it again, if you do use it, you will + /// mess things up quite badly in the worst case. + /// + /// Since: Version 3.2 + /// See: Pathfinding.Util.StackPool + /// </summary> + public static class ListPool<T> { + /// <summary>Internal pool</summary> + static readonly List<List<T> > pool = new List<List<T> >(); + +#if !ASTAR_NO_POOLING + static readonly List<List<T> > largePool = new List<List<T> >(); + static readonly HashSet<List<T> > inPool = new HashSet<List<T> >(); +#endif + + /// <summary> + /// When requesting a list with a specified capacity, search max this many lists in the pool before giving up. + /// Must be greater or equal to one. + /// </summary> + const int MaxCapacitySearchLength = 8; + const int LargeThreshold = 5000; + const int MaxLargePoolSize = 8; + + /// <summary> + /// Claim a list. + /// Returns a pooled list if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this list should be released using the Release function (though not strictly necessary). + /// </summary> + public static List<T> Claim () { +#if ASTAR_NO_POOLING + return new List<T>(); +#else + lock (pool) { + if (pool.Count > 0) { + List<T> ls = pool[pool.Count-1]; + pool.RemoveAt(pool.Count-1); + inPool.Remove(ls); + return ls; + } + + return new List<T>(); + } +#endif + } + + static int FindCandidate (List<List<T> > pool, int capacity) { + // Loop through the last MaxCapacitySearchLength items + // and check if any item has a capacity greater or equal to the one that + // is desired. If so return it. + // Otherwise take the largest one or if there are no lists in the pool + // then allocate a new one with the desired capacity + List<T> list = null; + int listIndex = -1; + + for (int i = 0; i < pool.Count && i < MaxCapacitySearchLength; i++) { + // ith last item + var candidate = pool[pool.Count-1-i]; + + // Find the largest list that is not too large (arbitrary decision to try to prevent some memory bloat if the list was not just a temporary list). + if ((list == null || candidate.Capacity > list.Capacity) && candidate.Capacity < capacity*16) { + list = candidate; + listIndex = pool.Count-1-i; + + if (list.Capacity >= capacity) { + return listIndex; + } + } + } + + return listIndex; + } + + /// <summary> + /// Claim a list with minimum capacity + /// Returns a pooled list if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this list should be released using the Release function (though not strictly necessary). + /// A subset of the pool will be searched for a list with a high enough capacity and one will be returned + /// if possible, otherwise the list with the largest capacity found will be returned. + /// </summary> + public static List<T> Claim (int capacity) { +#if ASTAR_NO_POOLING + return new List<T>(capacity); +#else + lock (pool) { + var currentPool = pool; + var listIndex = FindCandidate(pool, capacity); + + if (capacity > LargeThreshold) { + var largeListIndex = FindCandidate(largePool, capacity); + if (largeListIndex != -1) { + currentPool = largePool; + listIndex = largeListIndex; + } + } + + if (listIndex == -1) { + return new List<T>(capacity); + } else { + var list = currentPool[listIndex]; + // Swap current item and last item to enable a more efficient removal + inPool.Remove(list); + currentPool[listIndex] = currentPool[currentPool.Count-1]; + currentPool.RemoveAt(currentPool.Count-1); + return list; + } + } +#endif + } + + /// <summary> + /// Makes sure the pool contains at least count pooled items with capacity size. + /// This is good if you want to do all allocations at start. + /// </summary> + public static void Warmup (int count, int size) { + lock (pool) { + var tmp = new List<T>[count]; + for (int i = 0; i < count; i++) tmp[i] = Claim(size); + for (int i = 0; i < count; i++) Release(tmp[i]); + } + } + + + /// <summary> + /// Releases a list and sets the variable to null. + /// After the list has been released it should not be used anymore. + /// + /// Throws: System.InvalidOperationException + /// Releasing a list when it has already been released will cause an exception to be thrown. + /// + /// See: <see cref="Claim"/> + /// </summary> + public static void Release (ref List<T> list) { + Release(list); + list = null; + } + + /// <summary> + /// Releases a list. + /// After the list has been released it should not be used anymore. + /// + /// Throws: System.InvalidOperationException + /// Releasing a list when it has already been released will cause an exception to be thrown. + /// + /// See: <see cref="Claim"/> + /// </summary> + public static void Release (List<T> list) { +#if !ASTAR_NO_POOLING + list.ClearFast(); + + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING + if (!inPool.Add(list)) { + throw new InvalidOperationException("You are trying to pool a list twice. Please make sure that you only pool it once."); + } +#endif + if (list.Capacity > LargeThreshold) { + largePool.Add(list); + + // Remove the list which was used the longest time ago from the pool if it + // exceeds the maximum size as it probably just contributes to memory bloat + if (largePool.Count > MaxLargePoolSize) { + largePool.RemoveAt(0); + } + } else { + pool.Add(list); + } + } +#endif + } + + /// <summary> + /// Clears the pool for lists of this type. + /// This is an O(n) operation, where n is the number of pooled lists. + /// </summary> + public static void Clear () { + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING && !ASTAR_NO_POOLING + inPool.Clear(); +#endif + pool.Clear(); + } + } + + /// <summary>Number of lists of this type in the pool</summary> + public static int GetSize () { + // No lock required since int writes are atomic + return pool.Count; + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ListPool.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ListPool.cs.meta new file mode 100644 index 0000000..92e0b9c --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ListPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2b76b7593907b44d9a6ef1b186fee0a7 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ObjectPool.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ObjectPool.cs new file mode 100644 index 0000000..262830c --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ObjectPool.cs @@ -0,0 +1,131 @@ +#if !UNITY_EDITOR +// Extra optimizations when not running in the editor, but less error checking +#define ASTAR_OPTIMIZE_POOLING +#endif + +using System; +using System.Collections.Generic; + +namespace Pathfinding.Util { + public interface IAstarPooledObject { + void OnEnterPool(); + } + + /// <summary> + /// Lightweight object Pool for IAstarPooledObject. + /// Handy class for pooling objects of type T which implements the IAstarPooledObject interface. + /// + /// Usage: + /// - Claim a new object using <code> SomeClass foo = ObjectPool<SomeClass>.Claim (); </code> + /// - Use it and do stuff with it + /// - Release it with <code> ObjectPool<SomeClass>.Release (foo); </code> + /// + /// After you have released a object, you should never use it again. + /// + /// Since: Version 3.2 + /// Version: Since 3.7.6 this class is thread safe + /// See: Pathfinding.Util.ListPool + /// See: ObjectPoolSimple + /// </summary> + public static class ObjectPool<T> where T : class, IAstarPooledObject, new(){ + public static T Claim () { + return ObjectPoolSimple<T>.Claim(); + } + + public static void Release (ref T obj) { + obj.OnEnterPool(); + ObjectPoolSimple<T>.Release(ref obj); + } + } + + /// <summary> + /// Lightweight object Pool. + /// Handy class for pooling objects of type T. + /// + /// Usage: + /// - Claim a new object using <code> SomeClass foo = ObjectPool<SomeClass>.Claim (); </code> + /// - Use it and do stuff with it + /// - Release it with <code> ObjectPool<SomeClass>.Release (foo); </code> + /// + /// After you have released a object, you should never use it again. + /// + /// Since: Version 3.2 + /// Version: Since 3.7.6 this class is thread safe + /// See: Pathfinding.Util.ListPool + /// See: ObjectPool + /// </summary> + public static class ObjectPoolSimple<T> where T : class, new(){ + /// <summary>Internal pool</summary> + static List<T> pool = new List<T>(); + +#if !ASTAR_NO_POOLING + static readonly HashSet<T> inPool = new HashSet<T>(); +#endif + + /// <summary> + /// Claim a object. + /// Returns a pooled object if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this object should be released using the Release function (though not strictly necessary). + /// </summary> + public static T Claim () { +#if ASTAR_NO_POOLING + return new T(); +#else + lock (pool) { + if (pool.Count > 0) { + T ls = pool[pool.Count-1]; + pool.RemoveAt(pool.Count-1); + inPool.Remove(ls); + return ls; + } else { + return new T(); + } + } +#endif + } + + /// <summary> + /// Releases an object. + /// After the object has been released it should not be used anymore. + /// The variable will be set to null to prevent silly mistakes. + /// + /// Throws: System.InvalidOperationException + /// Releasing an object when it has already been released will cause an exception to be thrown. + /// However enabling ASTAR_OPTIMIZE_POOLING will prevent this check. + /// + /// See: Claim + /// </summary> + public static void Release (ref T obj) { +#if !ASTAR_NO_POOLING + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING + if (!inPool.Add(obj)) { + throw new InvalidOperationException("You are trying to pool an object twice. Please make sure that you only pool it once."); + } +#endif + pool.Add(obj); + } +#endif + obj = null; + } + + /// <summary> + /// Clears the pool for objects of this type. + /// This is an O(n) operation, where n is the number of pooled objects. + /// </summary> + public static void Clear () { + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING && !ASTAR_NO_POOLING + inPool.Clear(); +#endif + pool.Clear(); + } + } + + /// <summary>Number of objects of this type in the pool</summary> + public static int GetSize () { + return pool.Count; + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ObjectPool.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ObjectPool.cs.meta new file mode 100644 index 0000000..6240b52 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/ObjectPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 10837c5b030bd47a2a0e6e213fea0868 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/PathPool.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/PathPool.cs new file mode 100644 index 0000000..9fa75d1 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/PathPool.cs @@ -0,0 +1,88 @@ +//#define ASTAR_NO_POOLING // Disable pooling for some reason. Maybe for debugging or just for measuring the difference. +using System; +using System.Collections.Generic; + +namespace Pathfinding { + /// <summary>Pools path objects to reduce load on the garbage collector</summary> + public static class PathPool { + static readonly Dictionary<Type, Stack<Path> > pool = new Dictionary<Type, Stack<Path> >(); + static readonly Dictionary<Type, int> totalCreated = new Dictionary<Type, int>(); + + /// <summary> + /// Adds a path to the pool. + /// This function should not be used directly. Instead use the Path.Claim and Path.Release functions. + /// </summary> + public static void Pool (Path path) { +#if !ASTAR_NO_POOLING + lock (pool) { + if (((IPathInternals)path).Pooled) { + throw new System.ArgumentException("The path is already pooled."); + } + + Stack<Path> poolStack; + if (!pool.TryGetValue(path.GetType(), out poolStack)) { + poolStack = new Stack<Path>(); + pool[path.GetType()] = poolStack; + } + + ((IPathInternals)path).Pooled = true; + ((IPathInternals)path).OnEnterPool(); + poolStack.Push(path); + } +#endif + } + + /// <summary>Total created instances of paths of the specified type</summary> + public static int GetTotalCreated (Type type) { + int created; + + if (totalCreated.TryGetValue(type, out created)) { + return created; + } else { + return 0; + } + } + + /// <summary>Number of pooled instances of a path of the specified type</summary> + public static int GetSize (Type type) { + Stack<Path> poolStack; + + if (pool.TryGetValue(type, out poolStack)) { + return poolStack.Count; + } else { + return 0; + } + } + + /// <summary>Get a path from the pool or create a new one if the pool is empty</summary> + public static T GetPath<T>() where T : Path, new() { +#if ASTAR_NO_POOLING + T result = new T(); + ((IPathInternals)result).Reset(); + return result; +#else + lock (pool) { + T result; + Stack<Path> poolStack; + if (pool.TryGetValue(typeof(T), out poolStack) && poolStack.Count > 0) { + // Guaranteed to have the correct type + result = poolStack.Pop() as T; + } else { + result = new T(); + + // Make sure an entry for the path type exists + if (!totalCreated.ContainsKey(typeof(T))) { + totalCreated[typeof(T)] = 0; + } + + totalCreated[typeof(T)]++; + } + + ((IPathInternals)result).Pooled = false; + ((IPathInternals)result).Reset(); + return result; + } +#endif + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/PathPool.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/PathPool.cs.meta new file mode 100644 index 0000000..9ff4458 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/PathPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cefe1014ab62848a89016fb97b1f8f7b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/StackPool.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/StackPool.cs new file mode 100644 index 0000000..daf7a53 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/StackPool.cs @@ -0,0 +1,98 @@ +//#define ASTAR_NO_POOLING //@SHOWINEDITOR Disable pooling for some reason. Could be debugging or just for measuring the difference. + +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// <summary> + /// Lightweight Stack Pool. + /// Handy class for pooling stacks of type T. + /// + /// Usage: + /// - Claim a new stack using <code> Stack<SomeClass> foo = StackPool<SomeClass>.Claim (); </code> + /// - Use it and do stuff with it + /// - Release it with <code> StackPool<SomeClass>.Release (foo); </code> + /// + /// You do not need to clear the stack before releasing it. + /// After you have released a stack, you should never use it again. + /// + /// Warning: This class is not thread safe + /// + /// Since: Version 3.2 + /// See: Pathfinding.Util.ListPool + /// </summary> + public static class StackPool<T> { + /// <summary>Internal pool</summary> + static readonly List<Stack<T> > pool; + + /// <summary>Static constructor</summary> + static StackPool () { + pool = new List<Stack<T> >(); + } + + /// <summary> + /// Claim a stack. + /// Returns a pooled stack if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this stack should be released using the Release function (though not strictly necessary). + /// </summary> + public static Stack<T> Claim () { +#if ASTAR_NO_POOLING + return new Stack<T>(); +#else + lock (pool) { + if (pool.Count > 0) { + Stack<T> ls = pool[pool.Count-1]; + pool.RemoveAt(pool.Count-1); + return ls; + } + } + + return new Stack<T>(); +#endif + } + + /// <summary> + /// Makes sure the pool contains at least count pooled items. + /// This is good if you want to do all allocations at start. + /// </summary> + public static void Warmup (int count) { + var tmp = new Stack<T>[count]; + + for (int i = 0; i < count; i++) tmp[i] = Claim(); + for (int i = 0; i < count; i++) Release(tmp[i]); + } + + /// <summary> + /// Releases a stack. + /// After the stack has been released it should not be used anymore. + /// Releasing a stack twice will cause an error. + /// </summary> + public static void Release (Stack<T> stack) { +#if !ASTAR_NO_POOLING + stack.Clear(); + + lock (pool) { + for (int i = 0; i < pool.Count; i++) + if (pool[i] == stack) UnityEngine.Debug.LogError("The Stack is released even though it is inside the pool"); + + pool.Add(stack); + } +#endif + } + + /// <summary> + /// Clears all pooled stacks of this type. + /// This is an O(n) operation, where n is the number of pooled stacks + /// </summary> + public static void Clear () { + lock (pool) { + pool.Clear(); + } + } + + /// <summary>Number of stacks of this type in the pool</summary> + public static int GetSize () { + return pool.Count; + } + } +} diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/StackPool.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/StackPool.cs.meta new file mode 100644 index 0000000..1c8a3b5 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pooling/StackPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: de467bbbb1ff84668ae8262caad00941 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} |