void Dispose() { if (_isDisposed) { return; } Debug.Assert(_transaction != null, "No transaction"); Debug.Assert(_connector != null, "No connector"); Log.Trace($"Cleaning up resource manager (localid={_txId}", _connector.Id); if (_localTx != null) { _localTx.Dispose(); _localTx = null; } if (_connector.Connection != null) { _connector.Connection.EnlistedTransaction = null; } else { // We're here for connections which were closed before their TransactionScope completes. // These need to be closed now. if (_connector.Settings.Pooling) { var found = PoolManager.Pools.TryGetValue(_connector.ConnectionString, out var pool); Debug.Assert(found); pool.TryRemovePendingEnlistedConnector(_connector, _transaction); pool.Release(_connector); } else { _connector.Close(); } } _connector = null; _transaction = null; _isDisposed = true; }
internal void Release(NpgsqlConnector connector) { // If Clear/ClearAll has been been called since this connector was first opened, // throw it away. if (connector.ClearCounter < _clearCounter) { try { connector.Close(); } catch (Exception e) { Log.Warn("Exception while closing outdated connector", e, connector.Id); } lock (this) DecrementBusy(); Counters.SoftDisconnectsPerSecond.Increment(); Counters.NumberOfPooledConnections.Decrement(); return; } if (connector.IsBroken) { lock (this) DecrementBusy(); Counters.NumberOfPooledConnections.Decrement(); return; } connector.Reset(); lock (this) { // If there are any pending open attempts in progress hand the connector off to // them directly. while (_waiting.Count > 0) { var waitingOpenAttempt = _waiting.Dequeue(); var tcs = waitingOpenAttempt.TaskCompletionSource; // Some attempts may be in the queue but in cancelled state, since they've already timed out. // Simply dequeue these and move on. if (tcs.Task.IsCanceled) { continue; } // We have a pending open attempt. "Complete" it, handing off the connector. if (waitingOpenAttempt.IsAsync) { // If the waiting open attempt is asynchronous (i.e. OpenAsync()), we can't simply // call SetResult on its TaskCompletionSource, since it would execute the open's // continuation in our thread (the closing thread). Instead we schedule the completion // to run in the TP // We copy tcs2 and especially connector2 to avoid allocations caused by the closure, see // http://stackoverflow.com/questions/41507166/closure-heap-allocation-happening-at-start-of-method var tcs2 = tcs; var connector2 = connector; Task.Run(() => tcs2.TrySetResult(connector2)); } else { tcs.SetResult(connector); } return; } Idle.Push(connector); DecrementBusy(); EnsurePruningTimerState(); Debug.Assert(Idle.Count <= _max); } }