Skip to content

Commit 7ba73a8

Browse files
Address PR #4302 review: avoid reflection in WaitHandleDbConnectionPool shutdown tests
Replace reflection-based access of WaitHandleDbConnectionPool internals from the shutdown unit tests with direct field/method access. The pool's _waitCount, _errorTimer, _cleanupTimer, CleanupCallback, and ErrorCallback are now declared internal so the UnitTests assembly (covered by InternalsVisibleTo) can reach them without GetField/Invoke. The GetPrivateField<T> helper is removed. Per @mdaigle review feedback on #4302.
1 parent b24d4fb commit 7ba73a8

2 files changed

Lines changed: 15 additions & 35 deletions

File tree

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,16 @@ public void Dispose()
189189

190190
private readonly WaitCallback _poolCreateRequest;
191191

192-
private int _waitCount;
192+
internal int _waitCount;
193193
private readonly PoolWaitHandles _waitHandles;
194194

195195
private Exception _resError;
196196
private volatile bool _errorOccurred;
197197

198198
private int _errorWait;
199-
private Timer _errorTimer;
199+
internal Timer _errorTimer;
200200

201-
private Timer _cleanupTimer;
201+
internal Timer _cleanupTimer;
202202

203203
private readonly TransactedConnectionPool _transactedConnectionPool;
204204

@@ -327,7 +327,7 @@ public bool IsRunning
327327

328328
public TransactedConnectionPool TransactedConnectionPool => _transactedConnectionPool;
329329

330-
private void CleanupCallback(object state)
330+
internal void CleanupCallback(object state)
331331
{
332332
// If the pool is shutting down, skip work. Shutdown disposes the timer, but
333333
// a callback may already be in-flight when Shutdown runs; this guard ensures it does
@@ -773,7 +773,7 @@ private void DestroyObject(DbConnectionInternal obj)
773773
}
774774
}
775775

776-
private void ErrorCallback(object state)
776+
internal void ErrorCallback(object state)
777777
{
778778
// Skip work if the pool is shutting down. The shutdown path disposes the
779779
// timer; this guard handles the in-flight-callback race.

src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/WaitHandleDbConnectionPoolShutdownTest.cs

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6-
using System.Reflection;
76
using System.Threading;
87
using System.Threading.Tasks;
98
using Microsoft.Data.Common.ConnectionString;
@@ -42,13 +41,6 @@ private static WaitHandleDbConnectionPool CreatePool(int maxPoolSize = 5)
4241
return pool;
4342
}
4443

45-
private static T GetPrivateField<T>(object instance, string fieldName)
46-
{
47-
var field = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
48-
Assert.NotNull(field);
49-
return (T)field!.GetValue(instance)!;
50-
}
51-
5244
// State transitions to ShuttingDown on Shutdown.
5345
[Fact]
5446
public void Shutdown_TransitionsState_ToShuttingDown()
@@ -67,13 +59,11 @@ public void Shutdown_TransitionsState_ToShuttingDown()
6759
public void Shutdown_DisposesCleanupTimer()
6860
{
6961
var pool = CreatePool();
70-
var beforeTimer = GetPrivateField<Timer?>(pool, "_cleanupTimer");
71-
Assert.NotNull(beforeTimer);
62+
Assert.NotNull(pool._cleanupTimer);
7263

7364
pool.Shutdown();
7465

75-
var afterTimer = GetPrivateField<Timer?>(pool, "_cleanupTimer");
76-
Assert.Null(afterTimer);
66+
Assert.Null(pool._cleanupTimer);
7767
}
7868

7969
// Error timer is disposed when present.
@@ -82,15 +72,11 @@ public void Shutdown_DisposesErrorTimer_WhenPresent()
8272
{
8373
var pool = CreatePool();
8474
// Inject a real Timer into _errorTimer to mimic an error-state pool.
85-
var injected = new Timer(_ => { }, null, Timeout.Infinite, Timeout.Infinite);
86-
var field = pool.GetType().GetField("_errorTimer", BindingFlags.NonPublic | BindingFlags.Instance);
87-
Assert.NotNull(field);
88-
field!.SetValue(pool, injected);
75+
pool._errorTimer = new Timer(_ => { }, null, Timeout.Infinite, Timeout.Infinite);
8976

9077
pool.Shutdown();
9178

92-
var afterTimer = GetPrivateField<Timer?>(pool, "_errorTimer");
93-
Assert.Null(afterTimer);
79+
Assert.Null(pool._errorTimer);
9480
}
9581

9682
// Drains idle stacks.
@@ -136,12 +122,9 @@ public void CleanupCallback_AfterShutdown_IsNoOp()
136122
var pool = CreatePool();
137123
pool.Shutdown();
138124

139-
// Invoke the private callback directly via reflection. Must not throw and must
140-
// not re-arm any pool create requests.
141-
var method = pool.GetType().GetMethod("CleanupCallback", BindingFlags.NonPublic | BindingFlags.Instance);
142-
Assert.NotNull(method);
143-
144-
var ex = Record.Exception(() => method!.Invoke(pool, new object?[] { null }));
125+
// Invoke the callback directly. Must not throw and must not re-arm any pool
126+
// create requests.
127+
var ex = Record.Exception(() => pool.CleanupCallback(state: null));
145128
Assert.Null(ex);
146129
Assert.Equal(DbConnectionPoolState.ShuttingDown, pool.State);
147130
}
@@ -153,10 +136,7 @@ public void ErrorCallback_AfterShutdown_IsNoOp()
153136
var pool = CreatePool();
154137
pool.Shutdown();
155138

156-
var method = pool.GetType().GetMethod("ErrorCallback", BindingFlags.NonPublic | BindingFlags.Instance);
157-
Assert.NotNull(method);
158-
159-
var ex = Record.Exception(() => method!.Invoke(pool, new object?[] { null }));
139+
var ex = Record.Exception(() => pool.ErrorCallback(state: null));
160140
Assert.Null(ex);
161141
}
162142

@@ -216,11 +196,11 @@ public void Shutdown_UnblocksSyncWaiter()
216196
// happens immediately before it enters WaitHandle.WaitAny. Polling avoids the
217197
// CI-flakiness of a fixed Thread.Sleep on slow agents.
218198
var deadline = DateTime.UtcNow.AddSeconds(5);
219-
while (DateTime.UtcNow < deadline && GetPrivateField<int>(pool, "_waitCount") < 1)
199+
while (DateTime.UtcNow < deadline && pool._waitCount < 1)
220200
{
221201
Thread.Yield();
222202
}
223-
Assert.True(GetPrivateField<int>(pool, "_waitCount") >= 1, "Waiter did not park within 5s.");
203+
Assert.True(pool._waitCount >= 1, "Waiter did not park within 5s.");
224204
Assert.True(t.IsAlive, "Waiter should be parked, but thread already exited.");
225205

226206
pool.Shutdown();

0 commit comments

Comments
 (0)