public void CheckHealth(Connection c) { if (c.TimedOutOperations < _config.SocketOptions.DefunctReadTimeoutThreshold) { return; } //We are in the default thread-pool (non-io thread) //Defunct: close it and remove it from the pool _connections.Remove(c); c.Dispose(); }
/// <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 await 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)); }
/// <exception cref="System.Net.Sockets.SocketException">Throws a SocketException when the connection could not be established with the host</exception> /// <exception cref="AuthenticationException" /> /// <exception cref="UnsupportedProtocolVersionException"></exception> internal virtual Task<Connection> CreateConnection() { Logger.Info("Creating a new connection to the host " + _host.Address); var c = new Connection(_serializer, _host.Address, _config); return c.Open().ContinueWith(t => { if (t.Status == TaskStatus.RanToCompletion) { if (_config.GetPoolingOptions(_serializer.ProtocolVersion).GetHeartBeatInterval() > 0) { //Heartbeat is enabled, subscribe for possible exceptions c.OnIdleRequestException += OnIdleRequestException; } return c; } Logger.Info("The connection to {0} could not be opened", _host.Address); c.Dispose(); if (t.Exception != null) { t.Exception.Handle(_ => true); Logger.Error(t.Exception.InnerException); throw t.Exception.InnerException; } throw new TaskCanceledException("The connection creation task was cancelled"); }, TaskContinuationOptions.ExecuteSynchronously); }
/// <summary> /// Closes the connection and removes it from the pool /// </summary> public void Remove(Connection c) { OnConnectionClosing(c); c.Dispose(); }