File: System\ServiceModel\Channels\QueuedObjectPool.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Collections.Generic;
using System.Diagnostics.Contracts;
 
namespace System.ServiceModel.Channels
{
    // This is the base object pool class which manages objects in a FIFO queue. The objects are 
    // created through the provided Func<T> createObjectFunc. The main purpose for this class is
    // to get better memory usage for Garbage Collection (GC) when part or all of an object is
    // regularly pinned. Constantly creating such objects can cause large Gen0 Heap fragmentation
    // and thus high memory usage pressure. The pooled objects are first created in Gen0 heaps and
    // would be eventually moved to a more stable segment which would prevent the fragmentation
    // to happen.
    //
    // The objects are created in batches for better localization of the objects. Here are the
    // parameters that control the behavior of creation/removal:
    // 
    // batchAllocCount: number of objects to be created at the same time when new objects are needed
    //
    // createObjectFunc: func delegate that is used to create objects by sub-classes.
    //
    // maxFreeCount: max number of free objects the queue can store. This is to make sure the memory
    //     usage is bounded.
    //
    internal abstract class QueuedObjectPool<T>
    {
        private Queue<T> _objectQueue;
        private bool _isClosed;
        private int _batchAllocCount;
        private int _maxFreeCount;
 
        protected void Initialize(int batchAllocCount, int maxFreeCount)
        {
            if (batchAllocCount <= 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(batchAllocCount)));
            }
 
            Contract.Assert(batchAllocCount <= maxFreeCount, "batchAllocCount cannot be greater than maxFreeCount");
            _batchAllocCount = batchAllocCount;
            _maxFreeCount = maxFreeCount;
            _objectQueue = new Queue<T>(batchAllocCount);
        }
 
        private object ThisLock
        {
            get
            {
                return _objectQueue;
            }
        }
 
        public virtual bool Return(T value)
        {
            lock (ThisLock)
            {
                if (_objectQueue.Count < _maxFreeCount && !_isClosed)
                {
                    _objectQueue.Enqueue(value);
                    return true;
                }
 
                return false;
            }
        }
 
        public T Take()
        {
            lock (ThisLock)
            {
                Contract.Assert(!_isClosed, "Cannot take an item from closed QueuedObjectPool");
 
                if (_objectQueue.Count == 0)
                {
                    AllocObjects();
                }
 
                return _objectQueue.Dequeue();
            }
        }
 
        public void Close()
        {
            lock (ThisLock)
            {
                foreach (T item in _objectQueue)
                {
                    if (item != null)
                    {
                        CleanupItem(item);
                    }
                }
 
                _objectQueue.Clear();
                _isClosed = true;
            }
        }
 
        protected virtual void CleanupItem(T item)
        {
        }
 
        protected abstract T Create();
 
        private void AllocObjects()
        {
            Contract.Assert(_objectQueue.Count == 0, "The object queue must be empty for new allocations");
            for (int i = 0; i < _batchAllocCount; i++)
            {
                _objectQueue.Enqueue(Create());
            }
        }
    }
}