Skip to content

Commit

Permalink
Use read-write lock to protect concurrent pool accesses
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Nov 12, 2021
1 parent b38c269 commit c5b0c1a
Showing 1 changed file with 98 additions and 16 deletions.
114 changes: 98 additions & 16 deletions src/Microsoft.Data.Sqlite.Core/SqliteConnectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal class SqliteConnectionFactory
#pragma warning restore IDE0052 // Remove unread private members
private readonly List<SqliteConnectionPoolGroup> _idlePoolGroups = new();
private readonly List<SqliteConnectionPool> _poolsToRelease = new();
private readonly ReaderWriterLockSlim _lock = new();

private Dictionary<string, SqliteConnectionPoolGroup> _poolGroups = new();

Expand Down Expand Up @@ -52,28 +53,65 @@ public SqliteConnectionInternal GetConnection(SqliteConnection outerConnection)

public SqliteConnectionPoolGroup GetPoolGroup(string connectionString)
{
if (!_poolGroups.TryGetValue(connectionString, out var poolGroup)
|| (poolGroup.IsDisabled
&& !poolGroup.IsNonPooled))
var newLockingBehavior = !AppContext.TryGetSwitch("Microsoft.Data.Sqlite.Issue26612", out var enabled) || !enabled;

if (newLockingBehavior)
{
var connectionOptions = new SqliteConnectionStringBuilder(connectionString);
_lock.EnterUpgradeableReadLock();
}

lock (this)
try
{
if (!_poolGroups.TryGetValue(connectionString, out var poolGroup)
|| (poolGroup.IsDisabled
&& !poolGroup.IsNonPooled))
{
if (!_poolGroups.TryGetValue(connectionString, out poolGroup))
var connectionOptions = new SqliteConnectionStringBuilder(connectionString);

if (newLockingBehavior)
{
var isNonPooled = connectionOptions.DataSource == ":memory:"
|| connectionOptions.Mode == SqliteOpenMode.Memory
|| connectionOptions.DataSource.Length == 0
|| !connectionOptions.Pooling;
_lock.EnterWriteLock();
}
else
{
Monitor.Enter(this);
}

poolGroup = new SqliteConnectionPoolGroup(connectionOptions, connectionString, isNonPooled);
_poolGroups.Add(connectionString, poolGroup);
try
{
if (!_poolGroups.TryGetValue(connectionString, out poolGroup))
{
var isNonPooled = connectionOptions.DataSource == ":memory:"
|| connectionOptions.Mode == SqliteOpenMode.Memory
|| connectionOptions.DataSource.Length == 0
|| !connectionOptions.Pooling;

poolGroup = new SqliteConnectionPoolGroup(connectionOptions, connectionString, isNonPooled);
_poolGroups.Add(connectionString, poolGroup);
}
}
finally
{
if (newLockingBehavior)
{
_lock.ExitWriteLock();
}
else
{
Monitor.Exit(this);
}
}
}
}

return poolGroup;
return poolGroup;
}
finally
{
if (newLockingBehavior)
{
_lock.ExitUpgradeableReadLock();
}
}
}

public void ReleasePool(SqliteConnectionPool pool, bool clearing)
Expand All @@ -93,13 +131,35 @@ public void ReleasePool(SqliteConnectionPool pool, bool clearing)

public void ClearPools()
{
lock (this)
var newLockingBehavior = !AppContext.TryGetSwitch("Microsoft.Data.Sqlite.Issue26612", out var enabled) || !enabled;

if (newLockingBehavior)
{
_lock.EnterWriteLock();
}
else
{
Monitor.Enter(this);
}

try
{
foreach (var entry in _poolGroups)
{
entry.Value.Clear();
}
}
finally
{
if (newLockingBehavior)
{
_lock.ExitWriteLock();
}
else
{
Monitor.Exit(this);
}
}
}

private void PruneCallback(object? _)
Expand Down Expand Up @@ -129,7 +189,18 @@ private void PruneCallback(object? _)
}
}

lock (this)
var newLockingBehavior = !AppContext.TryGetSwitch("Microsoft.Data.Sqlite.Issue26612", out var enabled) || !enabled;

if (newLockingBehavior)
{
_lock.EnterWriteLock();
}
else
{
Monitor.Enter(this);
}

try
{
var activePoolGroups = new Dictionary<string, SqliteConnectionPoolGroup>();
foreach (var entry in _poolGroups)
Expand All @@ -148,6 +219,17 @@ private void PruneCallback(object? _)

_poolGroups = activePoolGroups;
}
finally
{
if (newLockingBehavior)
{
_lock.ExitWriteLock();
}
else
{
Monitor.Exit(this);
}
}
}
}
}

0 comments on commit c5b0c1a

Please sign in to comment.