internal Task <NpgsqlConnector> Allocate(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) { Monitor.Enter(this); while (Idle.Count > 0) { var connector = Idle.Pop(); // An idle connector could be broken because of a keepalive if (connector.IsBroken) { continue; } connector.Connection = conn; IncrementBusy(); EnsurePruningTimerState(); Monitor.Exit(this); return(Task.FromResult <NpgsqlConnector>(connector)); } // No idle connectors available. Have to actually open a new connector or wait for one. return(AllocateLong(conn, timeout, async, cancellationToken)); }
internal async Task <NpgsqlConnector> AllocateLong(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) { Debug.Assert(Monitor.IsEntered(this)); NpgsqlConnector connector; Debug.Assert(Busy <= _max); if (Busy == _max) { // TODO: Async cancellation var tcs = new TaskCompletionSource <NpgsqlConnector>(); _waiting.Enqueue(new WaitingOpenAttempt { TaskCompletionSource = tcs, IsAsync = async }); Monitor.Exit(this); try { if (async) { if (timeout.IsSet) { var timeLeft = timeout.TimeLeft; if (timeLeft <= TimeSpan.Zero || tcs.Task != await Task.WhenAny(tcs.Task, Task.Delay(timeLeft))) { throw new NpgsqlException($"The connection pool has been exhausted, either raise MaxPoolSize (currently {_max}) or Timeout (currently {Settings.Timeout} seconds)"); } } else { await tcs.Task; } } else { if (timeout.IsSet) { var timeLeft = timeout.TimeLeft; if (timeLeft <= TimeSpan.Zero || !tcs.Task.Wait(timeLeft)) { throw new NpgsqlException($"The connection pool has been exhausted, either raise MaxPoolSize (currently {_max}) or Timeout (currently {Settings.Timeout} seconds)"); } } else { tcs.Task.Wait(); } } } catch { // We're here if the timeout expired or the cancellation token was triggered // Re-lock and check in case the task was set to completed after coming out of the Wait lock (this) { if (!tcs.Task.IsCompleted) { tcs.SetCanceled(); throw; } } } connector = tcs.Task.Result; connector.Connection = conn; return(connector); } // No idle connectors are available, and we're under the pool's maximum capacity. IncrementBusy(); Monitor.Exit(this); try { connector = new NpgsqlConnector(conn) { ClearCounter = _clearCounter }; await connector.Open(timeout, async, cancellationToken); Counters.NumberOfPooledConnections.Increment(); EnsureMinPoolSize(conn); return(connector); } catch { lock (this) DecrementBusy(); throw; } }
internal static Task WithCancellationAndTimeout(this Task task, NpgsqlTimeout timeout, CancellationToken cancellationToken) { return(task .WithCancellation(cancellationToken) .WithTimeout(timeout)); }