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;
            }
        }
示例#3
0
 internal static Task WithCancellationAndTimeout(this Task task, NpgsqlTimeout timeout, CancellationToken cancellationToken)
 {
     return(task
            .WithCancellation(cancellationToken)
            .WithTimeout(timeout));
 }