Beispiel #1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="lockType"></param>
        /// <param name="tables"></param>
        /// <returns>false if thread grants access, false if thread is in a queue</returns>
        public bool AddSession(eTransactionTablesLockTypes lockType, string[] tables)
        {
            lock (lock_disposed)
            {
                if (disposed)
                {
                    return(true);
                }
            }

            internSession iSession = null;
            bool          ret      = true;

            _sync.EnterWriteLock();
            try
            {
                foreach (var ses in _acceptedSessions)
                {
                    if (DbUserTables.TableNamesIntersect(ses.Value.tables.ToList(), tables.ToList()))
                    {
                        if (ses.Value.lockType == eTransactionTablesLockTypes.EXCLUSIVE || lockType == eTransactionTablesLockTypes.EXCLUSIVE)
                        {
                            //Lock
                            ret = false;
                            break;
                        }
                    }
                }

                if (!ret)
                {
                    internSession xSes = null;
                    foreach (var ses in _waitingSessionSequence)
                    {
                        if (ses == Environment.CurrentManagedThreadId)
                        {
                            break;
                        }

                        _waitingSessions.TryGetValue(ses, out xSes);

                        if (DbUserTables.TableNamesIntersect(xSes.tables.ToList(), tables.ToList()))
                        {
                            if (xSes.lockType == eTransactionTablesLockTypes.EXCLUSIVE || lockType == eTransactionTablesLockTypes.EXCLUSIVE)
                            {
                                //Lock
                                ret = false;
                                break;
                            }
                        }
                    }
                }

                if (_waitingSessions.TryGetValue(Environment.CurrentManagedThreadId, out iSession))
                {
                    //This session was in the waiting list once
                    if (ret)
                    {
                        //We have to take away session from waiting list
                        iSession.gator.Dispose();
                        iSession.gator = null;
                        _waitingSessions.Remove(Environment.CurrentManagedThreadId);
                        _waitingSessionSequence.Remove(Environment.CurrentManagedThreadId);
                    }
                    else
                    {
                        iSession.gator.CloseGate();
                    }
                }
                else
                {
                    //Creating new session
                    iSession = new internSession()
                    {
                        lockType = lockType,
                        tables   = tables
                    };

                    if (!ret)
                    {
                        iSession.gator = new DbThreadsGator(false);
                        _waitingSessions.Add(Environment.CurrentManagedThreadId, iSession);
                        _waitingSessionSequence.Add(Environment.CurrentManagedThreadId);
                    }
                }

                if (ret)
                {
                    //Adding into accepted sessions
                    _acceptedSessions.Add(Environment.CurrentManagedThreadId, iSession);
                }
            }
            finally
            {
                _sync.ExitWriteLock();
            }

            if (!ret)
            {
                //putting gate
                iSession.gator.PutGateHere();
            }

            return(ret);
        }
        /// <summary>
        /// Access synchronizer.
        /// All calls of the WRITE LOCATOR come over this function.
        /// </summary>
        /// <param name="transactionThreadId"></param>
        /// <param name="tablesNames"></param>
        /// <param name="calledBySynchronizer"></param>
        public void RegisterWriteTablesForTransaction(int transactionThreadId, List <string> tablesNames, bool calledBySynchronizer)
        {
            //in every transaction unit we got a list of reserved for WRITING tables

            //if we have in tablesNames one of the tables which is in this list we have to stop the thread with mre
            bool            toWaitTillTransactionIsFinished = false;
            bool            breakOuterLoop  = false;
            TransactionUnit transactionUnit = null;
            bool            deadlock        = false;

            //When SyncTables is called
            //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> SYNC IN Thread: " + transactionThreadId);

            while (true)    //loop till thread will get full access to write tables
            {
                toWaitTillTransactionIsFinished = false;
                breakOuterLoop = false;
                deadlock       = false;

                //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> " + "Thread: " + transactionThreadId + "WHILE ");

                //only tables required for writing or read-commited will have to go over this fast bottleneck
                lock (_sync_dl)
                {
                    _sync_transactions.EnterReadLock();
                    try
                    {
                        this._transactions.TryGetValue(transactionThreadId, out transactionUnit);

                        if (transactionUnit == null)
                        {
                            return; //transaction doesn't exist anymore, gracefully goes out
                        }
                        if (!calledBySynchronizer)
                        {
                            //Here we are in case if Registrator is called by WriteTableCall, so we check intersections
                            //Between reserved Write tables and current table using patterns intersections technique.
                            //If they intersect we let the thread to proceed
                            if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tablesNames))
                            {
                                return;
                            }
                        }


                        //iterating over all open transactions except self, finding out if desired tables are locked by other threads.
                        foreach (var tu in this._transactions.Where(r => r.Value.TransactionThreadId != transactionThreadId))
                        {
                            foreach (string tableName in tu.Value.GetTransactionWriteTablesNames())
                            {
                                //if (tablesNames.Contains(tableName))
                                if (DbUserTables.TableNamesContains(tablesNames, tableName))
                                {
                                    //
                                    //++++++++++++++ here we can register all tables which are waiting for write lock release
                                    transactionUnit.AddTransactionWriteTablesAwaitingReservation(tablesNames);

                                    //++++++++++++++ if thread, who has locked this table has another table in a "waiting for reservation" blocked by this thread - it's a deadlock
                                    //if (transactionUnit.GetTransactionWriteTablesNames().Intersect(tu.Value.GetTransactionWriteTablesAwaitingReservation()).Count() > 0)
                                    if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tu.Value.GetTransactionWriteTablesAwaitingReservation()))
                                    {
                                        //we got deadlock, we will stop this transaction with an exception
                                        deadlock = true;
                                    }

                                    //other thread has reserved table for the transaction we have to wait
                                    toWaitTillTransactionIsFinished = true;
                                    breakOuterLoop = true;

                                    if (!deadlock)
                                    {
                                        //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> " + "Thread: " + transactionThreadId + " GATE IS CLOSED ");

                                        ThreadsGator.CloseGate();  //closing gate only if no deadlock situation
                                        //mreWriteTransactionLock.Reset();   //setting to signalled only in non-deadlock case
                                    }

                                    break;
                                }
                            }

                            if (breakOuterLoop)
                            {
                                break;
                            }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        this.UnregisterTransaction(transactionThreadId);

                        throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_TABLE_WRITE_REGISTRATION_FAILED, ex);
                    }
                    finally
                    {
                        _sync_transactions.ExitReadLock();
                    }

                    //if(true) this thread owns all table for modification lock
                    if (!toWaitTillTransactionIsFinished)
                    {
                        //+++++++++++ Here we can clear all table names in the waiting reservation queue
                        transactionUnit.ClearTransactionWriteTablesAwaitingReservation(tablesNames);

                        //we have to reserve for our transaction all tables
                        foreach (var tbn in tablesNames)
                        {
                            transactionUnit.AddTransactionWriteTable(tbn, null);
                        }

                        //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> SYNC OUT Thread: " + transactionThreadId + "   Sync stop: " + transactionUnit.udtSyncStop.ToString("dd.MM.yyyy HH:mm:ss"));

                        return;
                    }
                }//end of lock


                if (deadlock)
                {
                    this.UnregisterTransaction(transactionThreadId);

                    throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_IN_DEADLOCK);
                }

                if (toWaitTillTransactionIsFinished)
                {
                    //Console.WriteLine(DateTime.UtcNow.ToString("dd.MM.yyyy HH:mm:ss") + "> " + "Thread: " + transactionThreadId + " GATE IS PUT ");

                    //blocking thread which requires busy tables for writing, till they are released
                    //ThreadsGator.PutGateHere(20000);    //every 20 second (or by Gate open we give a chance to re-try, for safety reasons of hanged threads, if programmer didn't dispose DBreeze process after the programm end)

                    //#if ASYNC
                    //                    await ThreadsGator.PutGateHere().ConfigureAwait(false);
                    //#else
                    //                    ThreadsGator.PutGateHere();
                    //#endif

                    ThreadsGator.PutGateHere();

                    //mreWriteTransactionLock.WaitOne();
                }
            }//eo while
        }
        /// <summary>
        /// Access synchronizer.
        /// All calls of the WRITE LOCATOR come over this function.
        /// </summary>
        /// <param name="transactionThreadId"></param>
        /// <param name="tablesNames"></param>
        public void RegisterWriteTablesForTransaction(int transactionThreadId, List <string> tablesNames, bool calledBySynchronizer)
        {
            //in every transaction unit we got a list of reserved for WRITING tables

            //if we have in tablesNames one of the tables which is in this list we have to stop the thread with mre
            bool            toWaitTillTransactionIsFinished = false;
            bool            breakOuterLoop  = false;
            TransactionUnit transactionUnit = null;
            bool            deadlock        = false;

            Exception innerException = null;

            while (true)    //loop till thread will get full access to write tables
            {
                toWaitTillTransactionIsFinished = false;
                breakOuterLoop = false;
                deadlock       = false;

                //only tables required for writing or read-commited will have to go over this fast bottleneck
                lock (_sync_dl)
                {
                    _sync_transactions.EnterReadLock();
                    try
                    {
                        this._transactions.TryGetValue(transactionThreadId, out transactionUnit);

                        if (transactionUnit == null)
                        {
                            return; //transaction doesn't exist anymore, gracefully goes out
                        }
                        if (!calledBySynchronizer)
                        {
                            //Here we are in case if Registrator is called by WriteTableCall, so we check intersections
                            //Between reserved Write tables and current table using patterns intersections technique.
                            //If they intersect we let the thread to proceed
                            if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tablesNames))
                            {
                                return;
                            }
                            //Help for the programmer on the early stage to see problem with the possible deadlock
                            if (_engine.Configuration.NotifyAhead_WhenWriteTablePossibleDeadlock)
                            {
                                if (transactionUnit.TransactionWriteTablesCount > 0)
                                {
                                    throw new Exception("Put table \"" + tablesNames.FirstOrDefault() + "\" into tran.SynchronizeTables statement, because it will be modified");
                                }
                            }
                        }


                        //iterating over all open transactions except self, finding out if desired tables are locked by other threads.
                        foreach (var tu in this._transactions.Where(r => r.Value.TransactionThreadId != transactionThreadId))
                        {
                            foreach (string tableName in tu.Value.GetTransactionWriteTablesNames())
                            {
                                //if (tablesNames.Contains(tableName))
                                if (DbUserTables.TableNamesContains(tablesNames, tableName))
                                {
                                    //
                                    //++++++++++++++ here we can register all tables which are waiting for write lock release
                                    transactionUnit.AddTransactionWriteTablesAwaitingReservation(tablesNames);

                                    //++++++++++++++ if thread, who has locked this table has another table in a "waiting for reservation" blocked by this thread - it's a deadlock
                                    //if (transactionUnit.GetTransactionWriteTablesNames().Intersect(tu.Value.GetTransactionWriteTablesAwaitingReservation()).Count() > 0)
                                    if (DbUserTables.TableNamesIntersect(transactionUnit.GetTransactionWriteTablesNames(), tu.Value.GetTransactionWriteTablesAwaitingReservation()))
                                    {
                                        //we got deadlock, we will stop this transaction with an exception
                                        deadlock = true;
                                    }

                                    //other thread has reserved table for the transaction we have to wait
                                    toWaitTillTransactionIsFinished = true;
                                    breakOuterLoop = true;

                                    if (!deadlock)
                                    {
                                        ThreadsGator.CloseGate();  //closing gate only if no deadlock situation
                                        //mreWriteTransactionLock.Reset();   //setting to signalled only in non-deadlock case
                                    }

                                    break;
                                }
                            }

                            if (breakOuterLoop)
                            {
                                break;
                            }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        innerException = ex;
                        //this.UnregisterTransaction(transactionThreadId);

                        //throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_TABLE_WRITE_REGISTRATION_FAILED,ex);
                    }
                    finally
                    {
                        _sync_transactions.ExitReadLock();
                    }

                    if (innerException != null)
                    {
                        this.UnregisterTransaction(transactionThreadId);
                        throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_TABLE_WRITE_REGISTRATION_FAILED, innerException);
                    }

                    //if(true) this thread owns all table for modification lock
                    if (!toWaitTillTransactionIsFinished)
                    {
                        //+++++++++++ Here we can clear all table names in the waiting reservation queue
                        transactionUnit.ClearTransactionWriteTablesAwaitingReservation(tablesNames);

                        //we have to reserve for our transaction all tables
                        foreach (var tbn in tablesNames)
                        {
                            transactionUnit.AddTransactionWriteTable(tbn, null);
                        }

                        return;
                    }
                }//end of lock


                if (deadlock)
                {
                    this.UnregisterTransaction(transactionThreadId);

                    throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.TRANSACTION_IN_DEADLOCK);
                }

                if (toWaitTillTransactionIsFinished)
                {
                    //blocking thread which requires busy tables for writing, till they are released
                    //ThreadsGator.PutGateHere(20000);    //every 20 second (or by Gate open we give a chance to re-try, for safety reasons of hanged threads, if programmer didn't dispose DBreeze process after the programm end)
                    ThreadsGator.PutGateHere();
                    //mreWriteTransactionLock.WaitOne();
                }
            }
        }