File: System\ServiceModel\Channels\ConnectionPool.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Net;
    using System.Runtime;
    using System.Runtime.Diagnostics;
    using System.ServiceModel;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Diagnostics.Application;
 
    // code that pools items and closes/aborts them as necessary.
    // shared by IConnection and IChannel users
    abstract class CommunicationPool<TKey, TItem>
        where TKey : class
        where TItem : class
    {
        Dictionary<TKey, EndpointConnectionPool> endpointPools;
        int maxCount;
        int openCount;
        // need to make sure we prune over a certain number of endpoint pools
        int pruneAccrual;
        const int pruneThreshold = 30;
 
        protected CommunicationPool(int maxCount)
        {
            this.maxCount = maxCount;
            this.endpointPools = new Dictionary<TKey, EndpointConnectionPool>();
            this.openCount = 1;
        }
 
        public int MaxIdleConnectionPoolCount
        {
            get { return this.maxCount; }
        }
 
        protected object ThisLock
        {
            get { return this; }
        }
 
        protected abstract void AbortItem(TItem item);
 
        [Fx.Tag.Throws(typeof(CommunicationException), "A communication exception occurred closing this item")]
        [Fx.Tag.Throws(typeof(TimeoutException), "Timed out trying to close this item")]
        protected abstract void CloseItem(TItem item, TimeSpan timeout);
        protected abstract void CloseItemAsync(TItem item, TimeSpan timeout);
 
        protected abstract TKey GetPoolKey(EndpointAddress address, Uri via);
 
        protected virtual EndpointConnectionPool CreateEndpointConnectionPool(TKey key)
        {
            return new EndpointConnectionPool(this, key);
        }
 
        public bool Close(TimeSpan timeout)
        {
            lock (ThisLock)
            {
                if (openCount <= 0)
                {
                    return true;
                }
 
                openCount--;
 
                if (openCount == 0)
                {
                    this.OnClose(timeout);
                    return true;
                }
 
                return false;
            }
        }
 
        List<TItem> PruneIfNecessary()
        {
            List<TItem> itemsToClose = null;
            pruneAccrual++;
            if (pruneAccrual > pruneThreshold)
            {
                pruneAccrual = 0;
                itemsToClose = new List<TItem>();
 
                // first prune the connection pool contents
                foreach (EndpointConnectionPool pool in endpointPools.Values)
                {
                    pool.Prune(itemsToClose);
                }
 
                // figure out which connection pools are now empty
                List<TKey> endpointKeysToRemove = null;
                foreach (KeyValuePair<TKey, EndpointConnectionPool> poolEntry in endpointPools)
                {
                    if (poolEntry.Value.CloseIfEmpty())
                    {
                        if (endpointKeysToRemove == null)
                        {
                            endpointKeysToRemove = new List<TKey>();
                        }
                        endpointKeysToRemove.Add(poolEntry.Key);
                    }
                }
 
                // and then prune the connection pools themselves
                if (endpointKeysToRemove != null)
                {
                    for (int i = 0; i < endpointKeysToRemove.Count; i++)
                    {
                        endpointPools.Remove(endpointKeysToRemove[i]);
                    }
                }
            }
 
            return itemsToClose;
        }
 
        EndpointConnectionPool GetEndpointPool(TKey key, TimeSpan timeout)
        {
            EndpointConnectionPool result = null;
            List<TItem> itemsToClose = null;
            lock (ThisLock)
            {
                if (!endpointPools.TryGetValue(key, out result))
                {
                    itemsToClose = PruneIfNecessary();
                    result = CreateEndpointConnectionPool(key);
                    endpointPools.Add(key, result);
                }
            }
 
            Fx.Assert(result != null, "EndpointPool must be non-null at this point");
            if (itemsToClose != null && itemsToClose.Count > 0)
            {
                // allocate half the remaining timeout for our g----ful shutdowns
                TimeoutHelper timeoutHelper = new TimeoutHelper(TimeoutHelper.Divide(timeout, 2));
                for (int i = 0; i < itemsToClose.Count; i++)
                {
                    result.CloseIdleConnection(itemsToClose[i], timeoutHelper.RemainingTime());
                }
            }
 
            return result;
        }
 
        public bool TryOpen()
        {
            lock (ThisLock)
            {
                if (openCount <= 0)
                {
                    // can't reopen connection pools since the registry purges them on close
                    return false;
                }
                else
                {
                    openCount++;
                    return true;
                }
            }
        }
 
        protected virtual void OnClosed()
        {
        }
 
        void OnClose(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            foreach (EndpointConnectionPool pool in endpointPools.Values)
            {
                try
                {
                    pool.Close(timeoutHelper.RemainingTime());
                }
                catch (CommunicationException exception)
                {
                    if (DiagnosticUtility.ShouldTraceError)
                    {
                        TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.ConnectionPoolCloseException, SR.GetString(SR.TraceCodeConnectionPoolCloseException), this, exception);
                    }
                }
                catch (TimeoutException exception)
                {
                    if (TD.CloseTimeoutIsEnabled())
                    {
                        TD.CloseTimeout(exception.Message);
                    }
                    if (DiagnosticUtility.ShouldTraceError)
                    {
                        TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.ConnectionPoolCloseException, SR.GetString(SR.TraceCodeConnectionPoolCloseException), this, exception);
                    }
                }
            }
 
            endpointPools.Clear();
        }
 
        public void AddConnection(TKey key, TItem connection, TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            EndpointConnectionPool endpointPool = GetEndpointPool(key, timeoutHelper.RemainingTime());
            endpointPool.AddConnection(connection, timeoutHelper.RemainingTime());
        }
 
        public TItem TakeConnection(EndpointAddress address, Uri via, TimeSpan timeout, out TKey key)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            key = this.GetPoolKey(address, via);
            EndpointConnectionPool endpointPool = GetEndpointPool(key, timeoutHelper.RemainingTime());
            return endpointPool.TakeConnection(timeoutHelper.RemainingTime());
        }
 
        public void ReturnConnection(TKey key, TItem connection, bool connectionIsStillGood, TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            EndpointConnectionPool endpointPool = GetEndpointPool(key, timeoutHelper.RemainingTime());
            endpointPool.ReturnConnection(connection, connectionIsStillGood, timeoutHelper.RemainingTime());
        }
 
        // base class for our collection of Idle connections
        protected abstract class IdleConnectionPool
        {
            public abstract int Count { get; }
            public abstract bool Add(TItem item);
            public abstract bool Return(TItem item);
            public abstract TItem Take(out bool closeItem);
        }
 
        protected class EndpointConnectionPool
        {
            TKey key;
            List<TItem> busyConnections;
            bool closed;
            IdleConnectionPool idleConnections;
            CommunicationPool<TKey, TItem> parent;
 
            public EndpointConnectionPool(CommunicationPool<TKey, TItem> parent, TKey key)
            {
                this.key = key;
                this.parent = parent;
                this.busyConnections = new List<TItem>();
            }
 
            protected TKey Key
            {
                get { return this.key; }
            }
 
            IdleConnectionPool IdleConnections
            {
                get
                {
                    if (idleConnections == null)
                    {
                        idleConnections = GetIdleConnectionPool();
                    }
 
                    return idleConnections;
                }
            }
 
            protected CommunicationPool<TKey, TItem> Parent
            {
                get { return this.parent; }
            }
 
            protected object ThisLock
            {
                get { return this; }
            }
 
            // close down the pool if empty
            public bool CloseIfEmpty()
            {
                lock (ThisLock)
                {
                    if (!closed)
                    {
                        if (busyConnections.Count > 0)
                        {
                            return false;
                        }
 
                        if (idleConnections != null && idleConnections.Count > 0)
                        {
                            return false;
                        }
                        closed = true;
                    }
                }
 
                return true;
            }
 
            protected virtual void AbortItem(TItem item)
            {
                parent.AbortItem(item);
            }
 
            [Fx.Tag.Throws(typeof(CommunicationException), "A communication exception occurred closing this item")]
            [Fx.Tag.Throws(typeof(TimeoutException), "Timed out trying to close this item")]
            protected virtual void CloseItem(TItem item, TimeSpan timeout)
            {
                parent.CloseItem(item, timeout);
            }
 
            protected virtual void CloseItemAsync(TItem item, TimeSpan timeout)
            {
                parent.CloseItemAsync(item, timeout);
            }
 
            public void Abort()
            {
                if (closed)
                {
                    return;
                }
 
                List<TItem> idleItemsToClose = null;
                lock (ThisLock)
                {
                    if (closed)
                        return;
 
                    closed = true;
                    idleItemsToClose = SnapshotIdleConnections();
                }
 
                AbortConnections(idleItemsToClose);
            }
 
            [Fx.Tag.Throws(typeof(CommunicationException), "A communication exception occurred closing this item")]
            [Fx.Tag.Throws(typeof(TimeoutException), "Timed out trying to close this item")]
            public void Close(TimeSpan timeout)
            {
                List<TItem> itemsToClose = null;
                lock (ThisLock)
                {
                    if (closed)
                        return;
 
                    closed = true;
                    itemsToClose = SnapshotIdleConnections();
                }
 
                try
                {
                    TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                    for (int i = 0; i < itemsToClose.Count; i++)
                    {
                        this.CloseItem(itemsToClose[i], timeoutHelper.RemainingTime());
                    }
 
                    itemsToClose.Clear();
                }
                finally
                {
                    AbortConnections(itemsToClose);
                }
            }
 
            void AbortConnections(List<TItem> idleItemsToClose)
            {
                for (int i = 0; i < idleItemsToClose.Count; i++)
                {
                    this.AbortItem(idleItemsToClose[i]);
                }
 
                for (int i = 0; i < busyConnections.Count; i++)
                {
                    this.AbortItem(busyConnections[i]);
                }
                busyConnections.Clear();
            }
 
            // must call under lock (ThisLock) since we are calling IdleConnections.Take()
            List<TItem> SnapshotIdleConnections()
            {
                List<TItem> itemsToClose = new List<TItem>();
                bool dummy;
                for (;;)
                {
                    TItem item = IdleConnections.Take(out dummy);
                    if (item == null)
                        break;
 
                    itemsToClose.Add(item);
                }
 
                return itemsToClose;
            }
 
            public void AddConnection(TItem connection, TimeSpan timeout)
            {
                bool closeConnection = false;
                lock (ThisLock)
                {
                    if (!closed)
                    {
                        if (!IdleConnections.Add(connection))
                        {
                            closeConnection = true;
                        }
                    }
                    else
                    {
                        closeConnection = true;
                    }
                }
 
                if (closeConnection)
                {
                    CloseIdleConnection(connection, timeout);
                }
            }
 
            protected virtual IdleConnectionPool GetIdleConnectionPool()
            {
                return new PoolIdleConnectionPool(parent.MaxIdleConnectionPoolCount);
            }
 
            public virtual void Prune(List<TItem> itemsToClose)
            {
            }
 
            public TItem TakeConnection(TimeSpan timeout)
            {
                TItem item = null;
                List<TItem> itemsToClose = null;
                lock (ThisLock)
                {
                    if (closed)
                        return null;
 
                    bool closeItem;
                    while (true)
                    {
                        item = IdleConnections.Take(out closeItem);
                        if (item == null)
                        {
                            break;
                        }
 
                        if (!closeItem)
                        {
                            busyConnections.Add(item);
                            break;
                        }
 
                        if (itemsToClose == null)
                        {
                            itemsToClose = new List<TItem>();
                        }
                        itemsToClose.Add(item);
                    }
                }
 
                // cleanup any stale items accrued from IdleConnections
                if (itemsToClose != null)
                {
                    // and only allocate half the timeout passed in for our g----ful shutdowns
                    TimeoutHelper timeoutHelper = new TimeoutHelper(TimeoutHelper.Divide(timeout, 2));
                    for (int i = 0; i < itemsToClose.Count; i++)
                    {
                        CloseIdleConnection(itemsToClose[i], timeoutHelper.RemainingTime());
                    }
                }
 
                if (TD.ConnectionPoolMissIsEnabled())
                {
                    if (item == null && busyConnections != null)
                    {
                        TD.ConnectionPoolMiss(key != null ? key.ToString() : string.Empty, busyConnections.Count);
                    }
                }
 
                return item;
            }
 
            public void ReturnConnection(TItem connection, bool connectionIsStillGood, TimeSpan timeout)
            {
                bool closeConnection = false;
                bool abortConnection = false;
 
                lock (ThisLock)
                {
                    if (!closed)
                    {
                        if (busyConnections.Remove(connection) && connectionIsStillGood)
                        {
                            if (!IdleConnections.Return(connection))
                            {
                                closeConnection = true;
                            }
                        }
                        else
                        {
                            abortConnection = true;
                        }
                    }
                    else
                    {
                        abortConnection = true;
                    }
                }
 
                if (closeConnection)
                {
                    CloseIdleConnection(connection, timeout);
                }
                else if (abortConnection)
                {
                    this.AbortItem(connection);
                    OnConnectionAborted();
                }
            }
 
            public void CloseIdleConnection(TItem connection, TimeSpan timeout)
            {
                bool throwing = true;
                try
                {
                    this.CloseItemAsync(connection, timeout);
                    throwing = false;
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                finally
                {
                    if (throwing)
                    {
                        this.AbortItem(connection);
                    }
                }
            }
 
            protected virtual void OnConnectionAborted()
            {
            }
 
            protected class PoolIdleConnectionPool
                : IdleConnectionPool
            {
                Pool<TItem> idleConnections;
                int maxCount;
 
                public PoolIdleConnectionPool(int maxCount)
                {
                    this.idleConnections = new Pool<TItem>(maxCount);
                    this.maxCount = maxCount;
                }
 
                public override int Count
                {
                    get { return idleConnections.Count; }
                }
 
                public override bool Add(TItem connection)
                {
                    return ReturnToPool(connection);
                }
 
                public override bool Return(TItem connection)
                {
                    return ReturnToPool(connection);
                }
 
                bool ReturnToPool(TItem connection)
                {
                    bool result = this.idleConnections.Return(connection);
                    if (!result)
                    {
                        if (TD.MaxOutboundConnectionsPerEndpointExceededIsEnabled())
                        {
                            TD.MaxOutboundConnectionsPerEndpointExceeded(SR.GetString(SR.TraceCodeConnectionPoolMaxOutboundConnectionsPerEndpointQuotaReached, maxCount));
                        }
                        if (DiagnosticUtility.ShouldTraceInformation)
                        {
                            TraceUtility.TraceEvent(TraceEventType.Information,
                                TraceCode.ConnectionPoolMaxOutboundConnectionsPerEndpointQuotaReached,
                                SR.GetString(SR.TraceCodeConnectionPoolMaxOutboundConnectionsPerEndpointQuotaReached, maxCount),
                                this);
                        }
                    }
                    else if (TD.OutboundConnectionsPerEndpointRatioIsEnabled())
                    {
                        TD.OutboundConnectionsPerEndpointRatio(this.idleConnections.Count, maxCount);
                    }
 
                    return result;
                }
 
                public override TItem Take(out bool closeItem)
                {
                    closeItem = false;
                    TItem ret = this.idleConnections.Take();
                    if (TD.OutboundConnectionsPerEndpointRatioIsEnabled())
                    {
                        TD.OutboundConnectionsPerEndpointRatio(this.idleConnections.Count, maxCount);
                    }
                    return ret;
                }
            }
        }
    }
 
    // all our connection pools support Idling out of connections and lease timeout
    // (though Named Pipes doesn't leverage the lease timeout)
    abstract class ConnectionPool : IdlingCommunicationPool<string, IConnection>
    {
        int connectionBufferSize;
        TimeSpan maxOutputDelay;
        string name;
 
        protected ConnectionPool(IConnectionOrientedTransportChannelFactorySettings settings, TimeSpan leaseTimeout)
            : base(settings.MaxOutboundConnectionsPerEndpoint, settings.IdleTimeout, leaseTimeout)
        {
            this.connectionBufferSize = settings.ConnectionBufferSize;
            this.maxOutputDelay = settings.MaxOutputDelay;
            this.name = settings.ConnectionPoolGroupName;
        }
 
        public string Name
        {
            get { return this.name; }
        }
 
        protected override void AbortItem(IConnection item)
        {
            item.Abort();
        }
 
        protected override void CloseItem(IConnection item, TimeSpan timeout)
        {
            item.Close(timeout, false);
        }
 
        protected override void CloseItemAsync(IConnection item, TimeSpan timeout)
        {
            item.Close(timeout, true);
        }
 
        public virtual bool IsCompatible(IConnectionOrientedTransportChannelFactorySettings settings)
        {
            return (
                (this.name == settings.ConnectionPoolGroupName) &&
                (this.connectionBufferSize == settings.ConnectionBufferSize) &&
                (this.MaxIdleConnectionPoolCount == settings.MaxOutboundConnectionsPerEndpoint) &&
                (this.IdleTimeout == settings.IdleTimeout) &&
                (this.maxOutputDelay == settings.MaxOutputDelay)
                );
        }
    }
 
    // Helper class used to manage the lifetime of a connection relative to its pool.
    abstract class ConnectionPoolHelper
    {
        IConnectionInitiator connectionInitiator;
        ConnectionPool connectionPool;
        Uri via;
        bool closed;
 
        // key for rawConnection in the connection pool
        string connectionKey;
 
        // did rawConnection originally come from connectionPool?
        bool isConnectionFromPool;
 
        // the "raw" connection that should be stored in the pool
        IConnection rawConnection;
 
        // the "upgraded" connection built on top of the "raw" connection to be used for I/O
        IConnection upgradedConnection;
 
        EventTraceActivity eventTraceActivity;
 
        public ConnectionPoolHelper(ConnectionPool connectionPool, IConnectionInitiator connectionInitiator, Uri via)
        {
            this.connectionInitiator = connectionInitiator;
            this.connectionPool = connectionPool;
            this.via = via;
        }
 
        object ThisLock
        {
            get { return this; }
        }
 
        protected EventTraceActivity EventTraceActivity
        {
            get
            {
                if (this.eventTraceActivity == null)
                {
                    this.eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate();
                }
                return this.eventTraceActivity;
            }
        }
 
        protected abstract IConnection AcceptPooledConnection(IConnection connection, ref TimeoutHelper timeoutHelper);
        protected abstract IAsyncResult BeginAcceptPooledConnection(IConnection connection, ref TimeoutHelper timeoutHelper,
            AsyncCallback callback, object state);
        protected abstract IConnection EndAcceptPooledConnection(IAsyncResult result);
 
        protected abstract TimeoutException CreateNewConnectionTimeoutException(TimeSpan timeout, TimeoutException innerException);
 
        public IAsyncResult BeginEstablishConnection(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new EstablishConnectionAsyncResult(this, timeout, callback, state);
        }
 
        public IConnection EndEstablishConnection(IAsyncResult result)
        {
            return EstablishConnectionAsyncResult.End(result);
        }
 
        IConnection TakeConnection(TimeSpan timeout)
        {
            return this.connectionPool.TakeConnection(null, this.via, timeout, out this.connectionKey);
        }
 
        public IConnection EstablishConnection(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            IConnection localRawConnection = null;
            IConnection localUpgradedConnection = null;
            bool localIsConnectionFromPool = true;
 
            EventTraceActivity localEventTraceActivity = this.EventTraceActivity;
            if (TD.EstablishConnectionStartIsEnabled())
            {
                TD.EstablishConnectionStart(localEventTraceActivity,
                                            this.via != null ? this.via.AbsoluteUri : string.Empty);
            }
 
            // first try and use a connection from our pool (and use it if we successfully receive an ACK)
            while (localIsConnectionFromPool)
            {
                localRawConnection = this.TakeConnection(timeoutHelper.RemainingTime());
                if (localRawConnection == null)
                {
                    localIsConnectionFromPool = false;
                }
                else
                {
                    bool preambleSuccess = false;
                    try
                    {
                        localUpgradedConnection = AcceptPooledConnection(localRawConnection, ref timeoutHelper);
                        preambleSuccess = true;
                        break;
                    }
                    catch (CommunicationException e)
                    {
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        // CommmunicationException is ok since it was a cached connection of unknown state
                    }
                    catch (TimeoutException e)
                    {
                        if (TD.OpenTimeoutIsEnabled())
                        {
                            TD.OpenTimeout(e.Message);
                        }
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        // ditto for TimeoutException
                    }
                    finally
                    {
                        if (!preambleSuccess)
                        {
                            if (TD.ConnectionPoolPreambleFailedIsEnabled())
                            {
                                TD.ConnectionPoolPreambleFailed(localEventTraceActivity);
                            }
 
                            if (DiagnosticUtility.ShouldTraceInformation)
                            {
                                TraceUtility.TraceEvent(
                                    TraceEventType.Information,
                                    TraceCode.FailedAcceptFromPool,
                                    SR.GetString(
                                        SR.TraceCodeFailedAcceptFromPool,
                                        timeoutHelper.RemainingTime()));
                            }
 
                            // This cannot throw TimeoutException since isConnectionStillGood is false (doesn't attempt a Close).
                            this.connectionPool.ReturnConnection(connectionKey, localRawConnection, false, TimeSpan.Zero);
                        }
                    }
                }
            }
 
            // if there isn't anything in the pool, we need to use a new connection
            if (!localIsConnectionFromPool)
            {
                bool success = false;
                TimeSpan connectTimeout = timeoutHelper.RemainingTime();
                try
                {
                    try
                    {
                        localRawConnection = this.connectionInitiator.Connect(this.via, connectTimeout);
                    }
                    catch (TimeoutException e)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateNewConnectionTimeoutException(
                            connectTimeout, e));
                    }
 
                    this.connectionInitiator = null;
                    localUpgradedConnection = AcceptPooledConnection(localRawConnection, ref timeoutHelper);
                    success = true;
                }
                finally
                {
                    if (!success)
                    {
                        connectionKey = null;
                        if (localRawConnection != null)
                        {
                            localRawConnection.Abort();
                        }
                    }
                }
            }
 
            SnapshotConnection(localUpgradedConnection, localRawConnection, localIsConnectionFromPool);
 
            if (TD.EstablishConnectionStopIsEnabled())
            {
                TD.EstablishConnectionStop(localEventTraceActivity);
            }
 
            return localUpgradedConnection;
        }
 
        void SnapshotConnection(IConnection upgradedConnection, IConnection rawConnection, bool isConnectionFromPool)
        {
            lock (ThisLock)
            {
                if (closed)
                {
                    upgradedConnection.Abort();
 
                    // cleanup our pool if necessary
                    if (isConnectionFromPool)
                    {
                        this.connectionPool.ReturnConnection(this.connectionKey, rawConnection, false, TimeSpan.Zero);
                    }
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        new CommunicationObjectAbortedException(
                        SR.GetString(SR.OperationAbortedDuringConnectionEstablishment, this.via)));
                }
                else
                {
                    this.upgradedConnection = upgradedConnection;
                    this.rawConnection = rawConnection;
                    this.isConnectionFromPool = isConnectionFromPool;
                }
            }
        }
 
        public void Abort()
        {
            ReleaseConnection(true, TimeSpan.Zero);
        }
 
        public void Close(TimeSpan timeout)
        {
            ReleaseConnection(false, timeout);
        }
 
        void ReleaseConnection(bool abort, TimeSpan timeout)
        {
            string localConnectionKey;
            IConnection localUpgradedConnection;
            IConnection localRawConnection;
 
            lock (ThisLock)
            {
                this.closed = true;
                localConnectionKey = this.connectionKey;
                localUpgradedConnection = this.upgradedConnection;
                localRawConnection = this.rawConnection;
 
                this.upgradedConnection = null;
                this.rawConnection = null;
            }
 
            if (localUpgradedConnection == null)
            {
                return;
            }
 
            try
            {
                if (this.isConnectionFromPool)
                {
                    this.connectionPool.ReturnConnection(localConnectionKey, localRawConnection, !abort, timeout);
                }
                else
                {
                    if (abort)
                    {
                        localUpgradedConnection.Abort();
                    }
                    else
                    {
                        this.connectionPool.AddConnection(localConnectionKey, localRawConnection, timeout);
                    }
                }
            }
            catch (CommunicationException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                localUpgradedConnection.Abort();
            }
        }
 
        class EstablishConnectionAsyncResult : AsyncResult
        {
            ConnectionPoolHelper parent;
            TimeoutHelper timeoutHelper;
            IConnection currentConnection;
            IConnection rawConnection;
            bool newConnection;
            bool cleanupConnection;
            TimeSpan connectTimeout;
            static AsyncCallback onConnect;
            static AsyncCallback onProcessConnection = Fx.ThunkCallback(new AsyncCallback(OnProcessConnection));
            EventTraceActivity eventTraceActivity;
 
            public EstablishConnectionAsyncResult(ConnectionPoolHelper parent,
                TimeSpan timeout, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.parent = parent;
                this.timeoutHelper = new TimeoutHelper(timeout);
 
                bool success = false;
                bool completeSelf = false;
                try
                {
                    completeSelf = Begin();
                    success = true;
                }
                finally
                {
                    if (!success)
                    {
                        Cleanup();
                    }
                }
 
                if (completeSelf)
                {
                    Cleanup();
                    base.Complete(true);
                }
            }
 
            EventTraceActivity EventTraceActivity
            {
                get
                {
                    if (this.eventTraceActivity == null)
                    {
                        this.eventTraceActivity = new EventTraceActivity();
                    }
                    return this.eventTraceActivity;
                }
            }
 
            public static IConnection End(IAsyncResult result)
            {
                EstablishConnectionAsyncResult thisPtr = AsyncResult.End<EstablishConnectionAsyncResult>(result);
 
                if (TD.EstablishConnectionStopIsEnabled())
                {
                    TD.EstablishConnectionStop(thisPtr.EventTraceActivity);
                }
 
                return thisPtr.currentConnection;
            }
 
            bool Begin()
            {
                if (TD.EstablishConnectionStartIsEnabled())
                {
                    TD.EstablishConnectionStart(this.EventTraceActivity, this.parent.connectionKey);
                }
 
                IConnection connection = parent.TakeConnection(timeoutHelper.RemainingTime());
 
                TrackConnection(connection);
 
                // first try and use a connection from our pool
                bool openingFromPool;
                if (OpenUsingConnectionPool(out openingFromPool))
                {
                    return true;
                }
 
                if (openingFromPool)
                {
                    return false;
                }
                else
                {
                    // if there isn't anything in the pool, we need to use a new connection
                    return OpenUsingNewConnection();
                }
            }
 
            bool OpenUsingConnectionPool(out bool openingFromPool)
            {
                openingFromPool = true;
                while (this.currentConnection != null)
                {
                    bool snapshotCollection = false;
                    try
                    {
                        if (ProcessConnection())
                        {
                            snapshotCollection = true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    catch (CommunicationException e)
                    {
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        // CommunicationException is allowed for cached channels, as the connection
                        // could be stale
                        Cleanup(); // remove residual state
                    }
                    catch (TimeoutException e)
                    {
                        if (TD.OpenTimeoutIsEnabled())
                        {
                            TD.OpenTimeout(e.Message);
                        }
                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                        // ditto for TimeoutException
                        Cleanup(); // remove residual state
                    }
 
                    if (snapshotCollection) // connection succeeded. Snapshot and return
                    {
                        SnapshotConnection();
                        return true;
                    }
 
                    // previous connection failed, try again
                    IConnection connection = parent.TakeConnection(timeoutHelper.RemainingTime());
 
                    TrackConnection(connection);
                }
 
                openingFromPool = false;
                return false;
            }
 
            bool OpenUsingNewConnection()
            {
                this.newConnection = true;
                IAsyncResult result;
 
                try
                {
                    this.connectTimeout = timeoutHelper.RemainingTime();
 
                    if (onConnect == null)
                    {
                        onConnect = Fx.ThunkCallback(new AsyncCallback(OnConnect));
                    }
 
                    result = parent.connectionInitiator.BeginConnect(
                         parent.via, this.connectTimeout, onConnect, this);
                }
                catch (TimeoutException e)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        parent.CreateNewConnectionTimeoutException(connectTimeout, e));
                }
 
                if (!result.CompletedSynchronously)
                {
                    return false;
                }
 
                return HandleConnect(result);
            }
 
            bool HandleConnect(IAsyncResult connectResult)
            {
                try
                {
                    TrackConnection(parent.connectionInitiator.EndConnect(connectResult));
                }
                catch (TimeoutException e)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                        parent.CreateNewConnectionTimeoutException(connectTimeout, e));
                }
 
                if (ProcessConnection())
                {
                    // success. Snapshot and return
                    SnapshotConnection();
                    return true;
                }
                else
                {
                    return false;
                }
            }
 
            bool ProcessConnection()
            {
                IAsyncResult result = parent.BeginAcceptPooledConnection(this.rawConnection,
                    ref timeoutHelper, onProcessConnection, this);
                if (!result.CompletedSynchronously)
                {
                    return false;
                }
 
                return HandleProcessConnection(result);
            }
 
            bool HandleProcessConnection(IAsyncResult result)
            {
                this.currentConnection = parent.EndAcceptPooledConnection(result);
                this.cleanupConnection = false;
                return true;
            }
 
            void SnapshotConnection()
            {
                parent.SnapshotConnection(this.currentConnection, this.rawConnection, !this.newConnection);
            }
 
            void TrackConnection(IConnection connection)
            {
                this.cleanupConnection = true;
                this.rawConnection = connection;
                this.currentConnection = connection;
            }
 
            void Cleanup()
            {
                if (this.cleanupConnection)
                {
                    if (this.newConnection)
                    {
                        if (this.currentConnection != null)
                        {
                            this.currentConnection.Abort();
                            this.currentConnection = null;
                        }
                    }
                    else if (this.rawConnection != null)
                    {
                        if (DiagnosticUtility.ShouldTraceInformation)
                        {
                            TraceUtility.TraceEvent(
                                TraceEventType.Information,
                                TraceCode.FailedAcceptFromPool,
                                SR.GetString(
                                    SR.TraceCodeFailedAcceptFromPool,
                                    this.timeoutHelper.RemainingTime()));
                        }
 
                        // This cannot throw TimeoutException since isConnectionStillGood is false (doesn't attempt a Close).
                        parent.connectionPool.ReturnConnection(parent.connectionKey, this.rawConnection,
                            false, timeoutHelper.RemainingTime());
                        this.currentConnection = null;
                        this.rawConnection = null;
                    }
 
                    this.cleanupConnection = false;
                }
            }
 
            static void OnConnect(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                {
                    return;
                }
 
                EstablishConnectionAsyncResult thisPtr = (EstablishConnectionAsyncResult)result.AsyncState;
 
                Exception completionException = null;
                bool completeSelf;
                try
                {
                    completeSelf = thisPtr.HandleConnect(result);
                }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    completeSelf = true;
                    completionException = e;
                }
 
                if (completeSelf)
                {
                    thisPtr.Cleanup();
                    thisPtr.Complete(false, completionException);
                }
            }
 
            static void OnProcessConnection(IAsyncResult result)
            {
                if (result.CompletedSynchronously)
                {
                    return;
                }
 
                EstablishConnectionAsyncResult thisPtr = (EstablishConnectionAsyncResult)result.AsyncState;
 
                Exception completionException = null;
                bool completeSelf;
                try
                {
                    bool snapshotCollection = false;
                    try
                    {
                        completeSelf = thisPtr.HandleProcessConnection(result);
                        if (completeSelf)
                        {
                            snapshotCollection = true;
                        }
                    }
                    catch (CommunicationException communicationException)
                    {
                        if (!thisPtr.newConnection) // CommunicationException is ok from our cache
                        {
                            DiagnosticUtility.TraceHandledException(communicationException, TraceEventType.Information);                            
                            thisPtr.Cleanup();
                            completeSelf = thisPtr.Begin();
                        }
                        else
                        {
                            completeSelf = true;
                            completionException = communicationException;
                        }
                    }
                    catch (TimeoutException timeoutException)
                    {
                        if (!thisPtr.newConnection) // TimeoutException is ok from our cache
                        {
                            if (TD.OpenTimeoutIsEnabled())
                            {
                                TD.OpenTimeout(timeoutException.Message);
                            }
                            DiagnosticUtility.TraceHandledException(timeoutException, TraceEventType.Information);                            
                            thisPtr.Cleanup();
                            completeSelf = thisPtr.Begin();
                        }
                        else
                        {
                            completeSelf = true;
                            completionException = timeoutException;
                        }
                    }
 
                    if (snapshotCollection)
                    {
                        thisPtr.SnapshotConnection();
                    }
                }
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    completeSelf = true;
                    completionException = e;
                }
 
                if (completeSelf)
                {
                    thisPtr.Cleanup();
                    thisPtr.Complete(false, completionException);
                }
            }
        }
    }
}