<commit_before>using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using DHT.Utils.Logging;
using Microsoft.Data.Sqlite;

namespace DHT.Server.Database.Sqlite.Utils {
	sealed class SqliteConnectionPool : IDisposable {
		private static string GetConnectionString(SqliteConnectionStringBuilder connectionStringBuilder) {
			connectionStringBuilder.Pooling = false;
			return connectionStringBuilder.ToString();
		}

		private readonly object monitor = new ();
		private readonly Random rand = new ();
		private volatile bool isDisposed;

		private readonly BlockingCollection<PooledConnection> free = new (new ConcurrentStack<PooledConnection>());
		private readonly List<PooledConnection> used;

		public SqliteConnectionPool(SqliteConnectionStringBuilder connectionStringBuilder, int poolSize) {
			var connectionString = GetConnectionString(connectionStringBuilder);

			for (int i = 0; i < poolSize; i++) {
				var conn = new SqliteConnection(connectionString);
				conn.Open();
				
				var pooledConn = new PooledConnection(this, conn);
				
				using (var cmd = pooledConn.Command("PRAGMA journal_mode=WAL")) {
					cmd.ExecuteNonQuery();
				}
				
				free.Add(pooledConn);
			}

			used = new List<PooledConnection>(poolSize);
		}

		private void ThrowIfDisposed() {
			if (isDisposed) {
				throw new ObjectDisposedException(nameof(SqliteConnectionPool));
			}
		}

		public ISqliteConnection Take() {
			PooledConnection? conn = null;
			
			while (conn == null) {
				ThrowIfDisposed();
				lock (monitor) {
					if (free.TryTake(out conn, TimeSpan.FromMilliseconds(rand.Next(100, 200)))) {
						used.Add(conn);
						break;
					}
					else {
						Log.ForType<SqliteConnectionPool>().Warn("Thread " + Thread.CurrentThread.ManagedThreadId + " is starving for connections.");
					}
				}
			}

			return conn;
		}

		private void Return(PooledConnection conn) {
			ThrowIfDisposed();

			lock (monitor) {<commit_msg>Fix code for reducing chance of SQLite connection pool livelocks<commit_after>using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using DHT.Utils.Logging;
using Microsoft.Data.Sqlite;

namespace DHT.Server.Database.Sqlite.Utils {
	sealed class SqliteConnectionPool : IDisposable {
		private static string GetConnectionString(SqliteConnectionStringBuilder connectionStringBuilder) {
			connectionStringBuilder.Pooling = false;
			return connectionStringBuilder.ToString();
		}

		private readonly object monitor = new ();
		private readonly Random rand = new ();
		private volatile bool isDisposed;

		private readonly BlockingCollection<PooledConnection> free = new (new ConcurrentStack<PooledConnection>());
		private readonly List<PooledConnection> used;

		public SqliteConnectionPool(SqliteConnectionStringBuilder connectionStringBuilder, int poolSize) {
			var connectionString = GetConnectionString(connectionStringBuilder);

			for (int i = 0; i < poolSize; i++) {
				var conn = new SqliteConnection(connectionString);
				conn.Open();
				
				var pooledConn = new PooledConnection(this, conn);
				
				using (var cmd = pooledConn.Command("PRAGMA journal_mode=WAL")) {
					cmd.ExecuteNonQuery();
				}
				
				free.Add(pooledConn);
			}

			used = new List<PooledConnection>(poolSize);
		}

		private void ThrowIfDisposed() {
			if (isDisposed) {
				throw new ObjectDisposedException(nameof(SqliteConnectionPool));
			}
		}

		public ISqliteConnection Take() {
			while (true) {
				ThrowIfDisposed();
				
				lock (monitor) {
					if (free.TryTake(out var conn)) {
						used.Add(conn);
						return conn;
					}
					else {
						Log.ForType<SqliteConnectionPool>().Warn("Thread " + Thread.CurrentThread.ManagedThreadId + " is starving for connections.");
					}
				}
				
				Thread.Sleep(TimeSpan.FromMilliseconds(rand.Next(100, 200)));
			}
		}

		private void Return(PooledConnection conn) {
			ThrowIfDisposed();

			lock (monitor) {
{'fraction_non_alphanumeric': 0.0982991495747874, 'fraction_numerical': 0.003501750875437719, 'mean_word_length': 4.1138107416879794, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4820218', 'n_tokens_mistral': 1397, 'n_tokens_neox': 1112, 'n_words': 265}