File: channels\core\bytebufferpool.cs
Project: ndp\clr\src\managedlibraries\remoting\System.Runtime.Remoting.csproj (System.Runtime.Remoting)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//==========================================================================
//  File:       ByteBufferPool.cs
//
//  Summary:    Stream used for reading from a socket by remoting channels.
//
//==========================================================================
 
using System;
using System.Threading;
 
 
namespace System.IO
{
 
    internal interface IByteBufferPool
    {
        byte[] GetBuffer();
        void ReturnBuffer(byte[] buffer);
    }
 
 
    // This isn't actually a buffer pool. It always creates a new byte buffer.
    internal class ByteBufferAllocator : IByteBufferPool
    {
        private int _bufferSize;
 
        public ByteBufferAllocator(int bufferSize)
        {
            _bufferSize = bufferSize;
        }
 
        public byte[] GetBuffer()
        {
            return new byte[_bufferSize];
        }
 
        public void ReturnBuffer(byte[] buffer)
        {
        }
        
    } // ByteBufferAllocator
 
 
    internal class ByteBufferPool : IByteBufferPool
    {
        private byte[][] _bufferPool = null;
    
        private int _current; // -1 for none
        private int _last;
        private int _max;     // maximum number of buffers to pool
 
        private int _bufferSize;
 
        private Object _controlCookie = "cookie object";
 
 
        public ByteBufferPool(int maxBuffers, int bufferSize)
        {
            _max = maxBuffers;        
            _bufferPool = new byte[_max][];
            _bufferSize = bufferSize;
 
            _current = -1;
            _last = -1;
        } // ByteBufferPool        
        
 
        public byte[] GetBuffer()
        {
            Object cookie = null;
 
            try
            {
                // If a ThreadAbortException gets thrown after the exchange,
                //   but before the result is assigned to cookie, then the
                //   control cookie is lost forever. However, the buffer pool
                //   will still function normally and return everybody a new
                //   buffer each time (that isn't very likely to happen,
                //   so we don't really care).
                cookie = Interlocked.Exchange(ref _controlCookie, null);
 
                if (cookie != null)
                {
                    // we have the control cookie, so take a buffer
                
                    if (_current == -1)
                    {
                        _controlCookie = cookie;
                        // no pooled buffers available
                        return new byte[_bufferSize];
                    }
                    else
                    {
                        // grab next available buffer
                        byte[] buffer = _bufferPool[_current];
                        _bufferPool[_current] = null;      
 
                        // update "current" index
                        if (_current == _last)
                        {
                            // this is the last free buffer
                            _current = -1;
                        }
                        else
                        {
                            _current = (_current + 1) % _max;
                        }
    
                        _controlCookie = cookie;
                        return buffer;
                    }              
                }
                else
                {
                    // we don't have the control cookie, so just create a new buffer since
                    //   there will probably be a lot of contention anyway.
                    return new byte[_bufferSize];
                }            
            } 
            catch (ThreadAbortException)
            {
                if (cookie != null)
                {
                    // This should be rare, so just reset
                    //   everything to the initial state.
                    _current = -1;
                    _last = -1;
 
                    // restore cookie
                    _controlCookie = cookie;
                }
            
                throw;
            }                            
        } // GetBuffer
 
 
        public void ReturnBuffer(byte[] buffer)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
                
        
            // The purpose of the buffer pool is to try to reduce the 
            //   amount of garbage generated, so it doesn't matter  if
            //   the buffer gets tossed out. Since we don't want to
            //   take the perf hit of taking a lock, we only return
            //   the buffer if we can grab the control cookie.
            
            Object cookie = null;
 
            try
            {
                // If a ThreadAbortException gets thrown after the exchange,
                //   but before the result is assigned to cookie, then the
                //   control cookie is lost forever. However, the buffer pool
                //   will still function normally and return everybody a new
                //   buffer each time (that isn't very likely to happen,
                //   so we don't really care).
                cookie = Interlocked.Exchange(ref _controlCookie, null);
                
                if (cookie != null)
                {
                    if (_current == -1)
                    {
                        _bufferPool[0] = buffer;
                        _current = 0;
                        _last = 0;
                    }
                    else
                    {
                        int newLast = (_last + 1) % _max;
                        if (newLast != _current)
                        {
                            // the pool isn't full so store this buffer
                            _last = newLast;
                            _bufferPool[_last] = buffer;
                        }
                    }
 
                    _controlCookie = cookie;
                }            
            }
            catch (ThreadAbortException)
            {
                if (cookie != null)
                {
                    // This should be rare, so just reset
                    //   everything to the initial state.
                    _current = -1;
                    _last = -1;
 
                    // restore cookie
                    _controlCookie = cookie;
                }
 
                throw;            
            }
        } // ReturnBuffer
 
        
 
    } // ByteBufferPool
 
 
} // namespace System.IO