| File: System\ServiceModel\Channels\ConnectionPoolHelper.cs | Web Access |
| Project: src\src\System.ServiceModel.NetFramingBase\src\System.ServiceModel.NetFramingBase.csproj (System.ServiceModel.NetFramingBase) |
// 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.Runtime; using System.Threading.Tasks; namespace System.ServiceModel.Channels { // Helper class used to manage the lifetime of a connection relative to its pool. internal abstract class ConnectionPoolHelper { private IConnectionInitiator _connectionInitiator; private ConnectionPool _connectionPool; private Uri _via; private bool _closed; // key for rawConnection in the connection pool private string _connectionKey; // did rawConnection originally come from connectionPool? private bool _isConnectionFromPool; // the "raw" connection that should be stored in the pool private IConnection _rawConnection; // the "upgraded" connection built on top of the "raw" connection to be used for I/O private IConnection _upgradedConnection; public ConnectionPoolHelper(ConnectionPool connectionPool, IConnectionInitiator connectionInitiator, Uri via) { _connectionInitiator = connectionInitiator; _connectionPool = connectionPool; _via = via; } private object ThisLock { get { return this; } } protected abstract Task<IConnection> AcceptPooledConnectionAsync(IConnection connection, TimeoutHelper timeoutHelper); protected abstract TimeoutException CreateNewConnectionTimeoutException(TimeSpan timeout, TimeoutException innerException); private IConnection TakeConnection(TimeSpan timeout) { return _connectionPool.TakeConnection(null, _via, timeout, out _connectionKey); } public async Task<IConnection> EstablishConnectionAsync(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); IConnection localRawConnection = null; IConnection localUpgradedConnection = null; bool localIsConnectionFromPool = true; // first try and use a connection from our pool (and use it if we successfully receive an ACK) while (localIsConnectionFromPool) { localRawConnection = TakeConnection(timeoutHelper.RemainingTime()); if (localRawConnection == null) { localIsConnectionFromPool = false; } else { bool preambleSuccess = false; try { localUpgradedConnection = await AcceptPooledConnectionAsync(localRawConnection, timeoutHelper); preambleSuccess = true; break; } catch (CommunicationException) { // CommunicationException is ok since it was a cached connection of unknown state } catch (TimeoutException) { // ditto for TimeoutException } finally { if (!preambleSuccess) { // This cannot throw TimeoutException since isConnectionStillGood is false (doesn't attempt a Close). _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 = await _connectionInitiator.ConnectAsync(_via, connectTimeout); } catch (TimeoutException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateNewConnectionTimeoutException( connectTimeout, e)); } _connectionInitiator = null; localUpgradedConnection = await AcceptPooledConnectionAsync(localRawConnection, timeoutHelper); success = true; } finally { if (!success) { _connectionKey = null; if (localRawConnection != null) { localRawConnection.Abort(); } } } } SnapshotConnection(localUpgradedConnection, localRawConnection, localIsConnectionFromPool); return localUpgradedConnection; } private void SnapshotConnection(IConnection upgradedConnection, IConnection rawConnection, bool isConnectionFromPool) { lock (ThisLock) { if (_closed) { upgradedConnection.Abort(); // cleanup our pool if necessary if (isConnectionFromPool) { _connectionPool.ReturnConnection(_connectionKey, rawConnection, false, TimeSpan.Zero); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new CommunicationObjectAbortedException( SR.Format(SR.OperationAbortedDuringConnectionEstablishment, _via))); } else { _upgradedConnection = upgradedConnection; _rawConnection = rawConnection; _isConnectionFromPool = isConnectionFromPool; } } } public void Abort() { ReleaseConnection(true, TimeSpan.Zero); } public void Close(TimeSpan timeout) { ReleaseConnection(false, timeout); } private void ReleaseConnection(bool abort, TimeSpan timeout) { string localConnectionKey; IConnection localUpgradedConnection; IConnection localRawConnection; lock (ThisLock) { _closed = true; localConnectionKey = _connectionKey; localUpgradedConnection = _upgradedConnection; localRawConnection = _rawConnection; _upgradedConnection = null; _rawConnection = null; } if (localUpgradedConnection == null) { return; } try { if (_isConnectionFromPool) { _connectionPool.ReturnConnection(localConnectionKey, localRawConnection, !abort, timeout); } else { if (abort) { localUpgradedConnection.Abort(); } else { _connectionPool.AddConnection(localConnectionKey, localRawConnection, timeout); } } } catch (CommunicationException /*e*/) { localUpgradedConnection.Abort(); } } } }