Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,28 @@ internal static Timer UnsafeCreateTimer(TimerCallback callback, object state, Ti
}
}

/// <summary>
/// Creates an <see cref="ITimer"/> using the supplied <see cref="TimeProvider"/> without
/// capturing the current <see cref="ExecutionContext"/>.
/// </summary>
internal static ITimer UnsafeCreateTimer<T>(
TimeProvider timeProvider,
Action<T> callback,
T state,
TimeSpan dueTime,
TimeSpan period)
{
if (ExecutionContext.IsFlowSuppressed())
{
return timeProvider.CreateTimer(s => callback((T)s), state, dueTime, period);
}

using (ExecutionContext.SuppressFlow())
{
return timeProvider.CreateTimer(s => callback((T)s), state, dueTime, period);
}
}


#region COM+ exceptions
internal static ArgumentException Argument(string error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,11 @@ public void Dispose()
private volatile bool _errorOccurred;

private int _errorWait;
private Timer _errorTimer;
private ITimer _errorTimer;

// Clock used to create the error backoff timer. Defaults to the system clock in
// production; tests inject a fake clock to drive the exit timer deterministically.
private readonly TimeProvider _timeProvider;

private Timer _cleanupTimer;

Expand All @@ -212,7 +216,8 @@ internal WaitHandleDbConnectionPool(
SqlConnectionFactory connectionFactory,
DbConnectionPoolGroup connectionPoolGroup,
DbConnectionPoolIdentity identity,
DbConnectionPoolProviderInfo connectionPoolProviderInfo)
DbConnectionPoolProviderInfo connectionPoolProviderInfo,
TimeProvider timeProvider = null)
{
Debug.Assert(connectionPoolGroup != null, "null connectionPoolGroup");

Expand Down Expand Up @@ -249,6 +254,7 @@ internal WaitHandleDbConnectionPool(
_connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions;
_connectionPoolProviderInfo = connectionPoolProviderInfo;
_identity = identity;
_timeProvider = timeProvider ?? TimeProvider.System;

_waitHandles = new PoolWaitHandles();

Expand Down Expand Up @@ -597,7 +603,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio
_resError = e;

// Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent.
Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite);
ITimer t = ADP.UnsafeCreateTimer(_timeProvider, state => state.ErrorCallback(null), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);

bool timerIsNotDisposed;

Expand All @@ -608,7 +614,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio
// Note that the timer is created to allow periodic invocation. If ThreadAbort occurs in the middle of ErrorCallback,
// the timer will restart. Otherwise, the timer callback (ErrorCallback) destroys the timer after resetting the error to avoid second callback.
_errorTimer = t;
timerIsNotDisposed = t.Change(_errorWait, _errorWait);
timerIsNotDisposed = t.Change(TimeSpan.FromMilliseconds(_errorWait), TimeSpan.FromMilliseconds(_errorWait));

Debug.Assert(timerIsNotDisposed, "ErrorCallback timer has been disposed");

Expand Down Expand Up @@ -799,7 +805,7 @@ private void ErrorCallback(object state)
_waitHandles.ErrorEvent.Reset();

// the error state is cleaned, destroy the timer to avoid periodic invocation
Timer t = _errorTimer;
ITimer t = _errorTimer;
_errorTimer = null;
if (t != null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.Data.SqlClient.UnitTests.ConnectionPool
{
/// <summary>
/// Test helper for creating <see cref="SqlException"/> instances. Since <see cref="SqlException"/> has
/// an internal constructor, instances must be created via the <see cref="SqlException.CreateException"/> factory method.
/// </summary>
internal static class SqlExceptionHelper
{
/// <summary>
/// Creates a <see cref="SqlException"/> with the specified message using the internal factory method.
/// </summary>
/// <param name="message">The error message for the exception.</param>
/// <returns>A new <see cref="SqlException"/> with the specified message.</returns>
internal static SqlException CreateSqlException(string message)
{
var collection = new SqlErrorCollection();
collection.Add(new SqlError(0, (byte)0, (byte)0, "TestServer", message, "", 0));
return SqlException.CreateException(collection, "");
}
}
}
Loading
Loading