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) Busy--; return; } if (connector.IsBroken) { lock (this) Busy--; 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 tcs = Waiting.Dequeue(); // 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. // We do this in another thread because we don't want to execute the continuation here. Task.Run(() => tcs.SetResult(connector)); return; } Idle.Push(connector); Busy--; EnsurePruningTimerState(); Contract.Assert(Idle.Count <= _max); } }
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(() => { if (!tcs2.TrySetResult(connector2)) { // Race condition: the waiter timed out between our IsCanceled check above and here // Recursively call Release again, this will dequeue another open attempt and retry. Debug.Assert(tcs2.Task.IsCanceled); Release(connector2); } }); } else { tcs.SetResult(connector); } return; } Idle.Push(connector); DecrementBusy(); EnsurePruningTimerState(); Debug.Assert(Idle.Count <= _max); } }
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) Busy--; return; } if (connector.IsBroken) { lock (this) Busy--; 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 Task.Run(() => tcs.SetResult(connector)); } else { tcs.SetResult(connector); } return; } Idle.Push(connector); Busy--; EnsurePruningTimerState(); Contract.Assert(Idle.Count <= _max); } }