public void ToTimeout_should_return_the_amount_of_time_left()
        {
            var subject = new SlidingTimeout(TimeSpan.FromMilliseconds(20), _clock);

            subject.ToTimeout().Should().Be(TimeSpan.FromMilliseconds(20));

            _clock.UtcNow = _clock.UtcNow.AddMilliseconds(10);

            subject.ToTimeout().Should().Be(TimeSpan.FromMilliseconds(10));

            _clock.UtcNow = _clock.UtcNow.AddMilliseconds(10);

            subject.ToTimeout().Should().Be(TimeSpan.Zero);
        }
        public void ToTimeout_should_throw_a_TimeoutException_when_the_timeout_has_expired()
        {
            var subject = new SlidingTimeout(TimeSpan.FromMilliseconds(20), _clock);

            _clock.UtcNow = _clock.UtcNow.AddMilliseconds(30);

            Action act = () => subject.ToTimeout();

            act.ShouldThrow <TimeoutException>();
        }
        // public methods
        public async Task<IConnectionHandle> AcquireConnectionAsync(TimeSpan timeout, CancellationToken cancellationToken)
        {
            ThrowIfNotOpen();

            var slidingTimeout = new SlidingTimeout(timeout);

            bool enteredWaitQueue = false;
            bool enteredPool = false;

            Stopwatch stopwatch = new Stopwatch();
            try
            {
                if (_listener != null)
                {
                    _listener.ConnectionPoolBeforeEnteringWaitQueue(_serverId);
                }

                stopwatch.Start();
                enteredWaitQueue = _waitQueue.Wait(0); // don't wait...
                if (!enteredWaitQueue)
                {
                    throw new MongoException("Too many waiters in the connection pool.");
                }
                stopwatch.Stop();

                if(_listener != null)
                {
                    _listener.ConnectionPoolAfterEnteringWaitQueue(_serverId, stopwatch.Elapsed);
                    _listener.ConnectionPoolBeforeCheckingOutAConnection(_serverId);
                }

                stopwatch.Restart();
                var waitQueueTimeout = (int)Math.Min(slidingTimeout.ToTimeout().TotalMilliseconds, timeout.TotalMilliseconds);
                if (waitQueueTimeout == Timeout.Infinite)
                {
                    // if one of these is infinite (-1), then we don't timeout properly
                    waitQueueTimeout = (int)Math.Max(slidingTimeout.ToTimeout().TotalMilliseconds, timeout.TotalMilliseconds);
                }
                enteredPool = await _poolQueue.WaitAsync(TimeSpan.FromMilliseconds(waitQueueTimeout), cancellationToken);

                if (enteredPool)
                {
                    var acquired = AcquireConnection();
                    stopwatch.Stop();
                    if (_listener != null)
                    {
                        _listener.ConnectionPoolAfterCheckingOutAConnection(acquired.ConnectionId, stopwatch.Elapsed);
                    }
                    return acquired;
                }

                stopwatch.Stop();
                var message = string.Format("Timed out waiting for a connection after {0}ms.", stopwatch.ElapsedMilliseconds);
                throw new TimeoutException(message);
            }
            catch (Exception ex)
            {
                if (enteredPool)
                {
                    try
                    {
                        _poolQueue.Release();
                    }
                    catch
                    {
                        // TODO: log this, but don't throw... it's a bug if we get here
                    }
                }

                if (_listener != null)
                {
                    if (!enteredWaitQueue)
                    {
                        _listener.ConnectionPoolErrorEnteringWaitQueue(_serverId, stopwatch.Elapsed, ex);
                    }
                    else
                    {
                        _listener.ConnectionPoolErrorCheckingOutAConnection(_serverId, stopwatch.Elapsed, ex);
                    }
                }
                throw;
            }
            finally
            {
                if (enteredWaitQueue)
                {
                    try
                    {
                        _waitQueue.Release();
                    }
                    catch
                    {
                        // TODO: log this, but don't throw... it's a bug if we get here
                    }
                }
            }
        }
        public void ToTimeout_should_return_infinite_TimeSpan_when_expiration_is_MaxValue()
        {
            var subject = new SlidingTimeout(Timeout.InfiniteTimeSpan);

            subject.ToTimeout().Should().Be(Timeout.InfiniteTimeSpan);
        }
        public void ToTimeout_should_throw_a_TimeoutException_when_the_timeout_has_expired()
        {
            var subject = new SlidingTimeout(TimeSpan.FromMilliseconds(20), _clock);

            _clock.UtcNow = _clock.UtcNow.AddMilliseconds(30);

            Action act = () => subject.ToTimeout();

            act.ShouldThrow<TimeoutException>();
        }
        public void ToTimeout_should_return_the_amount_of_time_left()
        {
            var subject = new SlidingTimeout(TimeSpan.FromMilliseconds(20), _clock);

            subject.ToTimeout().Should().Be(TimeSpan.FromMilliseconds(20));

            _clock.UtcNow = _clock.UtcNow.AddMilliseconds(10);

            subject.ToTimeout().Should().Be(TimeSpan.FromMilliseconds(10));

            _clock.UtcNow = _clock.UtcNow.AddMilliseconds(10);

            subject.ToTimeout().Should().Be(TimeSpan.Zero);
        }
        public void ToTimeout_should_return_infinite_TimeSpan_when_expiration_is_MaxValue()
        {
            var subject = new SlidingTimeout(Timeout.InfiniteTimeSpan);

            subject.ToTimeout().Should().Be(Timeout.InfiniteTimeSpan);
        }