예제 #1
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// lock a record
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="entry"></param>
        /// <returns></returns>
        public DBResult LockRec(ref LockContext ctx, out LockEntry entry)
        {
            var locks = RecLocks.GetValueOrDefault(ctx.Page);

            if (locks == null || locks.Count == 0)
            {
                entry = CreateRecLock(ref ctx);
                return(DBResult.Success);
            }

            if (IsHeldTabLock(ref ctx, out entry))
            {
                return(DBResult.Success);
            }

            if (IsHeldRecLock(ref ctx, out entry))
            {
                return(DBResult.Success);
            }

            if (IsOthersHeldOrWaitConflictRecLock(ctx.Transaction, ctx.Page, ctx.RecordIndex, ctx.Flags))
            {
                ctx.Flags |= LockFlags.Waiting;
                return(CreateRecLockForWait(ref ctx, out entry));
            }

            entry = GetCanReuseRecLock(ref ctx) ?? CreateRecLock(ref ctx);

            return(DBResult.Success);
        }
예제 #2
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// create a new table-lock
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        private LockEntry CreateTabLock(ref LockContext ctx)
        {
            if (!TabLocks.TryGetValue(ctx.Index, out var list))
            {
                list = new LinkedList <LockEntry>();
                TabLocks[ctx.Index] = list;;
            }

            var tx    = ctx.Transaction;
            var entry = new LockEntry(ctx.Page, ctx.Flags, ctx.RecordCount);

            if (entry.IsWaiting)
            {
                tx.WaitLock = entry;
            }

            list.AddLast(entry);
            tx.Locks.AddLast(entry);

            entry.GNode       = list.Last;
            entry.TNode       = tx.Locks.Last;
            entry.Index       = ctx.Index;
            entry.Transaction = ctx.Transaction;
            entry.Thread      = Thread.CurrentThread.ManagedThreadId;

            return(entry);
        }
예제 #3
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// un-lock-tab and try wake up from the next to the end
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        internal DBResult UnLockTab(LockEntry entry, LockEntry next)
        {
            while (next != null)
            {
                if (!next.IsWaiting)
                {
                    next = FindNextLockEntry(next);
                    continue;
                }

                if (IsOthersHeldOrWaitConflictTabLock(next.Transaction, next.Index, next.Flags, next))
                {
                    next = FindNextLockEntry(next);
                    continue;
                }

                next.Flags |= ~LockFlags.Waiting;
                next.Transaction.WaitLock = null;
                next.Transaction.WaitEvent.Set();
                next.Transaction.State = TransactionState.Running;

                next = FindNextLockEntry(next);
            }

            return(DBResult.Success);
        }
예제 #4
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// un-lock-rec and try wake up from the next to the end
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        internal DBResult UnLockRec(LockEntry entry, LockEntry next)
        {
            while (next != null)
            {
                if (!next.IsWaiting)
                {
                    next = FindNextLockEntry(next);
                    continue;
                }

                var slot = next.GetFirstBitSlot();
                if (slot < 0 || entry.GetBit(slot) == 0)
                {
                    next = FindNextLockEntry(next);
                    continue;
                }

                if (IsOthersHeldOrWaitConflictRecLock(next.Transaction, next.Page, slot, next.Flags, next))
                {
                    next = FindNextLockEntry(next);
                    continue;
                }

                next.Flags &= ~LockFlags.Waiting;
                next.Transaction.WaitLock = null;
                next.Transaction.WaitEvent.Set();
                next.Transaction.State = TransactionState.Running;

                next = FindNextLockEntry(next);
            }

            return(DBResult.Success);
        }
예제 #5
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// check current transaction has held the table's lock >=ctx-lock-level and not been in wait state
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        private bool IsHeldTabLock(ref LockContext ctx, out LockEntry entry)
        {
            var locks = TabLocks.GetValueOrDefault(ctx.Index);

            if (locks == null)
            {
                entry = null;
                return(false);
            }

            for (var node = locks.First; node != null; node = node.Next)
            {
                var lockEntry = node.Value;
                if (lockEntry.Transaction != ctx.Transaction)
                {
                    continue;
                }

                if (lockEntry.IsExclusive || !ctx.Flags.IsExclusive())
                {
                    entry = lockEntry;
                    return(true);
                }
            }

            entry = null;
            return(false);
        }
예제 #6
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// create new rec lock then waitting
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="entry"></param>
        /// <returns></returns>
        private DBResult CreateRecLockForWait(ref LockContext ctx, out LockEntry entry)
        {
            var recEntry = CreateRecLock(ref ctx);

            if (!recEntry.IsWaiting)
            {
                recEntry.Flags |= LockFlags.Waiting;
            }

            entry = recEntry;
            entry.Transaction.WaitLock = recEntry;
            entry.Transaction.State    = TransactionState.Waitting;

            //check dead-lock
            if (IsCausedDeadLock(recEntry))
            {
                entry.Flags &= ~LockFlags.Waiting;
                entry.SetBit(ctx.RecordIndex, 0);
                entry.Transaction.WaitLock = null;
                return(DBResult.DeadLock);
            }

            entry.Transaction.WaitEvent.Reset();
            return(DBResult.WaitLock);
        }
예제 #7
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// check if the lock caused dead-lock
        /// </summary>
        /// <param name="initTx"></param>
        /// <returns></returns>
        private bool IsCausedDeadLock(Transaction initTx, LockEntry checkEntry)
        {
            var entry = checkEntry;

            if (entry.Transaction.DeadFlags == 1)
            {
                return(true);
            }

            var lockBit = entry.IsTable ? 0 : entry.GetFirstBitSlot();

            if (lockBit < 0)
            {
                return(false);
            }

            while (true)
            {
                var prevEntry = FindPrevLockEntry(entry);
                if (prevEntry == null)
                {
                    checkEntry.Transaction.DeadFlags = 1;
                    return(false);
                }

                if (prevEntry.Transaction.State != TransactionState.Waitting ||
                    prevEntry.Transaction == checkEntry.Transaction ||
                    (!prevEntry.IsTable && prevEntry.GetBit(lockBit) == 0))
                {
                    entry = prevEntry;
                    continue;
                }

                if (IsLockConflict(checkEntry, prevEntry))
                {
                    if (prevEntry.Transaction == initTx)
                    {
                        return(true);
                    }

                    if (prevEntry.Transaction.State == TransactionState.Waitting &&
                        IsCausedDeadLock(initTx, prevEntry.Transaction.WaitLock))
                    {
                        return(true);
                    }
                }

                entry = prevEntry;
            }
        }
예제 #8
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// check if the lock caused dead-lock
        /// </summary>
        /// <param name="entry"></param>
        /// <returns></returns>
        private bool IsCausedDeadLock(LockEntry entry)
        {
            if (entry.Transaction.Locks.Count == 1 || !entry.IsWaiting)
            {
                return(false);
            }

            foreach (var trx in EngineEnviorment.Transactions.Values)
            {
                trx.DeadFlags = 0;
            }

            return(IsCausedDeadLock(entry.Transaction, entry));
        }
예제 #9
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        private unsafe LockEntry SplitRecLock(LockEntry fromEntry, PagePosition to, int mid)
        {
            if (fromEntry == null)
            {
                return(null);
            }

            var count  = fromEntry.Bits.Length - (mid >> 3) + 1;
            var buffer = stackalloc byte[count];

            var bites = new byte[fromEntry.Bits.Length];

            Array.Copy(fromEntry.Bits, bites, bites.Length);
            fromEntry.CopyBitsTo(mid, buffer);
            fromEntry.TuncateBits(mid);

            if (fromEntry.IsEmpty && fromEntry.Page.PageNumber != 0)
            {
                fromEntry.Bits = bites;
                fromEntry.CopyBitsTo(mid, buffer);
                fromEntry.TuncateBits(mid);
            }

            for (var i = 0; i < count; i++)
            {
                if (buffer[i] == 0)
                {
                    continue;
                }

                var toEntry = new LockEntry()
                {
                    Bits        = new byte[count],
                    Page        = to,
                    Flags       = fromEntry.Flags,
                    Index       = fromEntry.Index,
                    Thread      = fromEntry.Thread,
                    Transaction = fromEntry.Transaction
                };

                Unsafe.CopyBlockUnaligned(ref toEntry.Bits[0], ref *buffer, (uint)count);

                return(toEntry);
            }

            return(null);
        }
예제 #10
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        public DBResult UnLock(LockEntry entry)
        {
            lock (SyncRoot)
            {
                if (entry == null)
                {
                    return(DBResult.Success);
                }

                var list = entry.GNode.List;
                var next = FindNextLockEntry(entry);

                list.Remove(entry.GNode);

                return(entry.IsTable ? UnLockTab(entry, next) : UnLockRec(entry, next));
            }
        }
예제 #11
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        internal bool IsLockConflict(LockEntry entry, LockEntry other)
        {
            if (entry == other || entry.Transaction == other.Transaction)
            {
                return(false);
            }

            if (entry.IsTable && entry.IsExclusive || other.IsTable && other.IsExclusive)
            {
                return(true);
            }
            else if (entry.IsTable || other.IsTable)
            {
                return(true);
            }
            else
            {
                return(entry.IsExclusive || other.IsExclusive);
            }
        }
예제 #12
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// create new rec lock then waitting
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="entry"></param>
        /// <returns></returns>
        private DBResult CreateTabLockForWait(ref LockContext ctx, out LockEntry entry)
        {
            var tabEntry = CreateTabLock(ref ctx);

            if (!tabEntry.IsWaiting)
            {
                tabEntry.Flags |= LockFlags.Waiting;
            }

            entry = tabEntry;
            entry.Transaction.WaitLock = tabEntry;
            entry.Transaction.State    = TransactionState.Waitting;

            //check dead-lock
            if (IsCausedDeadLock(tabEntry))
            {
                entry.Flags &= ~LockFlags.Waiting;
                entry.Transaction.WaitLock = null;
                return(DBResult.DeadLock);
            }

            entry.Transaction.WaitEvent.Reset();
            return(DBResult.WaitLock);
        }
예제 #13
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// lock a table
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="entry"></param>
        /// <returns></returns>
        public DBResult LockTab(ref LockContext ctx, out LockEntry entry)
        {
            var locks = TabLocks.GetValueOrDefault(ctx.Index);

            if (locks == null || locks.Count == 0)
            {
                entry = CreateTabLock(ref ctx);
                return(DBResult.Success);
            }

            if (IsHeldTabLock(ref ctx, out entry))
            {
                return(DBResult.Success);
            }

            if (IsOthersHeldOrWaitConflictTabLock(ctx.Transaction, ctx.Index, ctx.Flags))
            {
                ctx.Flags |= LockFlags.Waiting;
                return(CreateTabLockForWait(ref ctx, out entry));
            }

            entry = CreateTabLock(ref ctx);
            return(DBResult.Success);
        }
예제 #14
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
 internal LockEntry FindPrevLockEntry(LockEntry entry)
 {
     return(entry?.GNode?.Previous?.Value);
 }
예제 #15
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
 internal LockEntry FindNextLockEntry(LockEntry entry)
 {
     return(entry?.GNode?.Next?.Value);
 }
예제 #16
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// is other's transaction has held the table's lock and conflict with the ctx-lock
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        private bool IsOthersHeldOrWaitConflictTabLock(Transaction tx, TableIndex index, LockFlags flags, LockEntry endEntry = null)
        {
            var locks = TabLocks.GetValueOrDefault(index);

            if (locks == null)
            {
                return(false);
            }

            for (var node = locks.First; node != null; node = node.Next)
            {
                var entry = node.Value;
                if (entry == endEntry)
                {
                    break;
                }

                if (entry.Transaction == tx)
                {
                    continue;
                }

                if (entry.IsExclusive || flags.IsExclusive())
                {
                    return(true);
                }
            }

            return(false);
        }
예제 #17
0
파일: LockManager.cs 프로젝트: zuvys/Vicuna
        /// <summary>
        /// is other's transaction has held the recrod's lock and conflict with the ctx-lock
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        private bool IsOthersHeldOrWaitConflictRecLock(Transaction tx, PagePosition page, int slot, LockFlags flags, LockEntry endEntry = null)
        {
            var locks = RecLocks.GetValueOrDefault(page);

            if (locks == null)
            {
                return(false);
            }

            for (var node = locks.First; node != null; node = node.Next)
            {
                var entry = node.Value;
                if (entry == endEntry)
                {
                    break;
                }

                if (entry.Transaction == tx ||
                    entry.GetBit(slot) == 0)
                {
                    continue;
                }

                if (entry.IsExclusive || flags.IsExclusive())
                {
                    return(true);
                }
            }

            return(false);
        }