<commit_before>﻿using EDDiscovery2.DB;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace EDDiscovery.DB
{
    // This class uses a monitor to ensure only one can be
    // active at any one time
    public class SQLiteTxnLockED<TConn> : IDisposable
        where TConn : SQLiteConnectionED
    {
        private static object _transactionLock = new object();
        private static System.Threading.Timer _locktimer;
        private bool _locktaken = false;
        private static ConcurrentDictionary<Thread, bool> _waitingthreads = new ConcurrentDictionary<Thread, bool>();
        private Thread _owningThread;
        public DbCommand _executingCommand;
        public bool _commandExecuting = false;
        private bool _isLongRunning = false;
        private string _commandText = null;
        private bool _longRunningLogged = false;

        #region Constructor and Destructor
        public SQLiteTxnLockED()
        {
        }

        ~SQLiteTxnLockED()
        {
            this.Dispose(false);
        }
        #endregion

        #region Opening and Disposal
        private static void DebugLongRunningOperation(object state)
        {
            WeakReference weakref = state as WeakReference;

            if (weakref != null)
            {
                SQLiteTxnLockED<TConn> txnlock = weakref.Target as SQLiteTxnLockED<TConn>;

                if (txnlock != null)
                {
                    txnlock._isLongRunning = true;

                    if (txnlock._commandExecuting)
                    {
                        if (txnlock._isLongRunning)
                        {
                            Trace.WriteLine($"The following command is taking a long time to execute:\n{txnlock._commandText}");
                        }
                        if (txnlock._owningThread == Thread.CurrentThread)
                        {
                            StackTrace trace = new StackTrace(1, true);
                            Trace.WriteLine(trace.ToString());
                        }
                    }
                    else
                    {
                        Trace.WriteLine($"The transaction lock has been held for a long time.");

                        if (txnlock._commandText != null)
                        {
                            Trace.WriteLine($"Last command to execute:\n{txnlock._commandText}");
                        }
                    }
                }
            }
        }

        public void BeginCommand(DbCommand cmd)
        {
            this._executingCommand = cmd;
            this._commandText = cmd.CommandText;
            this._commandExecuting = true;

            if (this._isLongRunning && !this._longRunningLogged)
            {
                this._isLongRunning = false;
                DebugLongRunningOperation(new WeakReference(this));
                this._longRunningLogged = true;
            }
        }

        public void EndCommand()
        {
            this._commandExecuting = false;
        }

        public void Open()
        {
            // Only take the lock once
            if (!_locktaken)
            {
                bool retry = false;

                do
                {
                    try
                    {
                        // Add our thread to the set of threads waiting
                        // for the lock
                        lock (_waitingthreads)
                        {
                            _waitingthreads[Thread.CurrentThread] = true;
                        }

                        Monitor.Enter(_transactionLock, ref _locktaken);
                        _owningThread = Thread.CurrentThread;
                        _waitingthreads[Thread.CurrentThread] = false;
                        _locktimer = new System.Threading.Timer(DebugLongRunningOperation, new WeakReference(this), 2000, Timeout.Infinite);
                    }
                    // Retry the lock if we are interrupted by
                    // a leaked lock being finalized
                    catch (ThreadInterruptedException)
                    {
                        if (_waitingthreads[Thread.CurrentThread] == false)
                        {
                            retry = true;
                        }
                    }
                }
                while (retry);

                GC.ReRegisterForFinalize(this);
            }
        }

        public void Close()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // disposing: true if Dispose() was called, false
        // if being finalized by the garbage collector
        protected void Dispose(bool disposing)
        {
            if (_locktaken)
            {
                if (Thread.CurrentThread == _owningThread)
                {
                    Monitor.Exit(_transactionLock);
                }
                else
                {
                    // This thread doesn't own the lock, so we need to create
                    // a new lock object and interrupt all waiting threads
                    Console.WriteLine("ERROR: Transaction Lock Leaked");

                    lock (_waitingthreads)
                    {
                        _transactionLock = new object();

                        foreach (var thread in _waitingthreads.Keys)
                        {
                            if (_waitingthreads[thread])
                            {
                                _waitingthreads[thread] = false;
                                thread.Interrupt();
                            }
                        }
                    }
                }

                _locktaken = false;
            }

            if (_locktimer != null)
            {
                _locktimer.Dispose();
                _locktimer = null;
            }
        }<commit_msg>Prevent race condition while disposing txn lock<commit_after>﻿using EDDiscovery2.DB;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace EDDiscovery.DB
{
    // This class uses a monitor to ensure only one can be
    // active at any one time
    public class SQLiteTxnLockED<TConn> : IDisposable
        where TConn : SQLiteConnectionED
    {
        private static object _transactionLock = new object();
        private static System.Threading.Timer _locktimer;
        private bool _locktaken = false;
        private static ConcurrentDictionary<Thread, bool> _waitingthreads = new ConcurrentDictionary<Thread, bool>();
        private Thread _owningThread;
        public DbCommand _executingCommand;
        public bool _commandExecuting = false;
        private bool _isLongRunning = false;
        private string _commandText = null;
        private bool _longRunningLogged = false;

        #region Constructor and Destructor
        public SQLiteTxnLockED()
        {
        }

        ~SQLiteTxnLockED()
        {
            this.Dispose(false);
        }
        #endregion

        #region Opening and Disposal
        private static void DebugLongRunningOperation(object state)
        {
            WeakReference weakref = state as WeakReference;

            if (weakref != null)
            {
                SQLiteTxnLockED<TConn> txnlock = weakref.Target as SQLiteTxnLockED<TConn>;

                if (txnlock != null)
                {
                    txnlock._isLongRunning = true;

                    if (txnlock._commandExecuting)
                    {
                        if (txnlock._isLongRunning)
                        {
                            Trace.WriteLine($"The following command is taking a long time to execute:\n{txnlock._commandText}");
                        }
                        if (txnlock._owningThread == Thread.CurrentThread)
                        {
                            StackTrace trace = new StackTrace(1, true);
                            Trace.WriteLine(trace.ToString());
                        }
                    }
                    else
                    {
                        Trace.WriteLine($"The transaction lock has been held for a long time.");

                        if (txnlock._commandText != null)
                        {
                            Trace.WriteLine($"Last command to execute:\n{txnlock._commandText}");
                        }
                    }
                }
            }
        }

        public void BeginCommand(DbCommand cmd)
        {
            this._executingCommand = cmd;
            this._commandText = cmd.CommandText;
            this._commandExecuting = true;

            if (this._isLongRunning && !this._longRunningLogged)
            {
                this._isLongRunning = false;
                DebugLongRunningOperation(new WeakReference(this));
                this._longRunningLogged = true;
            }
        }

        public void EndCommand()
        {
            this._commandExecuting = false;
        }

        public void Open()
        {
            // Only take the lock once
            if (!_locktaken)
            {
                bool retry = false;

                do
                {
                    try
                    {
                        // Add our thread to the set of threads waiting
                        // for the lock
                        lock (_waitingthreads)
                        {
                            _waitingthreads[Thread.CurrentThread] = true;
                        }

                        Monitor.Enter(_transactionLock, ref _locktaken);
                        _owningThread = Thread.CurrentThread;
                        _waitingthreads[Thread.CurrentThread] = false;
                        _locktimer = new System.Threading.Timer(DebugLongRunningOperation, new WeakReference(this), 2000, Timeout.Infinite);
                    }
                    // Retry the lock if we are interrupted by
                    // a leaked lock being finalized
                    catch (ThreadInterruptedException)
                    {
                        if (_waitingthreads[Thread.CurrentThread] == false)
                        {
                            retry = true;
                        }
                    }
                }
                while (retry);

                GC.ReRegisterForFinalize(this);
            }
        }

        public void Close()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // disposing: true if Dispose() was called, false
        // if being finalized by the garbage collector
        protected void Dispose(bool disposing)
        {
            if (_locktaken)
            {
                if (Thread.CurrentThread == _owningThread)
                {
                    if (_locktimer != null)
                    {
                        _locktimer.Dispose();
                        _locktimer = null;
                    }

                    Monitor.Exit(_transactionLock);
                }
                else
                {
                    // This thread doesn't own the lock, so we need to create
                    // a new lock object and interrupt all waiting threads
                    Console.WriteLine("ERROR: Transaction Lock Leaked");

                    lock (_waitingthreads)
                    {
                        if (_locktimer != null)
                        {
                            _locktimer.Dispose();
                            _locktimer = null;
                        }

                        _transactionLock = new object();

                        foreach (var thread in _waitingthreads.Keys)
                        {
                            if (_waitingthreads[thread])
                            {
                                _waitingthreads[thread] = false;
                                thread.Interrupt();
                            }
                        }
                    }
                }

                _locktaken = false;
            }

        }
{'fraction_non_alphanumeric': 0.06564344746162928, 'fraction_numerical': 0.0009445100354191264, 'mean_word_length': 1.0863711001642036, 'pattern_counts': {'":': 0, '<': 13, '<?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': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4127824', 'n_tokens_mistral': 2940, 'n_tokens_neox': 2722, 'n_words': 698}