< Summary

Information
Class: SwiftCollections.Pool.SwiftObjectPool<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Pool/SwiftObjectPool.cs
Line coverage
100%
Covered lines: 51
Uncovered lines: 0
Coverable lines: 51
Total lines: 202
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_CountActive()100%11100%
get_CountInactive()100%11100%
Rent()100%66100%
Rent(...)100%11100%
Release(...)100%66100%
Release(...)100%22100%
Clear()100%66100%
Dispose()100%22100%
Finalize()100%11100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Pool/SwiftObjectPool.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftObjectPool.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using SwiftCollections.Diagnostics;
 9using System;
 10using System.Collections.Concurrent;
 11using System.Collections.Generic;
 12using System.Runtime.CompilerServices;
 13
 14namespace SwiftCollections.Pool;
 15
 16/// <summary>
 17/// A generic object pooling class designed to efficiently reuse objects, reducing memory allocation overhead
 18/// and improving performance. Provides thread-safe operations for creating, renting, and releasing objects.
 19/// </summary>
 20/// <typeparam name="T">The type of object to pool. Must be a reference type.</typeparam>
 21public sealed class SwiftObjectPool<T> : IDisposable, ISwiftObjectPool<T> where T : class
 22{
 23    #region Fields
 24
 25    private readonly ConcurrentStack<T> _pool;
 26    private readonly Func<T> _createFunc;
 27    private readonly Action<T>? _actionOnGet;
 28    private readonly Action<T>? _actionOnRelease;
 29    private readonly Action<T>? _actionOnDestroy;
 30    private readonly int _maxSize;
 31
 32    private volatile bool _disposed;
 33
 34    #endregion
 35
 36    #region Constructor
 37
 38    /// <summary>
 39    /// Initializes a new instance of the <see cref="SwiftObjectPool{T}"/> class.
 40    /// </summary>
 41    /// <param name="createFunc">A function used to create new instances of the object type.</param>
 42    /// <param name="actionOnGet">An optional action to perform when an object is rented from the pool.</param>
 43    /// <param name="actionOnRelease">An optional action to perform when an object is returned to the pool.</param>
 44    /// <param name="actionOnDestroy">An optional action to perform when an object is destroyed due to pool size constra
 45    /// <param name="maxSize">The maximum number of objects the pool can hold.</param>
 46    /// <exception cref="ArgumentNullException">Thrown if <paramref name="createFunc"/> is null.</exception>
 47    /// <exception cref="ArgumentException">Thrown if <paramref name="maxSize"/> is less than or equal to 0.</exception>
 5148    public SwiftObjectPool(
 5149        Func<T> createFunc,
 5150        Action<T>? actionOnGet = null,
 5151        Action<T>? actionOnRelease = null,
 5152        Action<T>? actionOnDestroy = null,
 5153        int maxSize = 100)
 54    {
 5155        SwiftThrowHelper.ThrowIfNull(createFunc, nameof(createFunc));
 5156        SwiftThrowHelper.ThrowIfNegativeOrZero(maxSize, nameof(maxSize));
 57
 5158        _pool = new ConcurrentStack<T>();
 5159        _createFunc = createFunc;
 5160        _actionOnGet = actionOnGet;
 5161        _actionOnRelease = actionOnRelease;
 5162        _actionOnDestroy = actionOnDestroy;
 5163        _maxSize = maxSize;
 5164    }
 65
 66    #endregion
 67
 68    #region Properties
 69
 70    /// <summary>
 71    /// Gets the total number of objects created by the pool, including both active and inactive objects.
 72    /// </summary>
 73    public int CountAll { get; private set; }
 74
 75    /// <summary>
 76    /// Gets the number of objects currently in use (rented from the pool).
 77    /// </summary>
 478    public int CountActive => CountAll - CountInactive;
 79
 80    /// <summary>
 81    /// Gets the number of objects currently available in the pool for rent.
 82    /// </summary>
 883    public int CountInactive => _pool.Count;
 84
 85    #endregion
 86
 87    #region Collection Manipulation
 88
 89    /// <summary>
 90    /// Rents an object from the pool. If the pool is empty, a new object is created using the factory function.
 91    /// </summary>
 92    /// <returns>An instance of <typeparamref name="T"/>.</returns>
 93    /// <exception cref="InvalidOperationException">Thrown if object creation fails.</exception>
 94    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 95    public T Rent()
 96    {
 7597        SwiftThrowHelper.ThrowIfDisposed(_disposed, nameof(SwiftObjectPool<T>));
 98
 7499        if (_pool.TryPop(out var obj))
 100        {
 14101            _actionOnGet?.Invoke(obj);
 14102            return obj;
 103        }
 104
 60105        var newObj = _createFunc();
 60106        SwiftThrowHelper.ThrowIfTrue(newObj is null, message: "Failed to create a new object.");
 59107        CountAll++;
 59108        _actionOnGet?.Invoke(newObj);
 59109        return newObj;
 110    }
 111
 112    /// <summary>
 113    /// Rents an object from the pool and wraps it in a <see cref="SwiftPooledObject{T}"/> for automatic release.
 114    /// </summary>
 115    /// <param name="value">The rented object.</param>
 116    /// <returns>A <see cref="SwiftPooledObject{T}"/> instance wrapping the rented object.</returns>
 117    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3118    public SwiftPooledObject<T> Rent(out T value) => new(value = Rent(), this);
 119
 120    /// <summary>
 121    /// Releases an object back to the pool for reuse. If the pool has reached its maximum size, the object
 122    /// is destroyed using the configured destroy action.
 123    /// </summary>
 124    /// <param name="element">The object to release.</param>
 125    /// <exception cref="ArgumentNullException">Thrown if <paramref name="element"/> is null.</exception>
 126    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 127    public void Release(T element)
 128    {
 45129        SwiftThrowHelper.ThrowIfDisposed(_disposed, nameof(SwiftObjectPool<T>));
 45130        SwiftThrowHelper.ThrowIfNull(element, nameof(element));
 131
 44132        _actionOnRelease?.Invoke(element);
 133
 44134        if (_pool.Count < _maxSize)
 41135            _pool.Push(element);
 136        else
 137        {
 3138            _actionOnDestroy?.Invoke(element);
 3139            CountAll--;
 140        }
 3141    }
 142
 143    /// <summary>
 144    /// Releases all objects back to the pool for reuse.  If the pool has reached its maximum size, objects
 145    /// are destroyed using the configured destroy action.
 146    /// </summary>
 147    /// <param name="elements"></param>
 148    /// <exception cref="ArgumentNullException">Thrown if any object in <paramref name="elements"/> is null.</exception>
 149    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 150    public void Release(IEnumerable<T> elements)
 151    {
 1152        SwiftThrowHelper.ThrowIfDisposed(_disposed, nameof(SwiftObjectPool<T>));
 153
 6154        foreach (T item in elements)
 2155            Release(item);
 1156    }
 157
 158    /// <summary>
 159    /// Clears all objects from the pool, destroying any active objects if a destroy action is configured.
 160    /// </summary>
 161    public void Clear()
 162    {
 49163        if (_disposed) return;
 164
 40165        while (_pool.TryPop(out var obj))
 17166            _actionOnDestroy?.Invoke(obj);
 167
 23168        CountAll = 0;
 23169    }
 170
 171    #endregion
 172
 173    #region IDisposable Implementation
 174
 175    /// <summary>
 176    /// Releases all resources used by the SwiftObjectPool.
 177    /// It is important to call Dispose() to release pooled arrays, preventing potential memory leaks.
 178    /// </summary>
 179    public void Dispose()
 180    {
 100181        if (_disposed)
 49182            return;
 183
 51184        _pool.Clear();
 185
 51186        _disposed = true;
 187
 188        // Suppress finalization to prevent unnecessary GC overhead
 51189        GC.SuppressFinalize(this);
 51190    }
 191
 192    /// <summary>
 193    /// Releases the resources used by the SwiftObjectPool instance.
 194    /// </summary>
 195    /// <remarks>
 196    /// This finalizer ensures that unmanaged resources are released if Dispose was not called explicitly.
 197    /// It is recommended to call Dispose to release resources deterministically.
 198    /// </remarks>
 31199    ~SwiftObjectPool() => Dispose();
 200
 201    #endregion
 202}