Пример #1
0
        /// <summary>
        /// Create the min amount of connections, if the pool is empty.
        /// It may return an empty array if its being closed.
        /// It may return an array of connections being closed.
        /// </summary>
        internal Task <Connection[]> MaybeCreateFirstConnection()
        {
            var tcs         = new TaskCompletionSource <Connection[]>();
            var connections = _connections.GetSnapshot();

            if (connections.Length > 0)
            {
                tcs.SetResult(connections);
                return(tcs.Task);
            }
            var creationTcs = Interlocked.CompareExchange(ref _creationTcs, tcs, null);

            if (creationTcs != null)
            {
                return(creationTcs.Task);
            }
            //Could have transitioned
            connections = _connections.GetSnapshot();
            if (connections.Length > 0)
            {
                TransitionCreationTask(tcs, connections);
                return(tcs.Task);
            }
            if (_isShuttingDown)
            {
                //It transitioned to DOWN, avoid try to create new Connections
                TransitionCreationTask(tcs, EmptyConnectionsArray);
                return(tcs.Task);
            }
            Logger.Info("Initializing pool to {0}", _host.Address);
            //There is a single thread creating a single connection
            CreateConnection().ContinueWith(t =>
            {
                if (t.Status == TaskStatus.RanToCompletion)
                {
                    if (_isShuttingDown)
                    {
                        //Is shutting down
                        t.Result.Dispose();
                        TransitionCreationTask(tcs, EmptyConnectionsArray);
                        return;
                    }
                    _connections.Add(t.Result);
                    _host.BringUpIfDown();
                    TransitionCreationTask(tcs, new[] { t.Result });
                    return;
                }
                if (t.Exception != null)
                {
                    TransitionCreationTask(tcs, null, t.Exception.InnerException);
                    return;
                }
                TransitionCreationTask(tcs, EmptyConnectionsArray);
            }, TaskContinuationOptions.ExecuteSynchronously);
            return(tcs.Task);
        }
Пример #2
0
        /// <summary>
        /// Opens one connection.
        /// If a connection is being opened it yields the same task, preventing creation in parallel.
        /// </summary>
        /// <exception cref="SocketException">Throws a SocketException when the connection could not be established with the host</exception>
        /// <exception cref="AuthenticationException" />
        /// <exception cref="UnsupportedProtocolVersionException" />
        private async Task <Connection> CreateOpenConnection(bool foreground)
        {
            var concurrentOpenTcs = Volatile.Read(ref _connectionOpenTcs);

            // Try to exit early (cheap) as there could be another thread creating / finishing creating
            if (concurrentOpenTcs != null)
            {
                // There is another thread opening a new connection
                return(await concurrentOpenTcs.Task.ConfigureAwait(false));
            }
            var tcs = new TaskCompletionSource <Connection>();

            // Try to set the creation task source
            concurrentOpenTcs = Interlocked.CompareExchange(ref _connectionOpenTcs, tcs, null);
            if (concurrentOpenTcs != null)
            {
                // There is another thread opening a new connection
                return(await concurrentOpenTcs.Task.ConfigureAwait(false));
            }
            if (IsClosing)
            {
                return(await FinishOpen(tcs, false, GetNotConnectedException()).ConfigureAwait(false));
            }
            // Before creating, make sure that its still needed
            // This method is the only one that adds new connections
            // But we don't control the removal, use snapshot
            var connectionsSnapshot = _connections.GetSnapshot();

            if (connectionsSnapshot.Length >= _expectedConnectionLength)
            {
                if (connectionsSnapshot.Length == 0)
                {
                    // Avoid race condition while removing
                    return(await FinishOpen(tcs, false, GetNotConnectedException()).ConfigureAwait(false));
                }
                return(await FinishOpen(tcs, true, null, connectionsSnapshot[0]).ConfigureAwait(false));
            }
            if (foreground && !_canCreateForeground)
            {
                // Foreground creation only cares about one connection
                // If its already there, yield it
                connectionsSnapshot = _connections.GetSnapshot();
                if (connectionsSnapshot.Length == 0)
                {
                    // When creating in foreground, it failed
                    return(await FinishOpen(tcs, false, GetNotConnectedException()).ConfigureAwait(false));
                }
                return(await FinishOpen(tcs, false, null, connectionsSnapshot[0]).ConfigureAwait(false));
            }
            Logger.Info("Creating a new connection to {0}", _host.Address);
            Connection c          = null;
            Exception  creationEx = null;

            try
            {
                c = await DoCreateAndOpen().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Logger.Info("Connection to {0} could not be created: {1}", _host.Address, ex);
                // Can not be awaited on catch on C# 5...
                creationEx = ex;
            }
            if (creationEx != null)
            {
                return(await FinishOpen(tcs, true, creationEx).ConfigureAwait(false));
            }
            if (IsClosing)
            {
                Logger.Info("Connection to {0} opened successfully but pool #{1} was being closed",
                            _host.Address, GetHashCode());
                c.Dispose();
                return(await FinishOpen(tcs, false, GetNotConnectedException()).ConfigureAwait(false));
            }
            var newLength = _connections.AddNew(c);

            Logger.Info("Connection to {0} opened successfully, pool #{1} length: {2}",
                        _host.Address, GetHashCode(), newLength);
            if (IsClosing)
            {
                // We haven't use a CAS operation, so it's possible that the pool is being closed while adding a new
                // connection, we should remove it.
                Logger.Info("Connection to {0} opened successfully and added to the pool #{1} but it was being closed",
                            _host.Address, GetHashCode());
                _connections.Remove(c);
                c.Dispose();
                return(await FinishOpen(tcs, false, GetNotConnectedException()).ConfigureAwait(false));
            }
            return(await FinishOpen(tcs, true, null, c).ConfigureAwait(false));
        }