Object pool pattern for asynchronous socket operations I have written my own object pool class called ObjectPool&lt;T&gt; which uses SpinLock and Stack&lt;T&gt;:

public class ObjectPool&lt;T&gt; where T : class
{
    private int _size;
    private Func&lt;T&gt; _factory;
    private SpinLock _spinLock;

    // storage for the pool objects.
    // the first item is expected to be most often case.
    private T _firstItem;
    private Stack&lt;T&gt; _cache; //Use Queue&lt;T&gt; for first-in-first-out (FIFO)

    public ObjectPool(Func&lt;T&gt; factory) : this(factory, Environment.ProcessorCount * 2) { }

    public ObjectPool(Func&lt;T&gt; factory, int size)
    {
        _factory = factory;
        _size = size;
        _cache = new Stack&lt;T&gt;();
        _spinLock = new SpinLock(false);
    }

    /// &lt;summary&gt;
    /// Produces an instance.
    /// &lt;/summary&gt;
    public T Rent()
    {
        bool lockTaken = false;
        _spinLock.Enter(ref lockTaken);

        try
        {
            T instance = _firstItem;
            if (instance == null || instance != Interlocked.CompareExchange(ref _firstItem, null, instance))
            {
                instance = RentFromCache();
            }

            return instance;
        }
        finally
        {
            if (lockTaken)
            {
                _spinLock.Exit(false);
            }
        }
    }

    private T RentFromCache()
    {
        var cache = _cache;

        if (cache.Count &gt; 0)
        {
            T instance = _cache.Pop();

            if (instance != null)
            {
                return instance;
            }
        }

        return CreateInstance();
    }

    private T CreateInstance()
    {
        var instance = _factory();
        return instance;
    }

    /// &lt;summary&gt;
    /// Returns objects to the pool.
    /// &lt;/summary&gt;
    public void Return(T obj)
    {
        bool lockTaken = false;
        _spinLock.Enter(ref lockTaken);

        try
        {
            if (_firstItem == null)
            {
                // worst case scenario: two objects may be stored into same slot.
                _firstItem = obj;
            }
            else
            {
                ReturnToCache(obj);
            }
        }
        finally
        {
            if (lockTaken)
            {
                _spinLock.Exit(false);
            }
        }
    }

    private void ReturnToCache(T obj)
    {
        var cache = _cache;

        if (cache.Count &gt;= _size)
        {
            // not a big fan of doing it this way
            return;
        }

        // worst case scenario: two objects may be stored into same slot.
        cache.Push(obj);
    }
}


I'm currently using this object pool to get high performance out of a Socket by making a pool of asynchronous socket operations (ObjectPool&lt;SocketAsyncEventArgs&gt;).

Note: I'm receiving UDP packets, which might help illustrate why some quirks exist (that won't work well for some generic-cases) in this object pool's design, such as using a LIFO pool.

It's working great so far. However, I'd appreciate some expert code review. I am hoping for advice on where it can be improved, further optimized, or if I have missed something important.



Additional Questions:


I'm wondering if ConcurrentStack&lt;T&gt; would provide me with any added benefits?
Because you don't change 

private int _size;
private Func&lt;T&gt; _factory;
private Stack&lt;T&gt; _cache;


private SpinLock _spinLock; //Use Queue&lt;T&gt; for first-in-first-out (FIFO)

you should make them readonly.  

Correction after the comment of t3chb0t:  

Don't make the SpinLock readonly. Further clarification: https://stackoverflow.com/a/9235028/2655508  


  The underlying problem is that the C# compiler creates a copy of a readonly value type field when you call a non-static method on it and executes that method on the copy - because the method could have side effects that change the value of the struct - which is not allowed for readonly fields. 





private T RentFromCache()
{
    var cache = _cache;

    if (cache.Count &gt; 0)
    {
        T instance = _cache.Pop();

        if (instance != null)
        {
            return instance;
        }
    }

    return CreateInstance();
}  



Here you are using var cache only for checking its Count property. I don't see a reason to not use the class level _cache directly. Using the class level variable directly won't confuse the reader of the code. The same applies to ReturnToCache() as well.