Skip to content

Commit 9ca19fc

Browse files
[6.0] Fix connection pool concurrency issue (#3632) (#3654)
1 parent c2184c6 commit 9ca19fc

File tree

3 files changed

+472
-32
lines changed

3 files changed

+472
-32
lines changed

src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ internal WaitHandle[] GetHandles(bool withCreate)
362362
}
363363
}
364364

365-
private const int MAX_Q_SIZE = (int)0x00100000;
365+
private const int MAX_Q_SIZE = 0x00100000;
366366

367367
// The order of these is important; we want the WaitAny call to be signaled
368368
// for a free object before a creation signal. Only the index first signaled
@@ -1412,29 +1412,36 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj
14121412
DestroyObject(obj);
14131413
obj = null; // Setting to null in case creating a new object fails
14141414

1415+
bool obtained = false;
14151416
if (onlyOneCheckConnection)
14161417
{
1417-
if (_waitHandles.CreationSemaphore.WaitOne(unchecked((int)waitForMultipleObjectsTimeout)))
1418-
{
14191418
#if NETFRAMEWORK
1420-
RuntimeHelpers.PrepareConstrainedRegions();
1419+
RuntimeHelpers.PrepareConstrainedRegions();
14211420
#endif
1422-
try
1421+
try
1422+
{
1423+
obtained = _waitHandles.CreationSemaphore.WaitOne((int)waitForMultipleObjectsTimeout);
1424+
if (obtained)
14231425
{
14241426
SqlClientEventSource.Log.TryPoolerTraceEvent("<prov.DbConnectionPool.GetConnection|RES|CPOOL> {0}, Creating new connection.", ObjectID);
14251427
obj = UserCreateRequest(owningObject, userOptions);
14261428
}
1427-
finally
1429+
else
14281430
{
1429-
_waitHandles.CreationSemaphore.Release(1);
1431+
// Timeout waiting for creation semaphore - return null
1432+
SqlClientEventSource.Log.TryPoolerTraceEvent("<prov.DbConnectionPool.GetConnection|RES|CPOOL> {0}, Wait timed out.", ObjectID);
1433+
connection = null;
1434+
return false;
14301435
}
14311436
}
1432-
else
1437+
finally
14331438
{
1434-
// Timeout waiting for creation semaphore - return null
1435-
SqlClientEventSource.Log.TryPoolerTraceEvent("<prov.DbConnectionPool.GetConnection|RES|CPOOL> {0}, Wait timed out.", ObjectID);
1436-
connection = null;
1437-
return false;
1439+
if (obtained)
1440+
{
1441+
// Ensure that we release this waiter, regardless
1442+
// of any exceptions that may be thrown.
1443+
_waitHandles.CreationSemaphore.Release(1);
1444+
}
14381445
}
14391446
}
14401447
}
@@ -1657,25 +1664,17 @@ private void PoolCreateRequest(object state)
16571664
{
16581665
return;
16591666
}
1660-
int waitResult = BOGUS_HANDLE;
16611667

1668+
bool obtained = false;
16621669
#if NETFRAMEWORK
16631670
RuntimeHelpers.PrepareConstrainedRegions();
16641671
#endif
16651672
try
16661673
{
16671674
// Obtain creation mutex so we're the only one creating objects
1668-
// and we must have the wait result
1669-
#if NETFRAMEWORK
1670-
RuntimeHelpers.PrepareConstrainedRegions();
1671-
#endif
1672-
try
1673-
{ }
1674-
finally
1675-
{
1676-
waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(withCreate: true), CreationTimeout);
1677-
}
1678-
if (CREATION_HANDLE == waitResult)
1675+
obtained = _waitHandles.CreationSemaphore.WaitOne(CreationTimeout);
1676+
1677+
if (obtained)
16791678
{
16801679
DbConnectionInternal newObj;
16811680

@@ -1710,17 +1709,12 @@ private void PoolCreateRequest(object state)
17101709
}
17111710
}
17121711
}
1713-
else if (WaitHandle.WaitTimeout == waitResult)
1712+
else
17141713
{
17151714
// do not wait forever and potential block this worker thread
17161715
// instead wait for a period of time and just requeue to try again
17171716
QueuePoolCreateRequest();
17181717
}
1719-
else
1720-
{
1721-
// trace waitResult and ignore the failure
1722-
SqlClientEventSource.Log.TryPoolerTraceEvent("<prov.DbConnectionPool.PoolCreateRequest|RES|CPOOL> {0}, PoolCreateRequest called WaitForSingleObject failed {1}", ObjectID, waitResult);
1723-
}
17241718
}
17251719
catch (Exception e)
17261720
{
@@ -1736,9 +1730,10 @@ private void PoolCreateRequest(object state)
17361730
}
17371731
finally
17381732
{
1739-
if (CREATION_HANDLE == waitResult)
1733+
if (obtained)
17401734
{
1741-
// reuse waitResult and ignore its value
1735+
// Ensure that we release this waiter, regardless
1736+
// of any exceptions that may be thrown.
17421737
_waitHandles.CreationSemaphore.Release(1);
17431738
}
17441739
}

src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@
253253
<None Include="SQL\ParameterTest\SqlParameterTest_ReleaseMode.bsl" />
254254
<None Include="SQL\ParameterTest\SqlParameterTest_ReleaseMode_Azure.bsl" />
255255
</ItemGroup>
256+
<ItemGroup Condition="'$(TestSet)' == '' OR '$(TestSet)' == '3'">
257+
<Compile Include="SQL\ConnectionPoolTest\ConnectionPoolStressTest.cs" />
258+
</ItemGroup>
256259
<ItemGroup Condition="'$(TargetGroup)'=='netcoreapp' AND ('$(TestSet)' == '' OR '$(TestSet)' == '3')">
257260
<Compile Include="TracingTests\EventCounterTest.cs" />
258261
<Compile Include="TracingTests\DiagnosticTest.cs" />

0 commit comments

Comments
 (0)